From 908ab9368866e6edf0edebdd546adefd5f3128f9 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Fri, 22 Feb 2013 11:23:39 +0800 Subject: [PATCH 01/68] ARM: dts: tegra: fix the activate polarity of cd-gpio in mmc host The GPIO pin of SD slot card detection should active low. Signed-off-by: Joseph Lo Tested-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra20-colibri-512.dtsi | 2 +- arch/arm/boot/dts/tegra20-harmony.dts | 4 ++-- arch/arm/boot/dts/tegra20-paz00.dts | 2 +- arch/arm/boot/dts/tegra20-seaboard.dts | 2 +- arch/arm/boot/dts/tegra20-tamonten.dtsi | 2 +- arch/arm/boot/dts/tegra20-trimslice.dts | 2 +- arch/arm/boot/dts/tegra20-ventana.dts | 2 +- arch/arm/boot/dts/tegra20-whistler.dts | 1 + arch/arm/boot/dts/tegra30-beaver.dts | 2 +- arch/arm/boot/dts/tegra30-cardhu.dtsi | 2 +- 10 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi index 444162090042..cb73e62d61a9 100644 --- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi +++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi @@ -444,7 +444,7 @@ }; sdhci@c8000600 { - cd-gpios = <&gpio 23 0>; /* gpio PC7 */ + cd-gpios = <&gpio 23 1>; /* gpio PC7 */ }; sound { diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index 61d027f03617..1f79c0debb05 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -437,7 +437,7 @@ sdhci@c8000200 { status = "okay"; - cd-gpios = <&gpio 69 0>; /* gpio PI5 */ + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 57 0>; /* gpio PH1 */ power-gpios = <&gpio 155 0>; /* gpio PT3 */ bus-width = <4>; @@ -445,7 +445,7 @@ sdhci@c8000600 { status = "okay"; - cd-gpios = <&gpio 58 0>; /* gpio PH2 */ + cd-gpios = <&gpio 58 1>; /* gpio PH2 */ wp-gpios = <&gpio 59 0>; /* gpio PH3 */ power-gpios = <&gpio 70 0>; /* gpio PI6 */ bus-width = <8>; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index 54d6fce00a59..9db36da8e023 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -436,7 +436,7 @@ sdhci@c8000000 { status = "okay"; - cd-gpios = <&gpio 173 0>; /* gpio PV5 */ + cd-gpios = <&gpio 173 1>; /* gpio PV5 */ wp-gpios = <&gpio 57 0>; /* gpio PH1 */ power-gpios = <&gpio 169 0>; /* gpio PV1 */ bus-width = <4>; diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 37b3a57ec0f1..715a8b8dd9cd 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -584,7 +584,7 @@ sdhci@c8000400 { status = "okay"; - cd-gpios = <&gpio 69 0>; /* gpio PI5 */ + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 57 0>; /* gpio PH1 */ power-gpios = <&gpio 70 0>; /* gpio PI6 */ bus-width = <4>; diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi index 4766abae7a72..6e9d91fc6195 100644 --- a/arch/arm/boot/dts/tegra20-tamonten.dtsi +++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi @@ -465,7 +465,7 @@ }; sdhci@c8000600 { - cd-gpios = <&gpio 58 0>; /* gpio PH2 */ + cd-gpios = <&gpio 58 1>; /* gpio PH2 */ wp-gpios = <&gpio 59 0>; /* gpio PH3 */ bus-width = <4>; status = "okay"; diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index 5d79e4fc49a6..98f3e44f2a51 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -325,7 +325,7 @@ sdhci@c8000600 { status = "okay"; - cd-gpios = <&gpio 121 0>; /* gpio PP1 */ + cd-gpios = <&gpio 121 1>; /* gpio PP1 */ wp-gpios = <&gpio 122 0>; /* gpio PP2 */ bus-width = <4>; }; diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index 425c89000c20..4aef56f2d96a 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -520,7 +520,7 @@ sdhci@c8000400 { status = "okay"; - cd-gpios = <&gpio 69 0>; /* gpio PI5 */ + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 57 0>; /* gpio PH1 */ power-gpios = <&gpio 70 0>; /* gpio PI6 */ bus-width = <4>; diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index ea57c0f6dcce..5762188c60ad 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -510,6 +510,7 @@ sdhci@c8000400 { status = "okay"; + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 173 0>; /* gpio PV5 */ bus-width = <8>; }; diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts index 8ff2ff20e4a3..0a2cd24df853 100644 --- a/arch/arm/boot/dts/tegra30-beaver.dts +++ b/arch/arm/boot/dts/tegra30-beaver.dts @@ -257,7 +257,7 @@ sdhci@78000000 { status = "okay"; - cd-gpios = <&gpio 69 0>; /* gpio PI5 */ + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 155 0>; /* gpio PT3 */ power-gpios = <&gpio 31 0>; /* gpio PD7 */ bus-width = <4>; diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi index 17499272a4ef..3e2d21018a5b 100644 --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi @@ -311,7 +311,7 @@ sdhci@78000000 { status = "okay"; - cd-gpios = <&gpio 69 0>; /* gpio PI5 */ + cd-gpios = <&gpio 69 1>; /* gpio PI5 */ wp-gpios = <&gpio 155 0>; /* gpio PT3 */ power-gpios = <&gpio 31 0>; /* gpio PD7 */ bus-width = <4>; From c34f30e588d310a70f994659c06f0a31dfdcfc15 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 4 Mar 2013 17:05:56 -0700 Subject: [PATCH 02/68] ARM: tegra: add CPU errata WARs to Tegra reset handler The CPU cores in Tegra contain some errata. Workarounds must be applied for these every time a CPU boots. Implement those workarounds directly in the Tegra-specific CPU reset vector. Many of these workarounds duplicate code in the core ARM kernel. However, the core ARM kernel cannot enable those workarounds when building a multi-platform kernel, since they require writing to secure- only registers, and a multi-platform kernel often does not run in secure mode, and also cannot generically/architecturally detect whether it is running in secure mode, and hence cannot either unconditionally or conditionally apply these workarounds. Instead, the workarounds must be applied in architecture-specific reset code, which is able to have more direct knowledge of the secure/normal state. On Tegra, we will be able to detect this using a non-architected register in the future, although we currently assume the kernel runs only in secure mode. Other SoCs may never run the kernel in secure mode, and hence always rely on a secure monitor to enable the workarounds, and hence never implement them in the kernel. Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/reset-handler.S | 45 +++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index 54382ceade4a..ff7b45c6c2a0 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -99,6 +99,8 @@ ENTRY(__tegra_cpu_reset_handler_start) * * Register usage within the reset handler: * + * Others: scratch + * R6 = SoC ID << 8 * R7 = CPU present (to the OS) mask * R8 = CPU in LP1 state mask * R9 = CPU in LP2 state mask @@ -114,6 +116,40 @@ ENTRY(__tegra_cpu_reset_handler_start) ENTRY(__tegra_cpu_reset_handler) cpsid aif, 0x13 @ SVC mode, interrupts disabled + + mov32 r6, TEGRA_APB_MISC_BASE + ldr r6, [r6, #APB_MISC_GP_HIDREV] + and r6, r6, #0xff00 +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +t20_check: + cmp r6, #(0x20 << 8) + bne after_t20_check +t20_errata: + # Tegra20 is a Cortex-A9 r1p1 + mrc p15, 0, r0, c1, c0, 0 @ read system control register + orr r0, r0, #1 << 14 @ erratum 716044 + mcr p15, 0, r0, c1, c0, 0 @ write system control register + mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register + orr r0, r0, #1 << 4 @ erratum 742230 + orr r0, r0, #1 << 11 @ erratum 751472 + mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register + b after_errata +after_t20_check: +#endif +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +t30_check: + cmp r6, #(0x30 << 8) + bne after_t30_check +t30_errata: + # Tegra30 is a Cortex-A9 r2p9 + mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register + orr r0, r0, #1 << 6 @ erratum 743622 + orr r0, r0, #1 << 11 @ erratum 751472 + mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register + b after_errata +after_t30_check: +#endif +after_errata: mrc p15, 0, r10, c0, c0, 5 @ MPIDR and r10, r10, #0x3 @ R10 = CPU number mov r11, #1 @@ -129,16 +165,13 @@ ENTRY(__tegra_cpu_reset_handler) #ifdef CONFIG_ARCH_TEGRA_2x_SOC /* Are we on Tegra20? */ - mov32 r6, TEGRA_APB_MISC_BASE - ldr r0, [r6, #APB_MISC_GP_HIDREV] - and r0, r0, #0xff00 - cmp r0, #(0x20 << 8) + cmp r6, #(0x20 << 8) bne 1f /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ - mov32 r6, TEGRA_PMC_BASE + mov32 r5, TEGRA_PMC_BASE mov r0, #0 cmp r10, #0 - strne r0, [r6, #PMC_SCRATCH41] + strne r0, [r5, #PMC_SCRATCH41] 1: #endif From 02e75d648899df96b79a4f98380679f48b91e3d4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 4 Mar 2013 17:05:57 -0700 Subject: [PATCH 03/68] ARM: tegra: remove save/restore of CPU diag register Prior to this change, {save,restore}_cpu_arch_register() collaborated to maintain the value of the CPU diagnostic register across power cycles. This was required to maintain any CPU errata workaround enable bits in that register. However, now that the Tegra reset vector code always enables all required workarounds, there is no need to save and restore the diagnostic register; it is always explicitly programmed in the required manner. Hence, remove the save/restore logic. This has the advantage that the kernel always directly controls the value of this register every boot, rather than relying on a bootloader or other kernel code having previously written the correct value into it. This makes CPU0 (which was previously saved/restored) and CPUn (which should have been set up by the reset vector) be controlled in exactly the same way, which is easier to debug/find/... In particular, when converting Tegra to a multi-platform kernel, the CPU0 diagnostic register value initially comes from the bootloader. Most Tegra bootloaders don't yet enable all required CPU bug workarounds. The previous commit updates the kernel to do so on any CPU power cycle. However, the save/restore code ends up over-writing the value with the old bootloader-driven value instead of the now more-likely-to-be-correct kernel value! Even irrespective of multi-platform conversion, this change limits the kernel's exposure to any WARs the bootloader didn't enable for CPU0: on the very first LP2 transition (CPU power-saving which power-cycles the CPU), the correct value will be enabled. Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/cpuidle-tegra30.c | 4 ---- arch/arm/mach-tegra/pm.c | 19 ------------------- 2 files changed, 23 deletions(-) diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 8b50cf4ddd6f..80445ed33d95 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -102,12 +102,8 @@ static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev, smp_wmb(); - save_cpu_arch_register(); - cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); - restore_cpu_arch_register(); - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); return true; diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 523604de666f..0494f739c95f 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -46,26 +46,11 @@ #define PMC_CPUPWROFF_TIMER 0xcc #ifdef CONFIG_PM_SLEEP -static unsigned int g_diag_reg; static DEFINE_SPINLOCK(tegra_lp2_lock); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); static struct clk *tegra_pclk; void (*tegra_tear_down_cpu)(void); -void save_cpu_arch_register(void) -{ - /* read diagnostic register */ - asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); - return; -} - -void restore_cpu_arch_register(void) -{ - /* write diagnostic register */ - asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); - return; -} - static void set_power_timers(unsigned long us_on, unsigned long us_off) { unsigned long long ticks; @@ -119,8 +104,6 @@ static void restore_cpu_complex(void) tegra_cpu_clock_resume(); flowctrl_cpu_suspend_exit(cpu); - - restore_cpu_arch_register(); } /* @@ -145,8 +128,6 @@ static void suspend_cpu_complex(void) tegra_cpu_clock_suspend(); flowctrl_cpu_suspend_enter(cpu); - - save_cpu_arch_register(); } void tegra_clear_cpu_in_lp2(int phy_cpu_id) From bf161d2163f7b8bf4823829dbc1a14111760187e Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Fri, 8 Feb 2013 14:44:09 +0200 Subject: [PATCH 04/68] clk: tegra: No 7.1 super clk dividers on Tegra20 Unlike Tegra30, Tegra20 does not have a 7.1 divider for the CPU superclk. Remove the clocks related to the divider. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 36 ++------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 143ce1f899ad..fa3173e3b331 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -711,8 +711,8 @@ static void tegra20_pll_init(void) } static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", - "pll_p_cclk", "pll_p_out4_cclk", - "pll_p_out3_cclk", "clk_d", "pll_x" }; + "pll_p", "pll_p_out4", + "pll_p_out3", "clk_d", "pll_x" }; static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", "pll_p_out3", "pll_p_out2", "clk_d", "clk_32k", "pll_m_out1" }; @@ -721,38 +721,6 @@ static void tegra20_super_clk_init(void) { struct clk *clk; - /* - * DIV_U71 dividers for CCLK, these dividers are used only - * if parent clock is fixed rate. - */ - - /* - * Clock input to cclk divided from pll_p using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_cclk", "pll_p", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out3 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out3_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out4 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out4_cclk", NULL); - /* CCLK */ clk = tegra_clk_register_super_mux("cclk", cclk_parents, ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, From b095ae2b9f35c838257786de27e550d62bd7c763 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 19 Feb 2013 18:16:13 +0800 Subject: [PATCH 05/68] ARM: tegra: don't unlock MMIO access to DBGLAR There is no need to unlock MMIO access to the DBGLAR all the time. Doing so may even cause problems if a SW bug causes writes to that MMIO region. Cortex-A15 processors do not support the CP14 register write the code currently uses to unlock the DBGLAR; the instruction throws an undefined instruction exceptions. This prevents tegra_secondary_startup() from executing on Tegra114, and hence prevents SMP. Remove the code that unlocks this access. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/headsmp.S | 3 --- arch/arm/mach-tegra/reset-handler.S | 3 --- 2 files changed, 6 deletions(-) diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index fd473f2b4c3d..045c16f2dd51 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -7,8 +7,5 @@ ENTRY(tegra_secondary_startup) bl v7_invalidate_l1 - /* Enable coresight */ - mov32 r0, 0xC5ACCE55 - mcr p14, 0, r0, c7, c12, 6 b secondary_startup ENDPROC(tegra_secondary_startup) diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index ff7b45c6c2a0..1676aba5e7b8 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -41,9 +41,6 @@ */ ENTRY(tegra_resume) bl v7_invalidate_l1 - /* Enable coresight */ - mov32 r0, 0xC5ACCE55 - mcr p14, 0, r0, c7, c12, 6 cpu_id r0 cmp r0, #0 @ CPU0? From b4c25cc38260950f9ede38a88f932c7958adb2ec Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Fri, 22 Feb 2013 14:24:25 +0800 Subject: [PATCH 06/68] ARM: tegra: Fix unchecked return value Check a return value for tegra_powergate_remove_clamping(). Signed-off-by: Hiroshi Doyu Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/platsmp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 2c6b3d55213b..4dfc75e118ff 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -124,6 +124,9 @@ remove_clamps: /* Remove I/O clamps. */ ret = tegra_powergate_remove_clamping(pwrgateid); + if (ret) + return ret; + udelay(10); /* Clear flow controller CSR. */ From 2be8951e145eacf2a951288ea8e752e3b21acefd Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Fri, 22 Feb 2013 14:24:26 +0800 Subject: [PATCH 07/68] ARM: tegra: fix the logical detection of power on sequence of warm boot CPUs The warm boot sequence of Tegra30 secondary CPUs should wait for the power ready then removing the clamps. This did not fix any known or unknown issue, but nice to have this fix. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/platsmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 4dfc75e118ff..e78d52d83acd 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -91,7 +91,7 @@ static int tegra30_power_up_cpu(unsigned int cpu) if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { timeout = jiffies + msecs_to_jiffies(50); do { - if (!tegra_powergate_is_powered(pwrgateid)) + if (tegra_powergate_is_powered(pwrgateid)) goto remove_clamps; udelay(10); } while (time_before(jiffies, timeout)); From 84b808da2dea7020211f1d73d015ff6c3ac207c4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 6 Mar 2013 17:16:25 -0700 Subject: [PATCH 08/68] ARM: tegra: fix ignored return value of regulator_enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: arch/arm/mach-tegra/board-harmony-pcie.c: In function ‘harmony_pcie_init’: arch/arm/mach-tegra/board-harmony-pcie.c:65:18: warning: ignoring return value of ‘regulator_enable’, declared with attribute warn_unused_result [-Wunused-result] Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/board-harmony-pcie.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c index 3cdc1bb8254c..d195db09ea32 100644 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -62,7 +62,11 @@ int __init harmony_pcie_init(void) goto err_reg; } - regulator_enable(regulator); + err = regulator_enable(regulator); + if (err) { + pr_err("%s: regulator_enable failed: %d\n", __func__, err); + goto err_en; + } err = tegra_pcie_init(true, true); if (err) { @@ -74,6 +78,7 @@ int __init harmony_pcie_init(void) err_pcie: regulator_disable(regulator); +err_en: regulator_put(regulator); err_reg: gpio_free(en_vdd_1v05); From 7469688e832e340a84a1f6d4c290d8680c723256 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 13 Feb 2013 19:15:48 +0200 Subject: [PATCH 09/68] ARM: tegra: Unify tegra{20,30,114}_init_early() Refactored tegra{20,30,114}_init_early() so that we have the unified tegra_init_early(). Signed-off-by: Hiroshi Doyu Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/board-dt-tegra114.c | 2 +- arch/arm/mach-tegra/board-dt-tegra20.c | 2 +- arch/arm/mach-tegra/board-dt-tegra30.c | 4 ++-- arch/arm/mach-tegra/board.h | 4 +--- arch/arm/mach-tegra/common.c | 26 ++----------------------- arch/arm/mach-tegra/hotplug.c | 23 +++++++++------------- arch/arm/mach-tegra/sleep.h | 10 +++++----- 7 files changed, 21 insertions(+), 50 deletions(-) diff --git a/arch/arm/mach-tegra/board-dt-tegra114.c b/arch/arm/mach-tegra/board-dt-tegra114.c index 085d63637b62..08e82941e53c 100644 --- a/arch/arm/mach-tegra/board-dt-tegra114.c +++ b/arch/arm/mach-tegra/board-dt-tegra114.c @@ -36,7 +36,7 @@ static const char * const tegra114_dt_board_compat[] = { DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)") .smp = smp_ops(tegra_smp_ops), .map_io = tegra_map_common_io, - .init_early = tegra114_init_early, + .init_early = tegra_init_early, .init_irq = tegra_dt_init_irq, .init_time = clocksource_of_init, .init_machine = tegra114_dt_init, diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c index a0edf2510280..fca18e9157bd 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/board-dt-tegra20.c @@ -145,7 +145,7 @@ static const char *tegra20_dt_board_compat[] = { DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") .map_io = tegra_map_common_io, .smp = smp_ops(tegra_smp_ops), - .init_early = tegra20_init_early, + .init_early = tegra_init_early, .init_irq = tegra_dt_init_irq, .init_time = clocksource_of_init, .init_machine = tegra_dt_init, diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c index bf68567e549d..63f8139879b9 100644 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ b/arch/arm/mach-tegra/board-dt-tegra30.c @@ -3,7 +3,7 @@ * * NVIDIA Tegra30 device tree board support * - * Copyright (C) 2011 NVIDIA Corporation + * Copyright (C) 2011, 2013, NVIDIA Corporation * * Derived from: * @@ -50,7 +50,7 @@ static const char *tegra30_dt_board_compat[] = { DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)") .smp = smp_ops(tegra_smp_ops), .map_io = tegra_map_common_io, - .init_early = tegra30_init_early, + .init_early = tegra_init_early, .init_irq = tegra_dt_init_irq, .init_time = clocksource_of_init, .init_machine = tegra30_dt_init, diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 86851c81a350..60431de585ca 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -26,9 +26,7 @@ void tegra_assert_system_reset(char mode, const char *cmd); -void __init tegra20_init_early(void); -void __init tegra30_init_early(void); -void __init tegra114_init_early(void); +void __init tegra_init_early(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_dt_init_irq(void); diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 5449a3f2977b..f0315c95c76d 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -94,7 +94,7 @@ static void __init tegra_init_cache(void) } -static void __init tegra_init_early(void) +void __init tegra_init_early(void) { tegra_cpu_reset_handler_init(); tegra_apb_io_init(); @@ -102,31 +102,9 @@ static void __init tegra_init_early(void) tegra_init_cache(); tegra_pmc_init(); tegra_powergate_init(); + tegra_hotplug_init(); } -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void __init tegra20_init_early(void) -{ - tegra_init_early(); - tegra20_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void __init tegra30_init_early(void) -{ - tegra_init_early(); - tegra30_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -void __init tegra114_init_early(void) -{ - tegra_init_early(); -} -#endif - void __init tegra_init_late(void) { tegra_powergate_debugfs_init(); diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index a599f6e36dea..8da9f78475da 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -1,8 +1,7 @@ /* - * * Copyright (C) 2002 ARM Ltd. * All Rights Reserved - * Copyright (c) 2010, 2012 NVIDIA Corporation. All rights reserved. + * Copyright (c) 2010, 2012-2013, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,6 +14,7 @@ #include #include +#include "fuse.h" #include "sleep.h" static void (*tegra_hotplug_shutdown)(void); @@ -56,18 +56,13 @@ int tegra_cpu_disable(unsigned int cpu) return cpu == 0 ? -EPERM : 0; } -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -extern void tegra20_hotplug_shutdown(void); -void __init tegra20_hotplug_init(void) +void __init tegra_hotplug_init(void) { - tegra_hotplug_shutdown = tegra20_hotplug_shutdown; -} -#endif + if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) + return; -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -extern void tegra30_hotplug_shutdown(void); -void __init tegra30_hotplug_init(void) -{ - tegra_hotplug_shutdown = tegra30_hotplug_shutdown; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + tegra_hotplug_shutdown = tegra20_hotplug_shutdown; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + tegra_hotplug_shutdown = tegra30_hotplug_shutdown; } -#endif diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 4ffae541726e..970ebd5138b9 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -124,11 +124,11 @@ int tegra_sleep_cpu_finish(unsigned long); void tegra_disable_clean_inv_dcache(void); #ifdef CONFIG_HOTPLUG_CPU -void tegra20_hotplug_init(void); -void tegra30_hotplug_init(void); +void tegra20_hotplug_shutdown(void); +void tegra30_hotplug_shutdown(void); +void tegra_hotplug_init(void); #else -static inline void tegra20_hotplug_init(void) {} -static inline void tegra30_hotplug_init(void) {} +static inline void tegra_hotplug_init(void) {} #endif void tegra20_cpu_shutdown(int cpu); From 2dfc91e831f56b36966d5ef924ccd98354986f8f Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 13 Feb 2013 19:15:49 +0200 Subject: [PATCH 10/68] ARM: tegra: Rename board-dt-tegra20.c to tegra.c This is the preparation to unify "board-dt-tegra{20,30,114}.c" to a single file "tegra.c". Signed-off-by: Hiroshi Doyu Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/Makefile | 2 +- arch/arm/mach-tegra/{board-dt-tegra20.c => tegra.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename arch/arm/mach-tegra/{board-dt-tegra20.c => tegra.c} (100%) diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f6b46ae2b7f8..c6d6be2cc951 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += board-dt-tegra114.o ifeq ($(CONFIG_CPU_IDLE),y) diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/tegra.c similarity index 100% rename from arch/arm/mach-tegra/board-dt-tegra20.c rename to arch/arm/mach-tegra/tegra.c From 1b14f3a57b0a1a9e0ca7091ca9f8c14fe23dfd70 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 13 Feb 2013 19:15:50 +0200 Subject: [PATCH 11/68] ARM: tegra: Unify Device tree board files Unify board-dt-tegra{30,114} to the Tegra20 DT board file, "tegra.c". Signed-off-by: Hiroshi Doyu Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/Makefile | 4 +- arch/arm/mach-tegra/board-dt-tegra114.c | 46 ------------------- arch/arm/mach-tegra/board-dt-tegra30.c | 60 ------------------------- arch/arm/mach-tegra/tegra.c | 14 +++--- 4 files changed, 10 insertions(+), 114 deletions(-) delete mode 100644 arch/arm/mach-tegra/board-dt-tegra114.c delete mode 100644 arch/arm/mach-tegra/board-dt-tegra30.c diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index c6d6be2cc951..92703f955a37 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -10,6 +10,7 @@ obj-y += pm.o obj-y += reset.o obj-y += reset-handler.o obj-y += sleep.o +obj-y += tegra.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o @@ -27,9 +28,6 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra.o -obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o -obj-$(CONFIG_ARCH_TEGRA_114_SOC) += board-dt-tegra114.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o endif diff --git a/arch/arm/mach-tegra/board-dt-tegra114.c b/arch/arm/mach-tegra/board-dt-tegra114.c deleted file mode 100644 index 08e82941e53c..000000000000 --- a/arch/arm/mach-tegra/board-dt-tegra114.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * NVIDIA Tegra114 device tree board support - * - * Copyright (C) 2013 NVIDIA Corporation - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include - -#include - -#include "board.h" -#include "common.h" - -static void __init tegra114_dt_init(void) -{ - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char * const tegra114_dt_board_compat[] = { - "nvidia,tegra114", - NULL, -}; - -DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)") - .smp = smp_ops(tegra_smp_ops), - .map_io = tegra_map_common_io, - .init_early = tegra_init_early, - .init_irq = tegra_dt_init_irq, - .init_time = clocksource_of_init, - .init_machine = tegra114_dt_init, - .init_late = tegra_init_late, - .restart = tegra_assert_system_reset, - .dt_compat = tegra114_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c deleted file mode 100644 index 63f8139879b9..000000000000 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * arch/arm/mach-tegra/board-dt-tegra30.c - * - * NVIDIA Tegra30 device tree board support - * - * Copyright (C) 2011, 2013, NVIDIA Corporation - * - * Derived from: - * - * arch/arm/mach-tegra/board-dt-tegra20.c - * - * Copyright (C) 2010 Secret Lab Technologies, Ltd. - * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "board.h" -#include "common.h" -#include "iomap.h" - -static void __init tegra30_dt_init(void) -{ - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char *tegra30_dt_board_compat[] = { - "nvidia,tegra30", - NULL -}; - -DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)") - .smp = smp_ops(tegra_smp_ops), - .map_io = tegra_map_common_io, - .init_early = tegra_init_early, - .init_irq = tegra_dt_init_irq, - .init_time = clocksource_of_init, - .init_machine = tegra30_dt_init, - .init_late = tegra_init_late, - .restart = tegra_assert_system_reset, - .dt_compat = tegra30_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index fca18e9157bd..27232c901a22 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -1,6 +1,7 @@ /* - * nVidia Tegra device tree board support + * NVIDIA Tegra SoC device tree board support * + * Copyright (C) 2011, 2013, NVIDIA Corporation * Copyright (C) 2010 Secret Lab Technologies, Ltd. * Copyright (C) 2010 Google, Inc. * @@ -111,7 +112,8 @@ static void __init harmony_init(void) static void __init paz00_init(void) { - tegra_paz00_wifikill_init(); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra_paz00_wifikill_init(); } static struct { @@ -137,12 +139,14 @@ static void __init tegra_dt_init_late(void) } } -static const char *tegra20_dt_board_compat[] = { +static const char * const tegra_dt_board_compat[] = { + "nvidia,tegra114", + "nvidia,tegra30", "nvidia,tegra20", NULL }; -DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") +DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)") .map_io = tegra_map_common_io, .smp = smp_ops(tegra_smp_ops), .init_early = tegra_init_early, @@ -151,5 +155,5 @@ DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") .init_machine = tegra_dt_init, .init_late = tegra_dt_init_late, .restart = tegra_assert_system_reset, - .dt_compat = tegra20_dt_board_compat, + .dt_compat = tegra_dt_board_compat, MACHINE_END From 6f88fb8af6c67f281b8e2cd607f08e0089c8ccbe Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Mon, 4 Feb 2013 15:40:30 +0200 Subject: [PATCH 12/68] clocksource: tegra: move to of_clk_get The new clockframework introduced DT IDs for each clock. To be able to remove the device registrations, this driver needs to be updated to use the DT IDs. Note that the actual removal of the clk_register_clkdev() calls will be done in a later series. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra20.dtsi | 2 ++ arch/arm/boot/dts/tegra30.dtsi | 2 ++ drivers/clocksource/tegra20_timer.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 9a428931d042..37701d8727d8 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -144,6 +144,7 @@ 0 1 0x04 0 41 0x04 0 42 0x04>; + clocks = <&tegra_car 5>; }; tegra_car: clock { @@ -303,6 +304,7 @@ compatible = "nvidia,tegra20-rtc"; reg = <0x7000e000 0x100>; interrupts = <0 2 0x04>; + clocks = <&tegra_car 4>; }; i2c@7000c000 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 767803e1fd55..7effa93ea9d9 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -147,6 +147,7 @@ 0 42 0x04 0 121 0x04 0 122 0x04>; + clocks = <&tegra_car 5>; }; tegra_car: clock { @@ -290,6 +291,7 @@ compatible = "nvidia,tegra30-rtc", "nvidia,tegra20-rtc"; reg = <0x7000e000 0x100>; interrupts = <0 2 0x04>; + clocks = <&tegra_car 4>; }; i2c@7000c000 { diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 0bde03feb095..bc4b8ad78aea 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -189,7 +189,7 @@ static void __init tegra20_init_timer(void) BUG(); } - clk = clk_get_sys("timer", NULL); + clk = of_clk_get(np, 0); if (IS_ERR(clk)) { pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n"); rate = 12000000; @@ -216,7 +216,7 @@ static void __init tegra20_init_timer(void) * rtc registers are used by read_persistent_clock, keep the rtc clock * enabled */ - clk = clk_get_sys("rtc-tegra", NULL); + clk = of_clk_get(np, 0); if (IS_ERR(clk)) pr_warn("Unable to get rtc-tegra clock\n"); else From 0d1f79b033bb87091c65cd11bd2dcb6a583c8320 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Fri, 22 Feb 2013 14:24:27 +0800 Subject: [PATCH 13/68] ARM: tegra: refactor tegra{20,30}_boot_secondary "tegra_boot_secondary()" has many condition branches for some Tegra SoC generations in a single function so that it's not easy to compile a kernel only for a single SoC if one wants with some reason, debug purpose(?). This patch provides SoC specific version of boot_secondary(), tegra{20,30}_boot_secondary(). This could allow any combination of SoC to be built. Those boot_secondary functions can be preparation when we ntroduce chip specific function pointers in the future without having chip dependent branches around. Also removed unused definition/prototpye. Signed-off-by: Hiroshi Doyu [josephl: remove the Tegra114 part of the original patch] Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/platsmp.c | 95 ++++++++++++++--------------------- 1 file changed, 39 insertions(+), 56 deletions(-) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index e78d52d83acd..41971ac9376f 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -35,13 +35,8 @@ #include "common.h" #include "iomap.h" -extern void tegra_secondary_startup(void); - static cpumask_t tegra_cpu_init_mask; -#define EVP_CPU_RESET_VECTOR \ - (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) - static void __cpuinit tegra_secondary_init(unsigned int cpu) { /* @@ -54,26 +49,48 @@ static void __cpuinit tegra_secondary_init(unsigned int cpu) cpumask_set_cpu(cpu, &tegra_cpu_init_mask); } -static int tegra20_power_up_cpu(unsigned int cpu) + +static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle) { - /* Enable the CPU clock. */ + cpu = cpu_logical_map(cpu); + + /* + * Force the CPU into reset. The CPU must remain in reset when + * the flow controller state is cleared (which will cause the + * flow controller to stop driving reset if the CPU has been + * power-gated via the flow controller). This will have no + * effect on first boot of the CPU since it should already be + * in reset. + */ + tegra_put_cpu_in_reset(cpu); + + /* + * Unhalt the CPU. If the flow controller was used to + * power-gate the CPU this will cause the flow controller to + * stop driving reset. The CPU will remain in reset because the + * clock and reset block is now driving reset. + */ + flowctrl_write_cpu_halt(cpu, 0); + tegra_enable_cpu_clock(cpu); - - /* Clear flow controller CSR. */ - flowctrl_write_cpu_csr(cpu, 0); - + flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ + tegra_cpu_out_of_reset(cpu); return 0; } -static int tegra30_power_up_cpu(unsigned int cpu) +static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) { int ret, pwrgateid; unsigned long timeout; + cpu = cpu_logical_map(cpu); pwrgateid = tegra_cpu_powergate_id(cpu); if (pwrgateid < 0) return pwrgateid; + tegra_put_cpu_in_reset(cpu); + flowctrl_write_cpu_halt(cpu, 0); + /* * The power up sequence of cold boot CPU and warm boot CPU * was different. @@ -85,7 +102,7 @@ static int tegra30_power_up_cpu(unsigned int cpu) * the IO clamps. * For cold boot CPU, do not wait. After the cold boot CPU be * booted, it will run to tegra_secondary_init() and set - * tegra_cpu_init_mask which influences what tegra30_power_up_cpu() + * tegra_cpu_init_mask which influences what tegra30_boot_secondary() * next time around. */ if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { @@ -129,54 +146,20 @@ remove_clamps: udelay(10); - /* Clear flow controller CSR. */ - flowctrl_write_cpu_csr(cpu, 0); - + flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ + tegra_cpu_out_of_reset(cpu); return 0; } -static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle) +static int __cpuinit tegra_boot_secondary(unsigned int cpu, + struct task_struct *idle) { - int status; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + return tegra20_boot_secondary(cpu, idle); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + return tegra30_boot_secondary(cpu, idle); - cpu = cpu_logical_map(cpu); - - /* - * Force the CPU into reset. The CPU must remain in reset when the - * flow controller state is cleared (which will cause the flow - * controller to stop driving reset if the CPU has been power-gated - * via the flow controller). This will have no effect on first boot - * of the CPU since it should already be in reset. - */ - tegra_put_cpu_in_reset(cpu); - - /* - * Unhalt the CPU. If the flow controller was used to power-gate the - * CPU this will cause the flow controller to stop driving reset. - * The CPU will remain in reset because the clock and reset block - * is now driving reset. - */ - flowctrl_write_cpu_halt(cpu, 0); - - switch (tegra_chip_id) { - case TEGRA20: - status = tegra20_power_up_cpu(cpu); - break; - case TEGRA30: - status = tegra30_power_up_cpu(cpu); - break; - default: - status = -EINVAL; - break; - } - - if (status) - goto done; - - /* Take the CPU out of reset. */ - tegra_cpu_out_of_reset(cpu); -done: - return status; + return -EINVAL; } static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) From 88c4aba92bc57334119bcff58ac87152c3f2981e Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 26 Feb 2013 16:27:42 +0000 Subject: [PATCH 14/68] ARM: tegra: pmc: add specific compatible DT string for Tegra30 and Tegra114 The PMC HW is not 100% compatible across all Tegra series. We need to specify each of them in the DT match table. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/pmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index d4fdb5fcec20..5d79d34e2c0f 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -36,6 +36,8 @@ static inline void tegra_pmc_writel(u32 val, u32 reg) #ifdef CONFIG_OF static const struct of_device_id matches[] __initconst = { + { .compatible = "nvidia,tegra114-pmc" }, + { .compatible = "nvidia,tegra30-pmc" }, { .compatible = "nvidia,tegra20-pmc" }, { } }; From 2b84e53beb236aec89b7ef87b4fc970f175e4feb Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 26 Feb 2013 16:27:43 +0000 Subject: [PATCH 15/68] ARM: tegra: fix the PMC compatible string in DT The PMC HW is not 100% compatible across all Tegra series. We need to specify them in DT. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra114.dtsi | 2 +- arch/arm/boot/dts/tegra30.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index 1dfaf2874c57..e4ddeddcd437 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -99,7 +99,7 @@ }; pmc { - compatible = "nvidia,tegra114-pmc", "nvidia,tegra30-pmc"; + compatible = "nvidia,tegra114-pmc"; reg = <0x7000e400 0x400>; }; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 7effa93ea9d9..d376959b7731 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -424,7 +424,7 @@ }; pmc { - compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc"; + compatible = "nvidia,tegra30-pmc"; reg = <0x7000e400 0x400>; }; From 291fde31a9e72ea81951f3f77444f61789276655 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Thu, 28 Feb 2013 21:32:10 +0000 Subject: [PATCH 16/68] ARM: tegra: pmc: convert PMC driver to support DT only The Tegra kernel only support boot from DT now. Clean up the PMC driver to support DT only, that includes: * remove the ifdef of CONFIG_OF * replace the static mapping of PMC addr to map from DT Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/pmc.c | 51 +++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index 5d79d34e2c0f..a916ecaa96e6 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012,2013 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,59 +18,52 @@ #include #include #include - -#include "iomap.h" +#include #define PMC_CTRL 0x0 #define PMC_CTRL_INTR_LOW (1 << 17) +static void __iomem *tegra_pmc_base; +static bool tegra_pmc_invert_interrupt; + static inline u32 tegra_pmc_readl(u32 reg) { - return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); + return readl(tegra_pmc_base + reg); } static inline void tegra_pmc_writel(u32 val, u32 reg) { - writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); + writel(val, tegra_pmc_base + reg); } -#ifdef CONFIG_OF static const struct of_device_id matches[] __initconst = { { .compatible = "nvidia,tegra114-pmc" }, { .compatible = "nvidia,tegra30-pmc" }, { .compatible = "nvidia,tegra20-pmc" }, { } }; -#endif + +static void tegra_pmc_parse_dt(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, matches); + BUG_ON(!np); + + tegra_pmc_base = of_iomap(np, 0); + + tegra_pmc_invert_interrupt = of_property_read_bool(np, + "nvidia,invert-interrupt"); +} void __init tegra_pmc_init(void) { - /* - * For now, Harmony is the only board that uses the PMC, and it wants - * the signal inverted. Seaboard would too if it used the PMC. - * Hopefully by the time other boards want to use the PMC, everything - * will be device-tree, or they also want it inverted. - */ - bool invert_interrupt = true; u32 val; -#ifdef CONFIG_OF - if (of_have_populated_dt()) { - struct device_node *np; - - invert_interrupt = false; - - np = of_find_matching_node(NULL, matches); - if (np) { - if (of_find_property(np, "nvidia,invert-interrupt", - NULL)) - invert_interrupt = true; - } - } -#endif + tegra_pmc_parse_dt(); val = tegra_pmc_readl(PMC_CTRL); - if (invert_interrupt) + if (tegra_pmc_invert_interrupt) val |= PMC_CTRL_INTR_LOW; else val &= ~PMC_CTRL_INTR_LOW; From c141753fc385df98a33790b59a22894537031a24 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Thu, 28 Feb 2013 21:32:11 +0000 Subject: [PATCH 17/68] ARM: tegra: pmc: add power on function for secondary CPUs Adding the power on function for secondary CPUs in PMC driver, this can help us to remove legacy powergate driver and add generic power domain support later. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/pmc.c | 101 +++++++++++++++++++++++++++++++++++++- arch/arm/mach-tegra/pmc.h | 4 ++ 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index a916ecaa96e6..b30e921cc3a9 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -20,8 +20,26 @@ #include #include -#define PMC_CTRL 0x0 -#define PMC_CTRL_INTR_LOW (1 << 17) +#define PMC_CTRL 0x0 +#define PMC_CTRL_INTR_LOW (1 << 17) +#define PMC_PWRGATE_TOGGLE 0x30 +#define PMC_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_REMOVE_CLAMPING 0x34 +#define PMC_PWRGATE_STATUS 0x38 + +#define TEGRA_POWERGATE_PCIE 3 +#define TEGRA_POWERGATE_VDEC 4 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 + +static u8 tegra_cpu_domains[] = { + 0xFF, /* not available for CPU0 */ + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; +static DEFINE_SPINLOCK(tegra_powergate_lock); static void __iomem *tegra_pmc_base; static bool tegra_pmc_invert_interrupt; @@ -36,6 +54,85 @@ static inline void tegra_pmc_writel(u32 val, u32 reg) writel(val, tegra_pmc_base + reg); } +static int tegra_pmc_get_cpu_powerdomain_id(int cpuid) +{ + if (cpuid <= 0 || cpuid >= num_possible_cpus()) + return -EINVAL; + return tegra_cpu_domains[cpuid]; +} + +static bool tegra_pmc_powergate_is_powered(int id) +{ + return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1; +} + +static int tegra_pmc_powergate_set(int id, bool new_state) +{ + bool old_state; + unsigned long flags; + + spin_lock_irqsave(&tegra_powergate_lock, flags); + + old_state = tegra_pmc_powergate_is_powered(id); + WARN_ON(old_state == new_state); + + tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE); + + spin_unlock_irqrestore(&tegra_powergate_lock, flags); + + return 0; +} + +static int tegra_pmc_powergate_remove_clamping(int id) +{ + u32 mask; + + /* + * Tegra has a bug where PCIE and VDE clamping masks are + * swapped relatively to the partition ids. + */ + if (id == TEGRA_POWERGATE_VDEC) + mask = (1 << TEGRA_POWERGATE_PCIE); + else if (id == TEGRA_POWERGATE_PCIE) + mask = (1 << TEGRA_POWERGATE_VDEC); + else + mask = (1 << id); + + tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING); + + return 0; +} + +bool tegra_pmc_cpu_is_powered(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return false; + return tegra_pmc_powergate_is_powered(id); +} + +int tegra_pmc_cpu_power_on(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return id; + return tegra_pmc_powergate_set(id, true); +} + +int tegra_pmc_cpu_remove_clamping(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return id; + return tegra_pmc_powergate_remove_clamping(id); +} + static const struct of_device_id matches[] __initconst = { { .compatible = "nvidia,tegra114-pmc" }, { .compatible = "nvidia,tegra30-pmc" }, diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 8995ee4a8768..7d44710368be 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,10 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +bool tegra_pmc_cpu_is_powered(int cpuid); +int tegra_pmc_cpu_power_on(int cpuid); +int tegra_pmc_cpu_remove_clamping(int cpuid); + void tegra_pmc_init(void); #endif From 7e56474456221541aab7b2fe415ff400d7c9910a Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 26 Feb 2013 16:28:06 +0000 Subject: [PATCH 18/68] ARM: tegra: replace the CPU power on function with PMC call Using the CPU power on function in PMC driver to bring up secondary CPUs, because we are going to re-factor powergate driver to support generic power domain. It will be removed later and added the generic power domain support in PMC driver. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/platsmp.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 41971ac9376f..601bd0c3f983 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -26,11 +26,10 @@ #include #include -#include - #include "fuse.h" #include "flowctrl.h" #include "reset.h" +#include "pmc.h" #include "common.h" #include "iomap.h" @@ -80,14 +79,10 @@ static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle) static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) { - int ret, pwrgateid; + int ret; unsigned long timeout; cpu = cpu_logical_map(cpu); - pwrgateid = tegra_cpu_powergate_id(cpu); - if (pwrgateid < 0) - return pwrgateid; - tegra_put_cpu_in_reset(cpu); flowctrl_write_cpu_halt(cpu, 0); @@ -108,7 +103,7 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { timeout = jiffies + msecs_to_jiffies(50); do { - if (tegra_powergate_is_powered(pwrgateid)) + if (tegra_pmc_cpu_is_powered(cpu)) goto remove_clamps; udelay(10); } while (time_before(jiffies, timeout)); @@ -120,14 +115,14 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) * be un-gated by un-toggling the power gate register * manually. */ - if (!tegra_powergate_is_powered(pwrgateid)) { - ret = tegra_powergate_power_on(pwrgateid); + if (!tegra_pmc_cpu_is_powered(cpu)) { + ret = tegra_pmc_cpu_power_on(cpu); if (ret) return ret; /* Wait for the power to come up. */ timeout = jiffies + msecs_to_jiffies(100); - while (tegra_powergate_is_powered(pwrgateid)) { + while (tegra_pmc_cpu_is_powered(cpu)) { if (time_after(jiffies, timeout)) return -ETIMEDOUT; udelay(10); @@ -140,7 +135,7 @@ remove_clamps: udelay(10); /* Remove I/O clamps. */ - ret = tegra_powergate_remove_clamping(pwrgateid); + ret = tegra_pmc_cpu_remove_clamping(cpu); if (ret) return ret; From e562b86581d2ccf3faaf55b1235b4e6438cb7712 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 26 Feb 2013 16:28:07 +0000 Subject: [PATCH 19/68] ARM: tegra: bring up secondary CPU for Tegra114 The secondary CPU can be brought up by toggling the power in PMC. Then the flow controller will release CPU to go by clearing the reset and clamp signal automatically. Based on the work by: Bo Yan Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/platsmp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 601bd0c3f983..516aab28fe34 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -146,6 +146,12 @@ remove_clamps: return 0; } +static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + cpu = cpu_logical_map(cpu); + return tegra_pmc_cpu_power_on(cpu); +} + static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle) { @@ -153,6 +159,8 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, return tegra20_boot_secondary(cpu, idle); if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) return tegra30_boot_secondary(cpu, idle); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) + return tegra114_boot_secondary(cpu, idle); return -EINVAL; } From d591fdf8e23ef2abf57bdfbc4b596bf1a43d15b4 Mon Sep 17 00:00:00 2001 From: Danny Huang Date: Thu, 14 Mar 2013 08:48:40 +0800 Subject: [PATCH 20/68] ARM: tegra: expose chip ID and revision Expose Tegra chip ID and revision in /sys/devices/soc for user mode usage Signed-off-by: Danny Huang Signed-off-by: Stephen Warren --- arch/arm/Kconfig | 1 + arch/arm/mach-tegra/tegra.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5b714695b01b..59b7be794582 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -675,6 +675,7 @@ config ARCH_TEGRA select HAVE_CLK select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 + select SOC_BUS select SPARSE_IRQ select USE_OF help diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 27232c901a22..84deeab23ee7 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -42,6 +44,7 @@ #include "board.h" #include "common.h" +#include "fuse.h" #include "iomap.h" static struct tegra_ehci_platform_data tegra_ehci1_pdata = { @@ -80,12 +83,36 @@ static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { static void __init tegra_dt_init(void) { + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device *parent = NULL; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + goto out; + + soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra"); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", tegra_chip_id); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr); + goto out; + } + + parent = soc_device_to_device(soc_dev); + /* * Finished with the static registrations now; fill in the missing * devices */ +out: of_platform_populate(NULL, of_default_bus_match_table, - tegra20_auxdata_lookup, NULL); + tegra20_auxdata_lookup, parent); } static void __init trimslice_init(void) From 7495b2eb0770b85e58af98b99faaf853e9563784 Mon Sep 17 00:00:00 2001 From: Danny Huang Date: Mon, 18 Mar 2013 19:17:34 +0800 Subject: [PATCH 21/68] ARM: tegra: add speedo-based process id for Tegra114 Add speedo-based process identification for Tegra114. Based on the work by: Alex Frid Signed-off-by: Danny Huang [swarren: added include of bug.h] Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/fuse.c | 4 + arch/arm/mach-tegra/fuse.h | 7 ++ arch/arm/mach-tegra/tegra114_speedo.c | 104 ++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 arch/arm/mach-tegra/tegra114_speedo.c diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 92703f955a37..e40326d0e29f 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o endif diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index f7db0782a6b6..e035cd284a6e 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/fuse.c * * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross @@ -137,6 +138,9 @@ void tegra_init_fuse(void) tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT; tegra_init_speedo_data = &tegra30_init_speedo_data; break; + case TEGRA114: + tegra_init_speedo_data = &tegra114_init_speedo_data; + break; default: pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id); tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index da78434678c7..aacc00d05980 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross @@ -66,4 +67,10 @@ void tegra30_init_speedo_data(void); static inline void tegra30_init_speedo_data(void) {} #endif +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_init_speedo_data(void); +#else +static inline void tegra114_init_speedo_data(void) {} +#endif + #endif diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c new file mode 100644 index 000000000000..5218d4853cd3 --- /dev/null +++ b/arch/arm/mach-tegra/tegra114_speedo.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS_NUM 2 +#define CPU_PROCESS_CORNERS_NUM 2 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { + {1123, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { + {1695, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold) +{ + u32 tmp; + + switch (sku) { + case 0x00: + case 0x10: + case 0x05: + case 0x06: + tegra_cpu_speedo_id = 1; + tegra_soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + + case 0x03: + case 0x04: + tegra_cpu_speedo_id = 2; + tegra_soc_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + + default: + pr_err("Tegra114 Unknown SKU %d\n", sku); + tegra_cpu_speedo_id = 0; + tegra_soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + } + + if (rev == TEGRA_REVISION_A01) { + tmp = tegra_fuse_readl(0x270) << 1; + tmp |= tegra_fuse_readl(0x26c); + if (!tmp) + tegra_cpu_speedo_id = 0; + } +} + +void tegra114_init_speedo_data(void) +{ + u32 cpu_speedo_val; + u32 core_speedo_val; + int threshold; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold); + + cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024; + core_speedo_val = tegra_fuse_readl(0x134); + + for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) + if (cpu_speedo_val < cpu_process_speedos[threshold][i]) + break; + tegra_cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) + if (core_speedo_val < core_process_speedos[threshold][i]) + break; + tegra_core_process_id = i; +} From 30ee400614385ac49f4c9b4bc03d77ff8f07a61e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 12:07:16 -0200 Subject: [PATCH 22/68] clk: mxs: Fix sparse warnings Fix the following sparse warnings: drivers/clk/mxs/clk.c:17:1: warning: symbol 'mxs_lock' was not declared. Should it be static? drivers/clk/mxs/clk.c:19:5: warning: symbol 'mxs_clk_wait' was not declared. Should it be static? Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mike Turquette --- drivers/clk/mxs/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c index b24d56067c80..5301bce8957b 100644 --- a/drivers/clk/mxs/clk.c +++ b/drivers/clk/mxs/clk.c @@ -13,6 +13,7 @@ #include #include #include +#include "clk.h" DEFINE_SPINLOCK(mxs_lock); From 3d6ee287a3e341c88eafd0b4620b12d640b3736b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:02 +0100 Subject: [PATCH 23/68] clk: Introduce optional is_prepared callback To reflect whether a clk_hw is prepared the clk_hw may implement the optional is_prepared callback. If not implemented we fall back to use the software prepare counter. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 21 +++++++++++++++++++++ include/linux/clk-provider.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b2405806..7571b5054f3c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -451,6 +451,27 @@ unsigned long __clk_get_flags(struct clk *clk) return !clk ? 0 : clk->flags; } +bool __clk_is_prepared(struct clk *clk) +{ + int ret; + + if (!clk) + return false; + + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!clk->ops->is_prepared) { + ret = clk->prepare_count ? 1 : 0; + goto out; + } + + ret = clk->ops->is_prepared(clk->hw); +out: + return !!ret; +} + bool __clk_is_enabled(struct clk *clk) { int ret; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7f197d7addb0..ee946862e058 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -45,6 +45,10 @@ struct clk_hw; * undo any work done in the @prepare callback. Called with * prepare_lock held. * + * @is_prepared: Queries the hardware to determine if the clock is prepared. + * This function is allowed to sleep. Optional, if this op is not + * set then the prepare count will be used. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -108,6 +112,7 @@ struct clk_hw; struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); + int (*is_prepared)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); @@ -351,6 +356,7 @@ unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk); unsigned long __clk_get_flags(struct clk *clk); +bool __clk_is_prepared(struct clk *clk); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); From 1c155b3dfe08351f5fc811062648969f1ba7af53 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:03 +0100 Subject: [PATCH 24/68] clk: Unprepare the unused prepared slow clocks at late init The unused ungated fast clocks are already being disabled from clk_disable_unused at late init. This patch extend this sequence to the slow unused prepared clocks to be unprepared. Unless the optional .is_prepared callback is implemented by a clk_hw the clk_disable_unused sequence will not unprepare any unused clocks, since it will fall back to use the software prepare counter. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette [mturquette@linaro.org: fixed hlist accessors per b67bfe0d] --- drivers/clk/clk.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 7571b5054f3c..c0141f3e1109 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -335,6 +335,28 @@ late_initcall(clk_debug_init); static inline int clk_debug_register(struct clk *clk) { return 0; } #endif +/* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk *clk) +{ + struct clk *child; + + if (!clk) + return; + + hlist_for_each_entry(child, &clk->children, child_node) + clk_unprepare_unused_subtree(child); + + if (clk->prepare_count) + return; + + if (clk->flags & CLK_IGNORE_UNUSED) + return; + + if (__clk_is_prepared(clk)) + if (clk->ops->unprepare) + clk->ops->unprepare(clk->hw); +} + /* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { @@ -386,6 +408,12 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_disable_unused_subtree(clk); + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_unprepare_unused_subtree(clk); + + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(clk); + mutex_unlock(&prepare_lock); return 0; From 3cc8247f1dce79511de8bf0f69ab02a46cc315b7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:04 +0100 Subject: [PATCH 25/68] clk: Introduce optional unprepare_unused callback An unprepare_unused callback is introduced due to the same reasons to why the disable_unused callback was added. During the clk_disable_unused sequence, those clk_hw that needs specific treatment with regards to being unprepared, shall implement the unprepare_unused callback. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 7 +++++-- include/linux/clk-provider.h | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c0141f3e1109..253792a46c08 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -352,9 +352,12 @@ static void clk_unprepare_unused_subtree(struct clk *clk) if (clk->flags & CLK_IGNORE_UNUSED) return; - if (__clk_is_prepared(clk)) - if (clk->ops->unprepare) + if (__clk_is_prepared(clk)) { + if (clk->ops->unprepare_unused) + clk->ops->unprepare_unused(clk->hw); + else if (clk->ops->unprepare) clk->ops->unprepare(clk->hw); + } } /* caller must hold prepare_lock */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index ee946862e058..56e6cc12c796 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -49,6 +49,10 @@ struct clk_hw; * This function is allowed to sleep. Optional, if this op is not * set then the prepare count will be used. * + * @unprepare_unused: Unprepare the clock atomically. Only called from + * clk_disable_unused for prepare clocks with special needs. + * Called with prepare mutex held. This function may sleep. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -113,6 +117,7 @@ struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); int (*is_prepared)(struct clk_hw *hw); + void (*unprepare_unused)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); From 2850985f7749abca7fc374a438bc0126ca28c9c4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:05 +0100 Subject: [PATCH 26/68] clk: ux500: Support is_prepared callback for clk-prcmu To be able to gate unused prcmu clocks from the clk_disable_unused sequence, clk-prcmu now implements the is_prepared callback. Signed-off-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/ux500/clk-prcmu.c | 130 ++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 52 deletions(-) diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf59..9d832567c6be 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -20,15 +20,23 @@ struct clk_prcmu { struct clk_hw hw; u8 cg_sel; + int is_prepared; int is_enabled; + int opp_requested; }; /* PRCMU clock operations. */ static int clk_prcmu_prepare(struct clk_hw *hw) { + int ret; struct clk_prcmu *clk = to_clk_prcmu(hw); - return prcmu_request_clock(clk->cg_sel, true); + + ret = prcmu_request_clock(clk->cg_sel, true); + if (!ret) + clk->is_prepared = 1; + + return ret;; } static void clk_prcmu_unprepare(struct clk_hw *hw) @@ -37,6 +45,14 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, hw->init->name); + else + clk->is_prepared = 0; +} + +static int clk_prcmu_is_prepared(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_prepared; } static int clk_prcmu_enable(struct clk_hw *hw) @@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, return prcmu_set_clock_rate(clk->cg_sel, rate); } -static int request_ape_opp100(bool enable) -{ - static int reqs; - int err = 0; - - if (enable) { - if (!reqs) - err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, - "clock", 100); - if (!err) - reqs++; - } else { - reqs--; - if (!reqs) - prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, - "clock"); - } - return err; -} - static int clk_prcmu_opp_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = request_ape_opp100(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk), + 100); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", + __func__, hw->init->name); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) - request_ape_opp100(false); + if (err) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (request_ape_opp100(false)) - goto out_error; - return; + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); + return; + } -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (clk->opp_requested) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) @@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = prcmu_request_ape_opp_100_voltage(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_request_ape_opp_100_voltage(true); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", + __func__, hw->init->name); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) + if (err) { prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (prcmu_request_ape_opp_100_voltage(false)) - goto out_error; - return; + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); + return; + } -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (clk->opp_requested) { + prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { static struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = { static struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name, } clk->cg_sel = cg_sel; + clk->is_prepared = 1; clk->is_enabled = 1; + clk->opp_requested = 0; /* "rate" can be used for changing the initial frequency */ if (rate) prcmu_set_clock_rate(cg_sel, rate); From 0e646c52cf0ee186ec50b41c4db8cf81500c8dd1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 16:22:29 +0100 Subject: [PATCH 27/68] clk: Add axi-clkgen driver This driver adds support for the AXI clkgen pcore to the common clock framework. The AXI clkgen pcore is a AXI front-end to the MMCM_ADV frequency synthesizer commonly found in Xilinx FPGAs. The AXI clkgen pcore is used in Analog Devices' reference designs targeting Xilinx FPGAs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/axi-clkgen.txt | 22 ++ drivers/clk/Kconfig | 8 + drivers/clk/Makefile | 1 + drivers/clk/clk-axi-clkgen.c | 331 ++++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/axi-clkgen.txt create mode 100644 drivers/clk/clk-axi-clkgen.c diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt new file mode 100644 index 000000000000..028b493e97ff --- /dev/null +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt @@ -0,0 +1,22 @@ +Binding for the axi-clkgen clock generator + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "adi,axi-clkgen". +- #clock-cells : from common clock binding; Should always be set to 0. +- reg : Address and length of the axi-clkgen register set. +- clocks : Phandle and clock specifier for the parent clock. + +Optional properties: +- clock-output-names : From common clock binding. + +Example: + clock@0xff000000 { + compatible = "adi,axi-clkgen"; + #clock-cells = <0>; + reg = <0xff000000 0x1000>; + clocks = <&osc 1>; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8c..a64caefdba12 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,6 +63,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d926..1c22f9dc721d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 000000000000..8137327847c3 --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { + .driver = { + .name = "adi-axi-clkgen", + .owner = THIS_MODULE, + .of_match_table = axi_clkgen_ids, + }, + .probe = axi_clkgen_probe, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); From a368a6a33b107d680e955c799e6e1e3d6b4bbe8a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Thu, 28 Feb 2013 09:59:07 -0400 Subject: [PATCH 28/68] documentation: clk: fix couple of misspelling Correcting misspelling inside the clk.txt. Signed-off-by: Eduardo Valentin Signed-off-by: Mike Turquette --- Documentation/clk.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 1943fae014fd..4274a546eb57 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw) }; Below is a matrix detailing which clk_ops are mandatory based upon the -hardware capbilities of that clock. A cell marked as "y" means +hardware capabilities of that clock. A cell marked as "y" means mandatory, a cell marked as "n" implies that either including that -callback is invalid or otherwise uneccesary. Empty cells are either +callback is invalid or otherwise unnecessary. Empty cells are either optional or must be evaluated on a case-by-case basis. clock hardware characteristics From 04981724172062029c4986e1d90732ff3c574944 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Thu, 7 Mar 2013 12:35:24 +0530 Subject: [PATCH 29/68] clk:SPEAr1340: Correct parent clock configuration This patch corrects wrongly configured parent clock for following devices: * Video enc/decoder * Video ip * Pin control * ACP * camx Signed-off-by: Vipul Kumar Samar Reviewed-by: Shiraz Hashim Acked-by: Viresh Kumar Signed-off-by: Mike Turquette --- drivers/clk/spear/spear1340_clock.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 82abea366b78..35e7e2698e10 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -960,47 +960,47 @@ void __init spear1340_clk_init(void) SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); - clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, + clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "acp_clk"); - clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, + clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "e2800000.gpio"); - clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, + clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_dec"); - clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, + clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_enc"); - clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, + clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "spear_vip"); - clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, + clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0200000.cam0"); - clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, + clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0300000.cam1"); - clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, + clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0400000.cam2"); - clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, + clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0500000.cam3"); From f15ea6cbc81b1369c5eefcd8eada35057c46ab86 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 11 Mar 2013 23:55:18 +0800 Subject: [PATCH 30/68] clk: prima2: fix return value check in sirfsoc_of_clk_init() In case of error, the function clk_get() returns ERR_PTR() not NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Mike Turquette [mturquette@linaro.org: added missing parenthesis to fix compile break] --- drivers/clk/clk-prima2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be2..643ca653fef0 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void) for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); - BUG_ON(!prima2_clks[i]); + BUG_ON(IS_ERR(prima2_clks[i])); } clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[io], NULL, "io"); From 5fda6858a49c2d8706adcc05f083b64af172d3eb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 13 Mar 2013 15:17:49 +0530 Subject: [PATCH 31/68] clk: Fix incorrect return type in clk.c Return type of function clk_propagate_rate_change is a pointer. But 0 was being returned. Change it to NULL. Silences the following warning: drivers/clk/clk.c:977:24: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Reviewed-by: Pankaj Jangra Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 253792a46c08..5e8ffff99362 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1026,7 +1026,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) - return 0; + return NULL; if (clk->notifier_count) { ret = __clk_notify(clk, event, clk->rate, clk->new_rate); From ce4f3313b05c836c21a91ac89f87dccf84ce9561 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Fri, 22 Mar 2013 14:07:53 +0200 Subject: [PATCH 32/68] clk: add table lookup to mux Add a table lookup feature to the mux clock. Also allow arbitrary masks instead of the width. This will be used by some clocks on Tegra114. Also adapt the tegra periph clk because it uses struct clk_mux directly. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/clk-mux.c | 50 ++++++++++++++++++++++++++++-------- drivers/clk/tegra/clk.h | 27 +++++++++++++------ include/linux/clk-private.h | 2 +- include/linux/clk-provider.h | 9 ++++++- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce4..25b1734560d0 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); + int num_parents = __clk_get_num_parents(hw->clk); u32 val; /* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * val = 0x4 really means "bit 2, index starts at bit 0" */ val = readl(mux->reg) >> mux->shift; - val &= (1 << mux->width) - 1; + val &= mux->mask; + + if (mux->table) { + int i; + + for (i = 0; i < num_parents; i++) + if (mux->table[i] == val) + return i; + return -EINVAL; + } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) if (val && (mux->flags & CLK_MUX_INDEX_ONE)) val--; - if (val >= __clk_get_num_parents(hw->clk)) + if (val >= num_parents) return -EINVAL; return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) u32 val; unsigned long flags = 0; - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); + if (mux->table) + index = mux->table[index]; - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; + else { + if (mux->flags & CLK_MUX_INDEX_BIT) + index = (1 << ffs(index)); + + if (mux->flags & CLK_MUX_INDEX_ONE) + index++; + } if (mux->lock) spin_lock_irqsave(mux->lock, flags); val = readl(mux->reg); - val &= ~(((1 << mux->width) - 1) << mux->shift); + val &= ~(mux->mask << mux->shift); val |= index << mux->shift; writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name, /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; - mux->width = width; + mux->mask = mask; mux->flags = clk_mux_flags; mux->lock = lock; + mux->table = table; mux->hw.init = &init; clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name, return clk; } + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0744731c6229..a09d7dcaf183 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -355,15 +355,16 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, struct tegra_clk_periph *periph, void __iomem *clk_base, u32 offset); -#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \ _div_shift, _div_width, _div_frac_width, \ _div_flags, _clk_num, _enb_refcnt, _regs, \ - _gate_flags) \ + _gate_flags, _table) \ { \ .mux = { \ .flags = _mux_flags, \ .shift = _mux_shift, \ - .width = _mux_width, \ + .mask = _mux_mask, \ + .table = _table, \ }, \ .divider = { \ .flags = _div_flags, \ @@ -393,26 +394,36 @@ struct tegra_periph_init_data { const char *dev_id; }; -#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ - _mux_shift, _mux_width, _mux_flags, _div_shift, \ +#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \ { \ .name = _name, \ .clk_id = _clk_id, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ + .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \ _mux_flags, _div_shift, \ _div_width, _div_frac_width, \ _div_flags, _clk_num, \ _enb_refcnt, _regs, \ - _gate_flags), \ + _gate_flags, _table), \ .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ } +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_width, _mux_flags, _div_shift, \ + _div_width, _div_frac_width, _div_flags, _regs, \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ + _div_shift, _div_width, _div_frac_width, _div_flags, \ + _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ + NULL) + /** * struct clk_super_mux - super clock * diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index 9c7f5807824b..dd7adff76e81 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -152,7 +152,7 @@ struct clk { }, \ .reg = _reg, \ .shift = _shift, \ - .width = _width, \ + .mask = BIT(_width) - 1, \ .flags = _mux_flags, \ .lock = _lock, \ }; \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 56e6cc12c796..63ba3b740794 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -297,8 +297,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, struct clk_mux { struct clk_hw hw; void __iomem *reg; + u32 *table; + u32 mask; u8 shift; - u8 width; u8 flags; spinlock_t *lock; }; @@ -307,11 +308,17 @@ struct clk_mux { #define CLK_MUX_INDEX_BIT BIT(1) extern const struct clk_ops clk_mux_ops; + struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); +struct clk *clk_register_mux_table(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock); + /** * struct clk_fixed_factor - fixed multiplier and divider clock * From ece70094f6ab2107d4313fa1802b13dab0234ac5 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Wed, 20 Mar 2013 17:30:34 +0530 Subject: [PATCH 33/68] clk: Add composite clock type Not all clocks are required to be decomposed into basic clock types but at the same time want to use the functionality provided by these basic clock types instead of duplicating. For example, Tegra SoC has ~100 clocks which can be decomposed into Mux -> Div -> Gate clock types making the clock count to ~300. Also, parent change operation can not be performed on gate clock which forces to use mux clock in driver if want to change the parent. Instead aggregate the basic clock types functionality into one clock and just use this clock for all operations. This clock type re-uses the functionality of basic clock types and not limited to basic clock types but any hardware-specific implementation. Signed-off-by: Prashant Gaikwad Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 201 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 31 ++++++ 3 files changed, 233 insertions(+) create mode 100644 drivers/clk/clk-composite.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1c22f9dc721d..41cb123a2d02 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 000000000000..097dee4fd209 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->recalc_rate(div_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->round_rate(div_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->set_rate(div_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *div_hw, const struct clk_ops *div_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_init_data init; + struct clk_composite *composite; + struct clk_ops *clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) { + pr_err("%s: could not allocate composite clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk_composite_ops = &composite->ops; + + if (mux_hw && mux_ops) { + if (!mux_ops->get_parent || !mux_ops->set_parent) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + clk_composite_ops->get_parent = clk_composite_get_parent; + clk_composite_ops->set_parent = clk_composite_set_parent; + } + + if (div_hw && div_ops) { + if (!div_ops->recalc_rate || !div_ops->round_rate || + !div_ops->set_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + clk_composite_ops->round_rate = clk_composite_round_rate; + clk_composite_ops->set_rate = clk_composite_set_rate; + } + + if (gate_hw && gate_ops) { + if (!gate_ops->is_enabled || !gate_ops->enable || + !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; + clk_composite_ops->is_enabled = clk_composite_is_enabled; + clk_composite_ops->enable = clk_composite_enable; + clk_composite_ops->disable = clk_composite_disable; + } + + init.ops = clk_composite_ops; + composite->hw.init = &init; + + clk = clk_register(dev, &composite->hw); + if (IS_ERR(clk)) + goto err; + + if (composite->mux_hw) + composite->mux_hw->clk = clk; + + if (composite->div_hw) + composite->div_hw->clk = clk; + + if (composite->gate_hw) + composite->gate_hw->clk = clk; + + return clk; + +err: + kfree(composite); + return clk; +} diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 63ba3b740794..1f0352802794 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -342,6 +342,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); +/*** + * struct clk_composite - aggregate clock of mux, divider and gate clocks + * + * @hw: handle between common and hardware-specific interfaces + * @mux_hw: handle between composite and hardware-specifix mux clock + * @div_hw: handle between composite and hardware-specifix divider clock + * @gate_hw: handle between composite and hardware-specifix gate clock + * @mux_ops: clock ops for mux + * @div_ops: clock ops for divider + * @gate_ops: clock ops for gate + */ +struct clk_composite { + struct clk_hw hw; + struct clk_ops ops; + + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *div_hw, const struct clk_ops *div_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags); + /** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock From a35e89ad91c5379bef867eac1aff46a6187f7d74 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 26 Mar 2013 10:42:10 -0300 Subject: [PATCH 34/68] ARM: imx: adapt clk_busy_mux to new clk_mux struct Commit ce4f3313b05 (clk: add table lookup to mux) caused the following build error on imx_v4_v5_defconfig/imx_v6_v7_defconfig: arch/arm/mach-imx/clk-busy.c:172:11: error: 'struct clk_mux' has no member named 'width' Fix it by passing the 'mask' field. Signed-off-by: Fabio Estevam Acked-by: Peter De Schrijver Signed-off-by: Mike Turquette [mturquette@linaro.org: shortened $SUBJECT line] --- arch/arm/mach-imx/clk-busy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/clk-busy.c b/arch/arm/mach-imx/clk-busy.c index 1ab91b5209e6..85b728cc27ab 100644 --- a/arch/arm/mach-imx/clk-busy.c +++ b/arch/arm/mach-imx/clk-busy.c @@ -169,7 +169,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, busy->mux.reg = reg; busy->mux.shift = shift; - busy->mux.width = width; + busy->mux.mask = BIT(width) - 1; busy->mux.lock = &imx_ccm_lock; busy->mux_ops = &clk_mux_ops; From b54891685162e09c4292cd38f067ca118604353c Mon Sep 17 00:00:00 2001 From: Maxime Coquelin Date: Tue, 26 Mar 2013 15:27:15 +0100 Subject: [PATCH 35/68] clk: ux500: Fix prcmu clocks registration In clk_reg_prcmu(), clk->hw.init field is assigned with a reference local to clk_reg_prcmu() function. This patch replaces references to clk->hw.init with calls to __clk_get_name when called after clock registration. This patch applies on top of v3.9-rc4. Signed-off-by: Maxime Coquelin Acked-by: Ulf Hansson Signed-off-by: Mike Turquette [mturquette@linaro.org: resolved trivial merge issues] --- drivers/clk/ux500/clk-prcmu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 9d832567c6be..293a28854417 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -44,7 +44,7 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) struct clk_prcmu *clk = to_clk_prcmu(hw); if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); else clk->is_prepared = 0; } @@ -106,7 +106,7 @@ static int clk_prcmu_opp_prepare(struct clk_hw *hw) 100); if (err) { pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", - __func__, hw->init->name); + __func__, __clk_get_name(hw->clk)); return err; } clk->opp_requested = 1; @@ -130,7 +130,7 @@ static void clk_prcmu_opp_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); return; } @@ -152,7 +152,7 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) err = prcmu_request_ape_opp_100_voltage(true); if (err) { pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", - __func__, hw->init->name); + __func__, __clk_get_name(hw->clk)); return err; } clk->opp_requested = 1; @@ -175,7 +175,7 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); return; } From e874a6697710f52fa8ab29487a99034d5d96fdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 25 Feb 2013 11:44:26 -0300 Subject: [PATCH 36/68] clk: arm: sunxi: Add a new clock driver for sunxi SOCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements the base CPU clocks for sunxi devices. It has been tested using a slightly modified cpufreq driver from the linux-sunxi 3.0 tree. Additionally, document the new bindings introduced by this patch. Idling: / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 60000000 cpu 0 0 60000000 axi 0 0 60000000 ahb 0 0 60000000 apb0 0 0 30000000 dummy 0 0 0 After "yes >/dev/null &": / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 1008000000 cpu 0 0 1008000000 axi 0 0 336000000 ahb 0 0 168000000 apb0 0 0 84000000 dummy 0 0 0 Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/sunxi.txt | 44 +++ drivers/clk/Makefile | 1 + drivers/clk/sunxi/Makefile | 5 + drivers/clk/sunxi/clk-factors.c | 180 +++++++++ drivers/clk/sunxi/clk-factors.h | 27 ++ drivers/clk/sunxi/clk-sunxi.c | 362 ++++++++++++++++++ drivers/clocksource/sunxi_timer.c | 4 +- include/linux/clk/sunxi.h | 22 ++ 8 files changed, 643 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt create mode 100644 drivers/clk/sunxi/Makefile create mode 100644 drivers/clk/sunxi/clk-factors.c create mode 100644 drivers/clk/sunxi/clk-factors.h create mode 100644 drivers/clk/sunxi/clk-sunxi.c create mode 100644 include/linux/clk/sunxi.h diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt new file mode 100644 index 000000000000..b23cfbdbcd6d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -0,0 +1,44 @@ +Device Tree Clock bindings for arch-sunxi + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of the following: + "allwinner,sunxi-osc-clk" - for a gatable oscillator + "allwinner,sunxi-pll1-clk" - for the main PLL clock + "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock + "allwinner,sunxi-axi-clk" - for the sunxi AXI clock + "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock + "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock + "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock + "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + +Required properties for all clocks: +- reg : shall be the control register address for the clock. +- clocks : shall be the input parent clock(s) phandle for the clock +- #clock-cells : from common clock binding; shall be set to 0. + +For example: + +osc24M: osc24M@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-osc-clk"; + reg = <0x01c20050 0x4>; + clocks = <&osc24M_fixed>; +}; + +pll1: pll1@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; +}; + +cpu: cpu@01c20054 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>; +}; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 41cb123a2d02..79e98e416724 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 000000000000..b5bac917612c --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 000000000000..88523f91d9b7 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Adjustable factor-based clock implementation + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_factors { + struct clk_hw hw; + void __iomem *reg; + struct clk_factors_config *config; + void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#define CLRMASK(len, pos) (~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ + (((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 n = 1, k = 0, p = 0, m = 0; + u32 reg; + unsigned long rate; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Get each individual factor if applicable */ + if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) + n = FACTOR_GET(config->nshift, config->nwidth, reg); + if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) + k = FACTOR_GET(config->kshift, config->kwidth, reg); + if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) + m = FACTOR_GET(config->mshift, config->mwidth, reg); + if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) + p = FACTOR_GET(config->pshift, config->pwidth, reg); + + /* Calculate the rate */ + rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + + return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_factors *factors = to_clk_factors(hw); + factors->get_factors((u32 *)&rate, (u32)*parent_rate, + NULL, NULL, NULL, NULL); + + return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u8 n, k, m, p; + u32 reg; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + + factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Set up the new factors - macros do not do anything if width is 0 */ + reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + + /* Apply them now */ + writel(reg, factors->reg); + + /* delay 500us so pll stabilizes */ + __delay((rate >> 20) * 500 / 2); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + +static const struct clk_ops clk_factors_ops = { + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors)(u32 *rate, u32 parent, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock) +{ + struct clk_factors *factors; + struct clk *clk; + struct clk_init_data init; + + /* allocate the factors */ + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) { + pr_err("%s: could not allocate factors clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_factors_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_factors assignments */ + factors->reg = reg; + factors->config = config; + factors->lock = lock; + factors->hw.init = &init; + factors->get_factors = get_factors; + + /* register the clock */ + clk = clk_register(dev, &factors->hw); + + if (IS_ERR(clk)) + kfree(factors); + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 000000000000..f49851cc4380 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include +#include + +#define SUNXI_FACTORS_NOT_APPLICABLE (0) + +struct clk_factors_config { + u8 nshift; + u8 nwidth; + u8 kshift; + u8 kwidth; + u8 mshift; + u8 mwidth; + u8 pshift; + u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors) (u32 *rate, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 000000000000..d4ad1c22859e --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,362 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE 0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, SUNXI_OSC24M_GATE, 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* Normalize value to a 6M multiple */ + div = *freq / 6000000; + *freq = 6000000 * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 3 for divs under 10 */ + if (div < 10) + *p = 3; + + /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ + else if (div < 20 || (div < 32 && (div & 1))) + *p = 2; + + /* p will be 1 for even divs under 32, divs under 40 and odd pairs + * of divs between 40-62 */ + else if (div < 40 || (div < 64 && (div & 2))) + *p = 1; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 calcm, calcp; + + if (parent_rate < *freq) + *freq = parent_rate; + + parent_rate = (parent_rate + (*freq - 1)) / *freq; + + /* Invalid rate! */ + if (parent_rate > 32) + return; + + if (parent_rate <= 4) + calcp = 0; + else if (parent_rate <= 8) + calcp = 1; + else if (parent_rate <= 16) + calcp = 2; + else + calcp = 3; + + calcm = (parent_rate >> calcp) - 1; + + *freq = (parent_rate >> calcp) / (calcm + 1); + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm; + *p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { + .mshift = 0, + .mwidth = 5, + .pshift = 16, + .pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { + .table = &pll1_config, + .getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { + .table = &apb1_config, + .getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, + struct factors_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, data->table, data->getter, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH 2 + +struct mux_data { + u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { + .shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { + .shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); + void *reg; + int i = 0; + + reg = of_iomap(node, 0); + + while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, + 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH 2 + +struct div_data { + u8 shift; + u8 pow; +}; + +static const __initconst struct div_data axi_data = { + .shift = 0, + .pow = 0, +}; + +static const __initconst struct div_data ahb_data = { + .shift = 4, + .pow = 1, +}; + +static const __initconst struct div_data apb0_data = { + .shift = 8, + .pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, + struct div_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parent; + void *reg; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, SUNXI_DIVISOR_WIDTH, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, + &clk_lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { + {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, + {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { + {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { + {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { + {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, + void *function) +{ + struct device_node *np; + const struct div_data *data; + const struct of_device_id *match; + void (*setup_function)(struct device_node *, const void *) = function; + + for_each_matching_node(np, clk_match) { + match = of_match_node(clk_match, np); + data = match->data; + setup_function(np, data); + } +} + +void __init sunxi_init_clocks(void) +{ + /* Register all the simple sunxi clocks on DT */ + of_clk_init(clk_match); + + /* Register factor clocks */ + of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + + /* Register divider clocks */ + of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + + /* Register mux clocks */ + of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); +} diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b9167159..0ce85e29769b 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define TIMER_CTL_REG 0x00 #define TIMER_CTL_ENABLE (1 << 0) @@ -123,7 +123,7 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - of_clk_init(NULL); + sunxi_init_clocks(); clk = of_clk_get(node, 0); if (IS_ERR(clk)) diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..e074fdd5a236 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +void __init sunxi_init_clocks(void); + +#endif From 8eb896ab7a716308698de77073a9265f584b12fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 25 Feb 2013 11:44:28 -0300 Subject: [PATCH 37/68] arm: sunxi: Add useful information about sunxi clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch contains useful bits of information about the sunxi clocks that may help and/or be interesting for current and future developers. Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/arm/sunxi/clocks.txt diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt new file mode 100644 index 000000000000..e09a88aa3136 --- /dev/null +++ b/Documentation/arm/sunxi/clocks.txt @@ -0,0 +1,56 @@ +Frequently asked questions about the sunxi clock system +======================================================= + +This document contains useful bits of information that people tend to ask +about the sunxi clock system, as well as accompanying ASCII art when adequate. + +Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the + system? + +A: The 24MHz oscillator allows gating to save power. Indeed, if gated + carelessly the system would stop functioning, but with the right + steps, one can gate it and keep the system running. Consider this + simplified suspend example: + + While the system is operational, you would see something like + + 24MHz 32kHz + | + PLL1 + \ + \_ CPU Mux + | + [CPU] + + When you are about to suspend, you switch the CPU Mux to the 32kHz + oscillator: + + 24Mhz 32kHz + | | + PLL1 | + / + CPU Mux _/ + | + [CPU] + + Finally you can gate the main oscillator + + 32kHz + | + | + / + CPU Mux _/ + | + [CPU] + +Q: Were can I learn more about the sunxi clocks? + +A: The linux-sunxi wiki contains a page documenting the clock registers, + you can find it at + + http://linux-sunxi.org/A10/CCM + + The authoritative source for information at this time is the ccmu driver + released by Allwinner, you can find it at + + https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu From e3276998da12c6ec093befd6e49be4848414d57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Tue, 26 Mar 2013 23:39:17 -0300 Subject: [PATCH 38/68] clk: sunxi: rename compatible strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the introduction of the Allwinner SoC platforms, sunxi was initially meant as a generic name for all the variants of the Allwinner SoC. It was ok at the time of the support of only the A10 and A13 that look pretty much the same; but it's beginning to be troublesome with the future addition of the Allwinner A31 (sun6i) that is quite different, and would introduce some weird logic, where sunxi would actually mean in some case sun4i and sun5i but without sun6i... Moreover, it makes the compatible strings naming scheme not consistent with other architectures, where usually for this kind of compability, we just use the oldest SoC name that has this IP, so let's do just this. Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/sunxi.txt | 22 +++++++++---------- drivers/clk/sunxi/clk-sunxi.c | 16 +++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index b23cfbdbcd6d..20b8479c2760 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -6,14 +6,14 @@ This binding uses the common clock binding[1]. Required properties: - compatible : shall be one of the following: - "allwinner,sunxi-osc-clk" - for a gatable oscillator - "allwinner,sunxi-pll1-clk" - for the main PLL clock - "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock - "allwinner,sunxi-axi-clk" - for the sunxi AXI clock - "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock - "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock - "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock - "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + "allwinner,sun4i-osc-clk" - for a gatable oscillator + "allwinner,sun4i-pll1-clk" - for the main PLL clock + "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-ahb-clk" - for the AHB clock + "allwinner,sun4i-apb0-clk" - for the APB0 clock + "allwinner,sun4i-apb1-clk" - for the APB1 clock + "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing Required properties for all clocks: - reg : shall be the control register address for the clock. @@ -24,21 +24,21 @@ For example: osc24M: osc24M@01c20050 { #clock-cells = <0>; - compatible = "allwinner,sunxi-osc-clk"; + compatible = "allwinner,sun4i-osc-clk"; reg = <0x01c20050 0x4>; clocks = <&osc24M_fixed>; }; pll1: pll1@01c20000 { #clock-cells = <0>; - compatible = "allwinner,sunxi-pll1-clk"; + compatible = "allwinner,sun4i-pll1-clk"; reg = <0x01c20000 0x4>; clocks = <&osc24M>; }; cpu: cpu@01c20054 { #clock-cells = <0>; - compatible = "allwinner,sunxi-cpu-clk"; + compatible = "allwinner,sun4i-cpu-clk"; reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d4ad1c22859e..d528a2496690 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -305,29 +305,29 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, /* Matches for of_clk_init */ static const __initconst struct of_device_id clk_match[] = { {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, - {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, {} }; /* Matches for factors clocks */ static const __initconst struct of_device_id clk_factors_match[] = { - {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, - {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, {} }; /* Matches for divider clocks */ static const __initconst struct of_device_id clk_div_match[] = { - {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, - {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, - {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, {} }; /* Matches for mux clocks */ static const __initconst struct of_device_id clk_mux_match[] = { - {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, - {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, {} }; From 43c4120c0656692d08f5c005881cc0c4573ce3b5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 27 Mar 2013 15:45:06 +0100 Subject: [PATCH 39/68] clk: zynq: Add missing zynq clk header Include zynq clk header where init function is declared. It removes this sparse warning: drivers/clk/clk-zynq.c:373:13: warning: symbol 'xilinx_zynq_clocks_init' was not declared. Should it be static? Signed-off-by: Michal Simek Signed-off-by: Mike Turquette --- drivers/clk/clk-zynq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index b14a25f39255..32062977f453 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -20,6 +20,7 @@ #include #include #include +#include static void __iomem *slcr_base; From eab89f690ee0805c02017d7959f4f930379a8c46 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Thu, 28 Mar 2013 13:59:01 -0700 Subject: [PATCH 40/68] clk: abstract locking out into helper functions Create locking helpers for the global mutex and global spinlock. The definitions of these helpers will be expanded upon in the next patch which introduces reentrancy into the locking scheme. Signed-off-by: Mike Turquette Cc: Rajagopal Venkat Cc: David Brown Tested-by: Laurent Pinchart Reviewed-by: Thomas Gleixner Reviewed-by: Ulf Hansson --- drivers/clk/clk.c | 99 +++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5e8ffff99362..0b5d61201ca1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -27,6 +27,29 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); +/*** locking ***/ +static void clk_prepare_lock(void) +{ + mutex_lock(&prepare_lock); +} + +static void clk_prepare_unlock(void) +{ + mutex_unlock(&prepare_lock); +} + +static unsigned long clk_enable_lock(void) +{ + unsigned long flags; + spin_lock_irqsave(&enable_lock, flags); + return flags; +} + +static void clk_enable_unlock(unsigned long flags) +{ + spin_unlock_irqrestore(&enable_lock, flags); +} + /*** debugfs support ***/ #ifdef CONFIG_COMMON_CLK_DEBUG @@ -69,7 +92,7 @@ static int clk_summary_show(struct seq_file *s, void *data) seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, "---------------------------------------------------------------------\n"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) clk_summary_show_subtree(s, c, 0); @@ -77,7 +100,7 @@ static int clk_summary_show(struct seq_file *s, void *data) hlist_for_each_entry(c, &clk_orphan_list, child_node) clk_summary_show_subtree(s, c, 0); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -130,7 +153,7 @@ static int clk_dump(struct seq_file *s, void *data) seq_printf(s, "{"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) { if (!first_node) @@ -144,7 +167,7 @@ static int clk_dump(struct seq_file *s, void *data) clk_dump_subtree(s, c, 0); } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); seq_printf(s, "}"); return 0; @@ -316,7 +339,7 @@ static int __init clk_debug_init(void) if (!orphandir) return -ENOMEM; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_debug_create_subtree(clk, rootdir); @@ -326,7 +349,7 @@ static int __init clk_debug_init(void) inited = 1; - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -372,7 +395,7 @@ static void clk_disable_unused_subtree(struct clk *clk) hlist_for_each_entry(child, &clk->children, child_node) clk_disable_unused_subtree(child); - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) goto unlock_out; @@ -393,7 +416,7 @@ static void clk_disable_unused_subtree(struct clk *clk) } unlock_out: - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); out: return; @@ -403,7 +426,7 @@ static int clk_disable_unused(void) { struct clk *clk; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_disable_unused_subtree(clk); @@ -417,7 +440,7 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_unprepare_unused_subtree(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -600,9 +623,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { - mutex_lock(&prepare_lock); + clk_prepare_lock(); __clk_unprepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unprepare); @@ -648,9 +671,9 @@ int clk_prepare(struct clk *clk) { int ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_prepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -692,9 +715,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); __clk_disable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -745,9 +768,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); ret = __clk_enable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); return ret; } @@ -792,9 +815,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_round_rate(clk, rate); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -889,13 +912,13 @@ unsigned long clk_get_rate(struct clk *clk) { unsigned long rate; - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) __clk_recalc_rates(clk, 0); rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return rate; } @@ -1100,7 +1123,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = 0; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* bail early if nothing to do */ if (rate == clk->rate) @@ -1132,7 +1155,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_change_rate(top); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1148,9 +1171,9 @@ struct clk *clk_get_parent(struct clk *clk) { struct clk *parent; - mutex_lock(&prepare_lock); + clk_prepare_lock(); parent = __clk_get_parent(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return parent; } @@ -1294,19 +1317,19 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) __clk_prepare(parent); /* FIXME replace with clk_is_enabled(clk) someday */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_enable(parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); /* change clock input source */ ret = clk->ops->set_parent(clk->hw, i); /* clean up old prepare and enable */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_disable(old_parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); if (clk->prepare_count) __clk_unprepare(old_parent); @@ -1338,7 +1361,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return -ENOSYS; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk->parent == parent) goto out; @@ -1367,7 +1390,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) __clk_reparent(clk, parent); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1390,7 +1413,7 @@ int __clk_init(struct device *dev, struct clk *clk) if (!clk) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* check to see if a clock with this name is already registered */ if (__clk_lookup(clk->name)) { @@ -1514,7 +1537,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1748,7 +1771,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) @@ -1772,7 +1795,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) clk->notifier_count++; out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1797,7 +1820,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) @@ -1818,7 +1841,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) ret = -ENOENT; } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } From 533ddeb1e86f506129ee388a6cc13796dcf31311 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Thu, 28 Mar 2013 13:59:02 -0700 Subject: [PATCH 41/68] clk: allow reentrant calls into the clk framework Reentrancy into the clock framework is necessary for clock operations that result in nested calls to the clk api. A common example is a clock that is prepared via an i2c transaction, such as a clock inside of a discrete audio chip or a power management IC. The i2c subsystem itself will use the clk api resulting in a deadlock: clk_prepare(audio_clk) i2c_transfer(..) clk_prepare(i2c_controller_clk) The ability to reenter the clock framework prevents this deadlock. Other use cases exist such as allowing .set_rate callbacks to call clk_set_parent to achieve the best rate, or to save power in certain configurations. Yet another example is performing pinctrl operations from a clk_ops callback. Calls into the pinctrl subsystem may call clk_{un}prepare on an unrelated clock. Allowing for nested calls to reenter the clock framework enables both of these use cases. Reentrancy is implemented by two global pointers that track the owner currently holding a global lock. One pointer tracks the owner during sleepable, mutex-protected operations and the other one tracks the owner during non-interruptible, spinlock-protected operations. When the clk framework is entered we try to hold the global lock. If it is held we compare the current task against the current owner; a match implies a nested call and we reenter. If the values do not match then we block on the lock until it is released. Signed-off-by: Mike Turquette Cc: Rajagopal Venkat Cc: David Brown Tested-by: Laurent Pinchart Reviewed-by: Thomas Gleixner Reviewed-by: Ulf Hansson --- drivers/clk/clk.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0b5d61201ca1..0230c9d95975 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,10 +19,17 @@ #include #include #include +#include static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; + static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); @@ -30,23 +37,56 @@ static LIST_HEAD(clk_notifier_list); /*** locking ***/ static void clk_prepare_lock(void) { - mutex_lock(&prepare_lock); + if (!mutex_trylock(&prepare_lock)) { + if (prepare_owner == current) { + prepare_refcnt++; + return; + } + mutex_lock(&prepare_lock); + } + WARN_ON_ONCE(prepare_owner != NULL); + WARN_ON_ONCE(prepare_refcnt != 0); + prepare_owner = current; + prepare_refcnt = 1; } static void clk_prepare_unlock(void) { + WARN_ON_ONCE(prepare_owner != current); + WARN_ON_ONCE(prepare_refcnt == 0); + + if (--prepare_refcnt) + return; + prepare_owner = NULL; mutex_unlock(&prepare_lock); } static unsigned long clk_enable_lock(void) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + + if (!spin_trylock_irqsave(&enable_lock, flags)) { + if (enable_owner == current) { + enable_refcnt++; + return flags; + } + spin_lock_irqsave(&enable_lock, flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; return flags; } static void clk_enable_unlock(unsigned long flags) { + WARN_ON_ONCE(enable_owner != current); + WARN_ON_ONCE(enable_refcnt == 0); + + if (--enable_refcnt) + return; + enable_owner = NULL; spin_unlock_irqrestore(&enable_lock, flags); } From 7021d1220584ab1e6efd3d59da47b65674d9896a Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:27 +0800 Subject: [PATCH 42/68] ARM: tegra: add clock source of PMC to device trees Adding the bindings of the clock source of PMC in DT. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 29 ++++++++++++++++++- arch/arm/boot/dts/tegra114-dalmore.dts | 13 +++++++++ arch/arm/boot/dts/tegra114-pluto.dts | 13 +++++++++ arch/arm/boot/dts/tegra114.dtsi | 2 ++ arch/arm/boot/dts/tegra20-colibri-512.dtsi | 13 +++++++++ arch/arm/boot/dts/tegra20-harmony.dts | 13 +++++++++ arch/arm/boot/dts/tegra20-paz00.dts | 13 +++++++++ arch/arm/boot/dts/tegra20-seaboard.dts | 13 +++++++++ arch/arm/boot/dts/tegra20-tamonten.dtsi | 13 +++++++++ arch/arm/boot/dts/tegra20-trimslice.dts | 13 +++++++++ arch/arm/boot/dts/tegra20-ventana.dts | 13 +++++++++ arch/arm/boot/dts/tegra20-whistler.dts | 13 +++++++++ arch/arm/boot/dts/tegra20.dtsi | 2 ++ arch/arm/boot/dts/tegra30-beaver.dts | 13 +++++++++ arch/arm/boot/dts/tegra30-cardhu.dtsi | 13 +++++++++ arch/arm/boot/dts/tegra30.dtsi | 2 ++ 16 files changed, 190 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt index b5846e21cc2e..ee529b17cb9f 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -1,9 +1,15 @@ NVIDIA Tegra Power Management Controller (PMC) -Properties: +Required properties: - name : Should be pmc - compatible : Should contain "nvidia,tegra-pmc". - reg : Offset and length of the register set for the device +- clocks : Must contain an entry for each entry in clock-names. +- clock-names : Must include the following entries: + "pclk" (The Tegra clock of that name), + "clk32k_in" (The 32KHz clock input to Tegra). + +Optional properties: - nvidia,invert-interrupt : If present, inverts the PMU interrupt signal. The PMU is an external Power Management Unit, whose interrupt output signal is fed into the PMC. This signal is optionally inverted, and then @@ -12,8 +18,29 @@ Properties: Example: +/ SoC dts including file pmc@7000f400 { compatible = "nvidia,tegra20-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 110>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; nvidia,invert-interrupt; }; + +/ Tegra board dts file +{ + ... + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + ... +}; diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts index a30aca62658a..6ebc1b704190 100644 --- a/arch/arm/boot/dts/tegra114-dalmore.dts +++ b/arch/arm/boot/dts/tegra114-dalmore.dts @@ -18,4 +18,17 @@ pmc { nvidia,invert-interrupt; }; + + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; }; diff --git a/arch/arm/boot/dts/tegra114-pluto.dts b/arch/arm/boot/dts/tegra114-pluto.dts index 9bea8f57aa47..5deb8692b350 100644 --- a/arch/arm/boot/dts/tegra114-pluto.dts +++ b/arch/arm/boot/dts/tegra114-pluto.dts @@ -18,4 +18,17 @@ pmc { nvidia,invert-interrupt; }; + + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; }; diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index e4ddeddcd437..c0b527d15fda 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -101,6 +101,8 @@ pmc { compatible = "nvidia,tegra114-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 261>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; iommu { diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi index cb73e62d61a9..4e3afdef28a8 100644 --- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi +++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi @@ -447,6 +447,19 @@ cd-gpios = <&gpio 23 1>; /* gpio PC7 */ }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + sound { compatible = "nvidia,tegra-audio-wm9712-colibri_t20", "nvidia,tegra-audio-wm9712"; diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index 1f79c0debb05..ae9d5a20834e 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -451,6 +451,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + kbc { status = "okay"; nvidia,debounce-delay-ms = <2>; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index 9db36da8e023..fd60940e4063 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -447,6 +447,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + gpio-keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 715a8b8dd9cd..4ee700a33ca5 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -595,6 +595,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + gpio-keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi index 6e9d91fc6195..c19025725918 100644 --- a/arch/arm/boot/dts/tegra20-tamonten.dtsi +++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi @@ -471,6 +471,19 @@ status = "okay"; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index 98f3e44f2a51..a9f3f06580f5 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -330,6 +330,19 @@ bus-width = <4>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + poweroff { compatible = "gpio-poweroff"; gpios = <&gpio 191 1>; /* gpio PX7, active low */ diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index 4aef56f2d96a..f544806e9618 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -531,6 +531,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index 5762188c60ad..258cf945f515 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -520,6 +520,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + kbc { status = "okay"; nvidia,debounce-delay-ms = <20>; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 37701d8727d8..8adaa3576c35 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -417,6 +417,8 @@ pmc { compatible = "nvidia,tegra20-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 110>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; memory-controller@7000f000 { diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts index 0a2cd24df853..6248b2445b32 100644 --- a/arch/arm/boot/dts/tegra30-beaver.dts +++ b/arch/arm/boot/dts/tegra30-beaver.dts @@ -268,6 +268,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi index 3e2d21018a5b..65bf2b63174e 100644 --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi @@ -322,6 +322,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index d376959b7731..94013404808a 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -426,6 +426,8 @@ pmc { compatible = "nvidia,tegra30-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 218>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; memory-controller { From 0337c3e0c3cbbb3a4f411c292f52fcc314abae67 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:28 +0800 Subject: [PATCH 43/68] ARM: tegra: moving the CPU power timer function to PMC driver The CPU power timer set up function was related to PMC register. Now moving it to PMC driver. And it also help to clean up the PM related code later. The timer was calculated based on the input clock of PMC. In this patch, we also get the clock from DT. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/common.c | 2 +- arch/arm/mach-tegra/pm.c | 37 +----------------------------------- arch/arm/mach-tegra/pmc.c | 35 ++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/pmc.h | 4 ++++ 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index f0315c95c76d..b02ebe767e7f 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -61,6 +61,7 @@ u32 tegra_uart_config[4] = { void __init tegra_dt_init_irq(void) { tegra_clocks_init(); + tegra_pmc_init(); tegra_init_irq(); irqchip_init(); } @@ -100,7 +101,6 @@ void __init tegra_init_early(void) tegra_apb_io_init(); tegra_init_fuse(); tegra_init_cache(); - tegra_pmc_init(); tegra_powergate_init(); tegra_hotplug_init(); } diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 0494f739c95f..5f5611f40b43 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -37,52 +36,18 @@ #include "reset.h" #include "flowctrl.h" #include "fuse.h" +#include "pmc.h" #include "sleep.h" #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ #define PMC_CTRL 0x0 -#define PMC_CPUPWRGOOD_TIMER 0xc8 -#define PMC_CPUPWROFF_TIMER 0xcc #ifdef CONFIG_PM_SLEEP static DEFINE_SPINLOCK(tegra_lp2_lock); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); -static struct clk *tegra_pclk; void (*tegra_tear_down_cpu)(void); -static void set_power_timers(unsigned long us_on, unsigned long us_off) -{ - unsigned long long ticks; - unsigned long long pclk; - unsigned long rate; - static unsigned long tegra_last_pclk; - - if (tegra_pclk == NULL) { - tegra_pclk = clk_get_sys(NULL, "pclk"); - WARN_ON(IS_ERR(tegra_pclk)); - } - - rate = clk_get_rate(tegra_pclk); - - if (WARN_ON_ONCE(rate <= 0)) - pclk = 100000000; - else - pclk = rate; - - if ((rate != tegra_last_pclk)) { - ticks = (us_on * pclk) + 999999ull; - do_div(ticks, 1000000); - writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER); - - ticks = (us_off * pclk) + 999999ull; - do_div(ticks, 1000000); - writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER); - wmb(); - } - tegra_last_pclk = pclk; -} - /* * restore_cpu_complex * diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index b30e921cc3a9..faa33e8f937d 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,9 @@ #define PMC_REMOVE_CLAMPING 0x34 #define PMC_PWRGATE_STATUS 0x38 +#define PMC_CPUPWRGOOD_TIMER 0xc8 +#define PMC_CPUPWROFF_TIMER 0xcc + #define TEGRA_POWERGATE_PCIE 3 #define TEGRA_POWERGATE_VDEC 4 #define TEGRA_POWERGATE_CPU1 9 @@ -43,6 +47,7 @@ static DEFINE_SPINLOCK(tegra_powergate_lock); static void __iomem *tegra_pmc_base; static bool tegra_pmc_invert_interrupt; +static struct clk *tegra_pclk; static inline u32 tegra_pmc_readl(u32 reg) { @@ -133,6 +138,34 @@ int tegra_pmc_cpu_remove_clamping(int cpuid) return tegra_pmc_powergate_remove_clamping(id); } +#ifdef CONFIG_PM_SLEEP +void set_power_timers(unsigned long us_on, unsigned long us_off) +{ + unsigned long long ticks; + unsigned long long pclk; + unsigned long rate; + static unsigned long tegra_last_pclk; + + rate = clk_get_rate(tegra_pclk); + if (WARN_ON_ONCE(rate <= 0)) + pclk = 100000000; + else + pclk = rate; + + if ((rate != tegra_last_pclk)) { + ticks = (us_on * pclk) + 999999ull; + do_div(ticks, 1000000); + tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER); + + ticks = (us_off * pclk) + 999999ull; + do_div(ticks, 1000000); + tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER); + wmb(); + } + tegra_last_pclk = pclk; +} +#endif + static const struct of_device_id matches[] __initconst = { { .compatible = "nvidia,tegra114-pmc" }, { .compatible = "nvidia,tegra30-pmc" }, @@ -151,6 +184,8 @@ static void tegra_pmc_parse_dt(void) tegra_pmc_invert_interrupt = of_property_read_bool(np, "nvidia,invert-interrupt"); + tegra_pclk = of_clk_get_by_name(np, "pclk"); + WARN_ON(IS_ERR(tegra_pclk)); } void __init tegra_pmc_init(void) diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 7d44710368be..22f16c9dd44d 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,10 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +#ifdef CONFIG_PM_SLEEP +void set_power_timers(unsigned long us_on, unsigned long us_off); +#endif + bool tegra_pmc_cpu_is_powered(int cpuid); int tegra_pmc_cpu_power_on(int cpuid); int tegra_pmc_cpu_remove_clamping(int cpuid); From 203f31cb86eb6aa4c49711e7ca25a7660efc39b8 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:44 +0800 Subject: [PATCH 44/68] gpio: tegra: add gpio wakeup source handling This patch add the gpio wakeup source handling for the Tegra platform. It was be done by enabling the irq for the gpio in the gpio controller and enabling the bank irq of the gpio in the Tegra legacy irq controller when the system going to suspend. Based on the work by: Varun Wadekar Cc: Grant Likely Cc: Linus Walleij Signed-off-by: Joseph Lo Acked-by: Linus Walleij Signed-off-by: Stephen Warren --- drivers/gpio/gpio-tegra.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 414ad912232f..e3956359202c 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -72,6 +72,7 @@ struct tegra_gpio_bank { u32 oe[4]; u32 int_enb[4]; u32 int_lvl[4]; + u32 wake_enb[4]; #endif }; @@ -333,15 +334,31 @@ static int tegra_gpio_suspend(struct device *dev) bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio)); bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio)); bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio)); + + /* Enable gpio irq for wake up source */ + tegra_gpio_writel(bank->wake_enb[p], + GPIO_INT_ENB(gpio)); } } local_irq_restore(flags); return 0; } -static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable) +static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) { struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int gpio = d->hwirq; + u32 port, bit, mask; + + port = GPIO_PORT(gpio); + bit = GPIO_BIT(gpio); + mask = BIT(bit); + + if (enable) + bank->wake_enb[port] |= mask; + else + bank->wake_enb[port] &= ~mask; + return irq_set_irq_wake(bank->irq, enable); } #endif @@ -353,7 +370,7 @@ static struct irq_chip tegra_gpio_irq_chip = { .irq_unmask = tegra_gpio_irq_unmask, .irq_set_type = tegra_gpio_irq_set_type, #ifdef CONFIG_PM_SLEEP - .irq_set_wake = tegra_gpio_wake_enable, + .irq_set_wake = tegra_gpio_irq_set_wake, #endif }; From e307cc8941fc420f008e1f3cb86e16a4269aa2af Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:45 +0800 Subject: [PATCH 45/68] ARM: tegra: irq: add wake up handling Add the wake up handling for legacy irq controller, and using IRQCHIP_MASK_ON_SUSPEND for wake irq handling. Based on the work by: Varun Wadekar Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/common.c | 2 + arch/arm/mach-tegra/irq.c | 96 +++++++++++++++++++++++++++++++++++- arch/arm/mach-tegra/irq.h | 6 +++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index b02ebe767e7f..c84505c1f644 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -33,6 +33,7 @@ #include "common.h" #include "fuse.h" #include "iomap.h" +#include "irq.h" #include "pmc.h" #include "apbio.h" #include "sleep.h" @@ -64,6 +65,7 @@ void __init tegra_dt_init_irq(void) tegra_pmc_init(); tegra_init_irq(); irqchip_init(); + tegra_legacy_irq_syscore_init(); } #endif diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 1952e82797cc..0de4eed1493d 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -4,7 +4,7 @@ * Author: * Colin Cross * - * Copyright (C) 2010, NVIDIA Corporation + * Copyright (C) 2010,2013, NVIDIA Corporation * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,6 +23,7 @@ #include #include #include +#include #include "board.h" #include "iomap.h" @@ -43,6 +44,7 @@ #define ICTLR_COP_IEP_CLASS 0x3c #define FIRST_LEGACY_IRQ 32 +#define TEGRA_MAX_NUM_ICTLRS 5 #define SGI_MASK 0xFFFF @@ -56,6 +58,15 @@ static void __iomem *ictlr_reg_base[] = { IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), }; +#ifdef CONFIG_PM_SLEEP +static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; +static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; +static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; +static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; + +static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; +#endif + bool tegra_pending_sgi(void) { u32 pending_set; @@ -125,6 +136,87 @@ static int tegra_retrigger(struct irq_data *d) return 1; } +#ifdef CONFIG_PM_SLEEP +static int tegra_set_wake(struct irq_data *d, unsigned int enable) +{ + u32 irq = d->irq; + u32 index, mask; + + if (irq < FIRST_LEGACY_IRQ || + irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32) + return -EINVAL; + + index = ((irq - FIRST_LEGACY_IRQ) / 32); + mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); + if (enable) + ictlr_wake_mask[index] |= mask; + else + ictlr_wake_mask[index] &= ~mask; + + return 0; +} + +static int tegra_legacy_irq_suspend(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = ictlr_reg_base[i]; + /* Save interrupt state */ + cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); + cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); + cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); + cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); + + /* Disable COP interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + + /* Disable CPU interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + + /* Enable the wakeup sources of ictlr */ + writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); + } + local_irq_restore(flags); + + return 0; +} + +static void tegra_legacy_irq_resume(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = ictlr_reg_base[i]; + writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); + writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); +} + +static struct syscore_ops tegra_legacy_irq_syscore_ops = { + .suspend = tegra_legacy_irq_suspend, + .resume = tegra_legacy_irq_resume, +}; + +int tegra_legacy_irq_syscore_init(void) +{ + register_syscore_ops(&tegra_legacy_irq_syscore_ops); + + return 0; +} +#else +#define tegra_set_wake NULL +#endif + void __init tegra_init_irq(void) { int i; @@ -150,6 +242,8 @@ void __init tegra_init_irq(void) gic_arch_extn.irq_mask = tegra_mask; gic_arch_extn.irq_unmask = tegra_unmask; gic_arch_extn.irq_retrigger = tegra_retrigger; + gic_arch_extn.irq_set_wake = tegra_set_wake; + gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; /* * Check if there is a devicetree present, since the GIC will be diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h index 5142649bba05..bc05ce5613fb 100644 --- a/arch/arm/mach-tegra/irq.h +++ b/arch/arm/mach-tegra/irq.h @@ -19,4 +19,10 @@ bool tegra_pending_sgi(void); +#ifdef CONFIG_PM_SLEEP +int tegra_legacy_irq_syscore_init(void); +#else +static inline int tegra_legacy_irq_syscore_init(void) { return 0; } +#endif + #endif From 4b51ccbc469facb7b589a71c2a4ae47d3e425d02 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:46 +0800 Subject: [PATCH 46/68] ARM: dt: tegra: add bindings of power management configurations for PMC The PMC mostly controls the entry and exit of the system from different sleep modes. Different platform or system may have different configurations. The power management configurations of PMC is represented as some properties. The system needs to define the properties when the system supports deep sleep mode (i.e. suspend). Cc: Grant Likely Cc: Rob Herring Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 38 +++++++++ arch/arm/mach-tegra/pmc.c | 83 +++++++++++++++++++ arch/arm/mach-tegra/pmc.h | 8 ++ 3 files changed, 129 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt index ee529b17cb9f..1608a54e90e1 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -1,5 +1,9 @@ NVIDIA Tegra Power Management Controller (PMC) +The PMC block interacts with an external Power Management Unit. The PMC +mostly controls the entry and exit of the system from different sleep +modes. It provides power-gating controllers for SoC and CPU power-islands. + Required properties: - name : Should be pmc - compatible : Should contain "nvidia,tegra-pmc". @@ -15,6 +19,32 @@ Optional properties: signal is fed into the PMC. This signal is optionally inverted, and then fed into the ARM GIC. The PMC is not involved in the detection or handling of this interrupt signal, merely its inversion. +- nvidia,suspend-mode : The suspend mode that the platform should use. + Valid values are 0, 1 and 2: + 0 (LP0): CPU + Core voltage off and DRAM in self-refresh + 1 (LP1): CPU voltage off and DRAM in self-refresh + 2 (LP2): CPU voltage off +- nvidia,core-power-req-active-high : Boolean, core power request active-high +- nvidia,sys-clock-req-active-high : Boolean, system clock request active-high +- nvidia,combined-power-req : Boolean, combined power request for CPU & Core +- nvidia,cpu-pwr-good-en : Boolean, CPU power good signal (from PMIC to PMC) + is enabled. + +Required properties when nvidia,suspend-mode is specified: +- nvidia,cpu-pwr-good-time : CPU power good time in uS. +- nvidia,cpu-pwr-off-time : CPU power off time in uS. +- nvidia,core-pwr-good-time : + Core power good time in uS. +- nvidia,core-pwr-off-time : Core power off time in uS. + +Required properties when nvidia,suspend-mode=<0>: +- nvidia,lp0-vec : Starting address and length of LP0 vector + The LP0 vector contains the warm boot code that is executed by AVP when + resuming from the LP0 state. The AVP (Audio-Video Processor) is an ARM7 + processor and always being the first boot processor when chip is power on + or resume from deep sleep mode. When the system is resumed from the deep + sleep mode, the warm boot code will restore some PLLs, clocks and then + bring up CPU0 for resuming the system. Example: @@ -25,6 +55,14 @@ pmc@7000f400 { clocks = <&tegra_car 110>, <&clk32k_in>; clock-names = "pclk", "clk32k_in"; nvidia,invert-interrupt; + nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <100>; + nvidia,core-pwr-good-time = <3845 3845>; + nvidia,core-pwr-off-time = <458>; + nvidia,core-power-req-active-high; + nvidia,sys-clock-req-active-high; + nvidia,lp0-vec = <0xbdffd000 0x2000>; }; / Tegra board dts file diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index faa33e8f937d..e896826d7d0f 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -21,6 +21,8 @@ #include #include +#include "pmc.h" + #define PMC_CTRL 0x0 #define PMC_CTRL_INTR_LOW (1 << 17) #define PMC_PWRGATE_TOGGLE 0x30 @@ -49,6 +51,22 @@ static void __iomem *tegra_pmc_base; static bool tegra_pmc_invert_interrupt; static struct clk *tegra_pclk; +struct pmc_pm_data { + u32 cpu_good_time; /* CPU power good time in uS */ + u32 cpu_off_time; /* CPU power off time in uS */ + u32 core_osc_time; /* Core power good osc time in uS */ + u32 core_pmu_time; /* Core power good pmu time in uS */ + u32 core_off_time; /* Core power off time in uS */ + bool corereq_high; /* Core power request active-high */ + bool sysclkreq_high; /* System clock request active-high */ + bool combined_req; /* Combined pwr req for CPU & Core */ + bool cpu_pwr_good_en; /* CPU power good signal is enabled */ + u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */ + u32 lp0_vec_size; /* The size of LP0 warm boot code */ + enum tegra_suspend_mode suspend_mode; +}; +static struct pmc_pm_data pmc_pm_data; + static inline u32 tegra_pmc_readl(u32 reg) { return readl(tegra_pmc_base + reg); @@ -176,6 +194,10 @@ static const struct of_device_id matches[] __initconst = { static void tegra_pmc_parse_dt(void) { struct device_node *np; + u32 prop; + enum tegra_suspend_mode suspend_mode; + u32 core_good_time[2] = {0, 0}; + u32 lp0_vec[2] = {0, 0}; np = of_find_matching_node(NULL, matches); BUG_ON(!np); @@ -186,6 +208,67 @@ static void tegra_pmc_parse_dt(void) "nvidia,invert-interrupt"); tegra_pclk = of_clk_get_by_name(np, "pclk"); WARN_ON(IS_ERR(tegra_pclk)); + + /* Grabbing the power management configurations */ + if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { + suspend_mode = TEGRA_SUSPEND_NONE; + } else { + switch (prop) { + case 0: + suspend_mode = TEGRA_SUSPEND_LP0; + break; + case 1: + suspend_mode = TEGRA_SUSPEND_LP1; + break; + case 2: + suspend_mode = TEGRA_SUSPEND_LP2; + break; + default: + suspend_mode = TEGRA_SUSPEND_NONE; + break; + } + } + + if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_good_time = prop; + + if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_off_time = prop; + + if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", + core_good_time, ARRAY_SIZE(core_good_time))) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_osc_time = core_good_time[0]; + pmc_pm_data.core_pmu_time = core_good_time[1]; + + if (of_property_read_u32(np, "nvidia,core-pwr-off-time", + &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_off_time = prop; + + pmc_pm_data.corereq_high = of_property_read_bool(np, + "nvidia,core-power-req-active-high"); + + pmc_pm_data.sysclkreq_high = of_property_read_bool(np, + "nvidia,sys-clock-req-active-high"); + + pmc_pm_data.combined_req = of_property_read_bool(np, + "nvidia,combined-power-req"); + + pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, + "nvidia,cpu-pwr-good-en"); + + if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, + ARRAY_SIZE(lp0_vec))) + if (suspend_mode == TEGRA_SUSPEND_LP0) + suspend_mode = TEGRA_SUSPEND_LP1; + + pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; + pmc_pm_data.lp0_vec_size = lp0_vec[1]; + + pmc_pm_data.suspend_mode = suspend_mode; } void __init tegra_pmc_init(void) diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 22f16c9dd44d..6bc0fc095269 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,14 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ + TEGRA_MAX_SUSPEND_MODE, +}; + #ifdef CONFIG_PM_SLEEP void set_power_timers(unsigned long us_on, unsigned long us_off); #endif From c8c2e6069065fdecfb195a2c438c7faa964aef22 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:47 +0800 Subject: [PATCH 47/68] ARM: tegra: pm: add platform suspend support Adding suspend to RAM support for Tegra platform. There are three suspend mode for Tegra. The difference were below. * LP2: CPU voltage off * LP1: CPU voltage off, DRAM in self-refresh * LP0: CPU + Core voltage off, DRAM in self-refresh After this patch, the LP2 suspend mode will be supported. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/common.c | 1 + arch/arm/mach-tegra/pm.c | 93 +++++++++++++++++++++++++++++++----- arch/arm/mach-tegra/pm.h | 15 ++++++ arch/arm/mach-tegra/pmc.c | 50 +++++++++++++++++-- arch/arm/mach-tegra/pmc.h | 4 +- 5 files changed, 146 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index c84505c1f644..eb1f3c8c74cc 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -109,5 +109,6 @@ void __init tegra_init_early(void) void __init tegra_init_late(void) { + tegra_init_suspend(); tegra_powergate_debugfs_init(); } diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 5f5611f40b43..3a3318a83ad3 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -38,14 +39,10 @@ #include "fuse.h" #include "pmc.h" #include "sleep.h" - -#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ - -#define PMC_CTRL 0x0 +#include "pmc.h" #ifdef CONFIG_PM_SLEEP static DEFINE_SPINLOCK(tegra_lp2_lock); -static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); void (*tegra_tear_down_cpu)(void); /* @@ -145,14 +142,7 @@ static int tegra_sleep_cpu(unsigned long v2p) void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) { - u32 mode; - - /* Only the last cpu down does the final suspend steps */ - mode = readl(pmc + PMC_CTRL); - mode |= TEGRA_POWER_CPU_PWRREQ_OE; - writel(mode, pmc + PMC_CTRL); - - set_power_timers(cpu_on_time, cpu_off_time); + tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); cpu_cluster_pm_enter(); suspend_cpu_complex(); @@ -162,4 +152,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) restore_cpu_complex(); cpu_cluster_pm_exit(); } + +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode) +{ + /* Tegra114 didn't support any suspending mode yet. */ + if (tegra_chip_id == TEGRA114) + return TEGRA_SUSPEND_NONE; + + /* + * The Tegra devices only support suspending to LP2 currently. + */ + if (mode > TEGRA_SUSPEND_LP2) + return TEGRA_SUSPEND_LP2; + + return mode; +} + +static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { + [TEGRA_SUSPEND_NONE] = "none", + [TEGRA_SUSPEND_LP2] = "LP2", + [TEGRA_SUSPEND_LP1] = "LP1", + [TEGRA_SUSPEND_LP0] = "LP0", +}; + +static int __cpuinit tegra_suspend_enter(suspend_state_t state) +{ + enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); + + if (WARN_ON(mode < TEGRA_SUSPEND_NONE || + mode >= TEGRA_MAX_SUSPEND_MODE)) + return -EINVAL; + + pr_info("Entering suspend state %s\n", lp_state[mode]); + + tegra_pmc_pm_set(mode); + + local_fiq_disable(); + + suspend_cpu_complex(); + switch (mode) { + case TEGRA_SUSPEND_LP2: + tegra_set_cpu_in_lp2(0); + break; + default: + break; + } + + cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); + + switch (mode) { + case TEGRA_SUSPEND_LP2: + tegra_clear_cpu_in_lp2(0); + break; + default: + break; + } + restore_cpu_complex(); + + local_fiq_enable(); + + return 0; +} + +static const struct platform_suspend_ops tegra_suspend_ops = { + .valid = suspend_valid_only_mem, + .enter = tegra_suspend_enter, +}; + +void __init tegra_init_suspend(void) +{ + if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) + return; + + tegra_pmc_suspend_init(); + + suspend_set_ops(&tegra_suspend_ops); +} #endif diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 787335cc964c..73a45f181fd9 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -21,6 +21,8 @@ #ifndef _MACH_TEGRA_PM_H_ #define _MACH_TEGRA_PM_H_ +#include "pmc.h" + extern unsigned long l2x0_saved_regs_addr; void save_cpu_arch_register(void); @@ -32,4 +34,17 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id); void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); extern void (*tegra_tear_down_cpu)(void); +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode); +void tegra_init_suspend(void); +#else +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode) +{ + return TEGRA_SUSPEND_NONE; +} +static inline void tegra_init_suspend(void) {} +#endif + #endif /* _MACH_TEGRA_PM_H_ */ diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index e896826d7d0f..32360e540ce6 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -21,7 +21,14 @@ #include #include +#include "fuse.h" +#include "pm.h" #include "pmc.h" +#include "sleep.h" + +#define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ +#define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ +#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ #define PMC_CTRL 0x0 #define PMC_CTRL_INTR_LOW (1 << 17) @@ -157,14 +164,12 @@ int tegra_pmc_cpu_remove_clamping(int cpuid) } #ifdef CONFIG_PM_SLEEP -void set_power_timers(unsigned long us_on, unsigned long us_off) +static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) { unsigned long long ticks; unsigned long long pclk; - unsigned long rate; static unsigned long tegra_last_pclk; - rate = clk_get_rate(tegra_pclk); if (WARN_ON_ONCE(rate <= 0)) pclk = 100000000; else @@ -182,6 +187,44 @@ void set_power_timers(unsigned long us_on, unsigned long us_off) } tegra_last_pclk = pclk; } + +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) +{ + return pmc_pm_data.suspend_mode; +} + +void tegra_pmc_pm_set(enum tegra_suspend_mode mode) +{ + u32 reg; + unsigned long rate = 0; + + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; + reg &= ~TEGRA_POWER_EFFECT_LP0; + + switch (mode) { + case TEGRA_SUSPEND_LP2: + rate = clk_get_rate(tegra_pclk); + break; + default: + break; + } + + set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, + rate); + + tegra_pmc_writel(reg, PMC_CTRL); +} + +void tegra_pmc_suspend_init(void) +{ + u32 reg; + + /* Always enable CPU power request */ + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; + tegra_pmc_writel(reg, PMC_CTRL); +} #endif static const struct of_device_id matches[] __initconst = { @@ -228,6 +271,7 @@ static void tegra_pmc_parse_dt(void) break; } } + suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) suspend_mode = TEGRA_SUSPEND_NONE; diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 6bc0fc095269..e1c2df272f7d 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -27,7 +27,9 @@ enum tegra_suspend_mode { }; #ifdef CONFIG_PM_SLEEP -void set_power_timers(unsigned long us_on, unsigned long us_off); +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_pm_set(enum tegra_suspend_mode mode); +void tegra_pmc_suspend_init(void); #endif bool tegra_pmc_cpu_is_powered(int cpuid); From 4d82d0587b4a964ea3a7c73aa044b433000527dd Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Tue, 2 Apr 2013 01:20:50 +0000 Subject: [PATCH 48/68] ARM: tegra: cpuidle: remove redundant parameters for powered-down mode After the patch series for system suspending support, tegra_idle_lp2_last() no longer uses its parameters cpu_on_time or cpu_off_time, so remove them. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/cpuidle-tegra20.c | 6 +----- arch/arm/mach-tegra/cpuidle-tegra30.c | 6 +----- arch/arm/mach-tegra/pm.c | 2 +- arch/arm/mach-tegra/pm.h | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 825ced4f7a40..8bbbdebed882 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -130,10 +130,6 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[index]; - u32 cpu_on_time = state->exit_latency; - u32 cpu_off_time = state->target_residency - state->exit_latency; - while (tegra20_cpu_is_resettable_soon()) cpu_relax(); @@ -142,7 +138,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - tegra_idle_lp2_last(cpu_on_time, cpu_off_time); + tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 80445ed33d95..c0931c8bb3e5 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -72,10 +72,6 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[index]; - u32 cpu_on_time = state->exit_latency; - u32 cpu_off_time = state->target_residency - state->exit_latency; - /* All CPUs entering LP2 is not working. * Don't let CPU0 enter LP2 when any secondary CPU is online. */ @@ -86,7 +82,7 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - tegra_idle_lp2_last(cpu_on_time, cpu_off_time); + tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 3a3318a83ad3..84d8742bdb1e 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -140,7 +140,7 @@ static int tegra_sleep_cpu(unsigned long v2p) return 0; } -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) +void tegra_idle_lp2_last(void) { tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 73a45f181fd9..9d2d038bf12e 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -31,7 +31,7 @@ void restore_cpu_arch_register(void); void tegra_clear_cpu_in_lp2(int phy_cpu_id); bool tegra_set_cpu_in_lp2(int phy_cpu_id); -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); +void tegra_idle_lp2_last(void); extern void (*tegra_tear_down_cpu)(void); #ifdef CONFIG_PM_SLEEP From 5a88b0d10f198ddd5f988f40d34b52f34c87a5c6 Mon Sep 17 00:00:00 2001 From: Yen Lin Date: Wed, 6 Mar 2013 11:47:24 +0000 Subject: [PATCH 49/68] clk: tegra: Fix periph_clk_to_bit macro The parameter name should be "gate", not "periph". This worked, however, because it happens that everywhere periph_clk_to_bit is called, "gate" was in the local scope. Signed-off-by: Yen Lin Signed-off-by: Andrew Chew Reviewed-by: Thierry Reding Reviewed-by: Prashant Gaikwad Acked-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph-gate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 6dd533251e7b..d87e1cece9fa 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -41,7 +41,7 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define write_rst_clr(val, gate) \ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) -#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32)) +#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) /* Peripheral gate clock ops */ static int clk_periph_is_enabled(struct clk_hw *hw) From 4dd59cdd35506b77ed4ebf4bb90347d7653ba585 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 28 Mar 2013 21:31:27 +0100 Subject: [PATCH 50/68] clk: tegra: Export peripheral reset functions The tegra_periph_reset_assert() and tegra_periph_reset_deassert() functions can be used by drivers to reset peripherals. In order to allow such drivers to be built as modules, export the functions. Note that this restores the status quo as the functions were exported before the move to the drivers/clk tree. Signed-off-by: Thierry Reding Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 788486e6331a..9dbd301ace77 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c) tegra_periph_reset(gate, 0); } +EXPORT_SYMBOL(tegra_periph_reset_deassert); void tegra_periph_reset_assert(struct clk *c) { @@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c) tegra_periph_reset(gate, 1); } +EXPORT_SYMBOL(tegra_periph_reset_assert); const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, From ce910686f814fe220f4e55ecca5cfdea7082b3de Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Apr 2013 16:18:44 +0200 Subject: [PATCH 51/68] clk: tegra: Make gr2d and gr3d clocks children of pll_c By default these clocks are children of pll_m, but in downstream kernels they are reparented to pll_c. While at it, decrease their frequencies to 300 MHz because the defaults aren't in the specified range. gr2d can reportedly run at much higher frequencies, but 300 MHz works and is a more conservative default. Signed-off-by: Thierry Reding Acked-by: Mike Turquette Acked-By: Peter De Schrijver Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra20.c | 2 ++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index b92d48be4cc9..f87bd4a8c324 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1247,6 +1247,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {host1x, pll_c, 150000000, 0}, {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ba6f51bc9f3b..b8b241de3f4c 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1911,6 +1911,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {disp1, pll_p, 600000000, 0}, {disp2, pll_p, 600000000, 0}, {twd, clk_max, 0, 1}, + {gr2d, pll_c, 300000000, 0}, + {gr3d, pll_c, 300000000, 0}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; From 82ce742140f32394cc5be75f1c98cdbbff284582 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Thu, 4 Apr 2013 14:35:04 +0530 Subject: [PATCH 52/68] clk: tegra: Fix cdev1 and cdev2 IDs Correct IDs for cdev1 and cdev2 are 94 and 93 respectively. Signed-off-by: Prashant Gaikwad [swarren: split into separate driver and device-tree patches] Signed-off-by: Stephen Warren --- .../devicetree/bindings/clock/nvidia,tegra20-car.txt | 4 ++-- drivers/clk/tegra/clk-tegra20.c | 2 +- drivers/clk/tegra/clk-tegra30.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.txt index 0921fac73528..e885680f6b45 100644 --- a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.txt +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.txt @@ -120,8 +120,8 @@ Required properties : 90 clk_d 91 unassigned 92 sus - 93 cdev1 - 94 cdev2 + 93 cdev2 + 94 cdev1 95 unassigned 96 uart2 diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f87bd4a8c324..a7dc0a937361 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -236,7 +236,7 @@ enum tegra20_clk { dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, - iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, + iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1, uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve, osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0, pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index b8b241de3f4c..181a6eef5ce8 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -330,7 +330,7 @@ enum tegra30_clk { usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow, dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92, - cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, + cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x, atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x, spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda, From 441f199a37cfd66c5dd8dd45490bd3ea6971117d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 25 Mar 2013 13:22:24 -0600 Subject: [PATCH 53/68] clk: tegra: defer application of init table The Tegra clock driver is initialized during the ARM machine descriptor's .init_irq() hook. It can't be initialized earlier, since dynamic memory usage is required. It can't be initialized later, since the .init_timer() hook needs the clocks initialized. However, at this time, udelay() doesn't work. The Tegra clock initialization table may enable some PLLs. Enabling a PLL may require usage of udelay(). Hence, this can't happen right when the clock driver is initialized. To solve this, separate the clock driver initialization from the clock table processing, so they can execute at separate times. Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/tegra.c | 3 +++ drivers/clk/tegra/clk-tegra20.c | 7 ++++++- drivers/clk/tegra/clk-tegra30.c | 7 ++++++- drivers/clk/tegra/clk.c | 10 ++++++++++ drivers/clk/tegra/clk.h | 3 +++ include/linux/clk/tegra.h | 1 + 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 84deeab23ee7..61749e2d8111 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,8 @@ static void __init tegra_dt_init(void) struct soc_device *soc_dev; struct device *parent = NULL; + tegra_clocks_apply_init_table(); + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) goto out; diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index a7dc0a937361..a15fb28197b5 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1252,6 +1252,11 @@ static __initdata struct tegra_clk_init_table init_table[] = { {clk_max, clk_max, 0, 0}, /* This MUST be the last entry */ }; +static void __init tegra20_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1318,7 +1323,7 @@ void __init tegra20_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra20_clock_apply_init_table; tegra_cpu_car_ops = &tegra20_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 181a6eef5ce8..e0ee93c2db6c 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1916,6 +1916,11 @@ static __initdata struct tegra_clk_init_table init_table[] = { {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; +static void __init tegra30_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + /* * Some clocks may be used by different drivers depending on the board * configuration. List those here to register them twice in the clock lookup @@ -1989,7 +1994,7 @@ void __init tegra30_clock_init(struct device_node *np) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - tegra_init_from_table(init_table, clks, clk_max); + tegra_clk_apply_init_table = tegra30_clock_apply_init_table; tegra_cpu_car_ops = &tegra30_cpu_car_ops; } diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index a603b9af0ad3..4a61d15425dc 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -83,3 +83,13 @@ void __init tegra_clocks_init(void) { of_clk_init(tegra_dt_clk_match); } + +tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + +void __init tegra_clocks_apply_init_table(void) +{ + if (!tegra_clk_apply_init_table) + return; + + tegra_clk_apply_init_table(); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index a09d7dcaf183..3c566e2d518a 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -510,4 +510,7 @@ void tegra30_clock_init(struct device_node *np); static inline void tegra30_clock_init(struct device_node *np) {} #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +typedef void (*tegra_clk_apply_init_table_func)(void); +extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + #endif /* TEGRA_CLK_H */ diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index 404d6f940872..642789baec74 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -123,5 +123,6 @@ static inline void tegra_cpu_clock_resume(void) void tegra_periph_reset_deassert(struct clk *c); void tegra_periph_reset_assert(struct clk *c); void tegra_clocks_init(void); +void tegra_clocks_apply_init_table(void); #endif /* __LINUX_CLK_TEGRA_H_ */ From 6a676fa0af4e2bd11ab3950e277e81a959a9a198 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:35 +0300 Subject: [PATCH 54/68] clk: tegra: provide dummy cpu car ops tegra_boot_secondary() relies on some of the car ops. This means having an uninitialized tegra_cpu_car_ops will lead to an early boot panic. Providing a dummy struct avoids this and makes adding Tegra114 clock support in a bisectable way a lot easier. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 4a61d15425dc..70b7a4783bee 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -22,7 +22,8 @@ #include "clk.h" /* Global data of Tegra CPU CAR ops */ -struct tegra_cpu_car_ops *tegra_cpu_car_ops; +static struct tegra_cpu_car_ops dummy_car_ops; +struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max) From dba4072a4a20b2986562cced98ce04a887614528 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:36 +0300 Subject: [PATCH 55/68] clk: tegra: Refactor PLL programming code Refactor the PLL programming code to make it useable by the new PLL types introduced by Tegra114. The following changes were done: * Split programming the PLL into updating m,n,p and updating cpcon * Move locking from _update_pll_cpcon() to clk_pll_set_rate() * Introduce _get_pll_mnp() helper * Move check for identical m,n,p values to clk_pll_set_rate() * struct tegra_clk_pll_freq_table will always contain the values as defined by the hardware. * Simplify the arguments to clk_pll_wait_for_lock() * Split _tegra_clk_register_pll() Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 284 +++++++++++++++++++------------- drivers/clk/tegra/clk-tegra20.c | 144 ++++++++-------- drivers/clk/tegra/clk-tegra30.c | 220 ++++++++++++------------- drivers/clk/tegra/clk.h | 9 +- 4 files changed, 360 insertions(+), 297 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 165f24734c1b..3feefb15e473 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -113,20 +113,28 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) pll_writel_misc(val, pll); } -static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll, - void __iomem *lock_addr, u32 lock_bit_idx) +static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) { int i; - u32 val; + u32 val, lock_bit; + void __iomem *lock_addr; if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { udelay(pll->params->lock_delay); return 0; } + lock_addr = pll->clk_base; + if (pll->flags & TEGRA_PLL_LOCK_MISC) + lock_addr += pll->params->misc_reg; + else + lock_addr += pll->params->base_reg; + + lock_bit = BIT(pll->params->lock_bit_idx); + for (i = 0; i < pll->params->lock_delay; i++) { val = readl_relaxed(lock_addr); - if (val & BIT(lock_bit_idx)) { + if (val & lock_bit) { udelay(PLL_POST_LOCK_DELAY); return 0; } @@ -155,7 +163,7 @@ static int clk_pll_is_enabled(struct clk_hw *hw) return val & PLL_BASE_ENABLE ? 1 : 0; } -static int _clk_pll_enable(struct clk_hw *hw) +static void _clk_pll_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); u32 val; @@ -172,11 +180,6 @@ static int _clk_pll_enable(struct clk_hw *hw) val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE; writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE); } - - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg, - pll->params->lock_bit_idx); - - return 0; } static void _clk_pll_disable(struct clk_hw *hw) @@ -204,7 +207,9 @@ static int clk_pll_enable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); - ret = _clk_pll_enable(hw); + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -241,8 +246,6 @@ static int _get_table_rate(struct clk_hw *hw, if (sel->input_rate == 0) return -EINVAL; - BUG_ON(sel->p < 1); - cfg->input_rate = sel->input_rate; cfg->output_rate = sel->output_rate; cfg->m = sel->m; @@ -290,88 +293,109 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, cfg->output_rate <<= 1) p_div++; - cfg->p = 1 << p_div; + cfg->p = p_div; cfg->m = parent_rate / cfreq; cfg->n = cfg->output_rate / cfreq; cfg->cpcon = OUT_OF_TABLE_CPCON; if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) || - cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) { + (1 << p_div) > divp_max(pll) + || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); return -EINVAL; } + if (pll->flags & TEGRA_PLLU) + cfg->p ^= 1; + return 0; } +static void _update_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) +{ + u32 val; + + val = pll_readl_base(pll); + + val &= ~((divm_mask(pll) << pll->divm_shift) | + (divn_mask(pll) << pll->divn_shift) | + (divp_mask(pll) << pll->divp_shift)); + val |= ((cfg->m << pll->divm_shift) | + (cfg->n << pll->divn_shift) | + (cfg->p << pll->divp_shift)); + + pll_writel_base(val, pll); +} + +static void _get_pll_mnp(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg) +{ + u32 val; + + val = pll_readl_base(pll); + + cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); + cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); + cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); +} + +static void _update_pll_cpcon(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate) +{ + u32 val; + + val = pll_readl_misc(pll); + + val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); + val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; + + if (pll->flags & TEGRA_PLL_SET_LFCON) { + val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); + if (cfg->n >= PLLDU_LFCON_SET_DIVN) + val |= 1 << PLL_MISC_LFCON_SHIFT; + } else if (pll->flags & TEGRA_PLL_SET_DCCON) { + val &= ~(1 << PLL_MISC_DCCON_SHIFT); + if (rate >= (pll->params->vco_max >> 1)) + val |= 1 << PLL_MISC_DCCON_SHIFT; + } + + pll_writel_misc(val, pll); +} + static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - unsigned long flags = 0; - u32 divp, val, old_base; - int state; - - divp = __ffs(cfg->p); - - if (pll->flags & TEGRA_PLLU) - divp ^= 1; - - if (pll->lock) - spin_lock_irqsave(pll->lock, flags); - - old_base = val = pll_readl_base(pll); - val &= ~((divm_mask(pll) << pll->divm_shift) | - (divn_mask(pll) << pll->divn_shift) | - (divp_mask(pll) << pll->divp_shift)); - val |= ((cfg->m << pll->divm_shift) | - (cfg->n << pll->divn_shift) | - (divp << pll->divp_shift)); - if (val == old_base) { - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - return 0; - } + int state, ret = 0; state = clk_pll_is_enabled(hw); - if (state) { - _clk_pll_disable(hw); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); - } - pll_writel_base(val, pll); - - if (pll->flags & TEGRA_PLL_HAS_CPCON) { - val = pll_readl_misc(pll); - val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); - val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; - if (pll->flags & TEGRA_PLL_SET_LFCON) { - val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); - if (cfg->n >= PLLDU_LFCON_SET_DIVN) - val |= 0x1 << PLL_MISC_LFCON_SHIFT; - } else if (pll->flags & TEGRA_PLL_SET_DCCON) { - val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); - if (rate >= (pll->params->vco_max >> 1)) - val |= 0x1 << PLL_MISC_DCCON_SHIFT; - } - pll_writel_misc(val, pll); - } - - if (pll->lock) - spin_unlock_irqrestore(pll->lock, flags); - if (state) - clk_pll_enable(hw); + _clk_pll_disable(hw); - return 0; + _update_pll_mnp(pll, cfg); + + if (pll->flags & TEGRA_PLL_HAS_CPCON) + _update_pll_cpcon(pll, cfg, rate); + + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } + + return ret; } static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; if (pll->flags & TEGRA_PLL_FIXED) { if (rate != pll->fixed_rate) { @@ -387,7 +411,18 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, _calc_rate(hw, &cfg, rate, parent_rate)) return -EINVAL; - return _program_pll(hw, &cfg, rate); + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) + ret = _program_pll(hw, &cfg, rate); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -409,7 +444,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * (1 << cfg.p)); return output_rate; } @@ -418,10 +453,12 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - u32 val = pll_readl_base(pll); - u32 divn = 0, divm = 0, divp = 0; + struct tegra_clk_pll_freq_table cfg; + u32 val; u64 rate = parent_rate; + val = pll_readl_base(pll); + if (val & PLL_BASE_BYPASS) return parent_rate; @@ -435,16 +472,16 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, return pll->fixed_rate; } - divp = (val >> pll->divp_shift) & (divp_mask(pll)); + _get_pll_mnp(pll, &cfg); + if (pll->flags & TEGRA_PLLU) - divp ^= 1; + cfg.p ^= 1; - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); - divm *= (1 << divp); + cfg.m *= 1 << cfg.p; + + rate *= cfg.n; + do_div(rate, cfg.m); - rate *= divn; - do_div(rate, divm); return rate; } @@ -538,8 +575,8 @@ static int clk_plle_enable(struct clk_hw *hw) val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE); pll_writel_base(val, pll); - clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg, - pll->params->lock_bit_idx); + clk_pll_wait_for_lock(pll); + return 0; } @@ -577,28 +614,17 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; -static struct clk *_tegra_clk_register_pll(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, - struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock, - const struct clk_ops *ops) +static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, + void __iomem *pmc, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { struct tegra_clk_pll *pll; - struct clk *clk; - struct clk_init_data init; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); - init.name = name; - init.ops = ops; - init.flags = flags; - init.parent_names = (parent_name ? &parent_name : NULL); - init.num_parents = (parent_name ? 1 : 0); - pll->clk_base = clk_base; pll->pmc = pmc; @@ -615,34 +641,68 @@ static struct clk *_tegra_clk_register_pll(const char *name, pll->divm_shift = PLL_BASE_DIVM_SHIFT; pll->divm_width = PLL_BASE_DIVM_WIDTH; + return pll; +} + +static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, + const char *name, const char *parent_name, unsigned long flags, + const struct clk_ops *ops) +{ + struct clk_init_data init; + + init.name = name; + init.ops = ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + /* Data in .init is copied by clk_register(), so stack variable OK */ pll->hw.init = &init; - clk = clk_register(NULL, &pll->hw); + return clk_register(NULL, &pll->hw); +} + +struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pll_ops); if (IS_ERR(clk)) kfree(pll); return clk; } -struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, - void __iomem *clk_base, void __iomem *pmc, - unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, - struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) -{ - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_pll_ops); -} - struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock) { - return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, - flags, fixed_rate, pll_params, pll_flags, freq_table, - lock, &tegra_clk_plle_ops); + struct tegra_clk_pll *pll; + struct clk *clk; + pll_flags |= TEGRA_PLL_LOCK_MISC; + + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index a15fb28197b5..c2a1c4cae47c 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -248,125 +248,125 @@ static struct clk *clks[clk_max]; static struct clk_onecell_data clk_data; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 600000000, 600, 12, 1, 8 }, - { 13000000, 600000000, 600, 13, 1, 8 }, - { 19200000, 600000000, 500, 16, 1, 6 }, - { 26000000, 600000000, 600, 26, 1, 8 }, + { 12000000, 600000000, 600, 12, 0, 8 }, + { 13000000, 600000000, 600, 13, 0, 8 }, + { 19200000, 600000000, 500, 16, 0, 6 }, + { 26000000, 600000000, 600, 26, 0, 8 }, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 19200000, 216000000, 90, 4, 2, 1}, - { 26000000, 216000000, 432, 26, 2, 8}, - { 12000000, 432000000, 432, 12, 1, 8}, - { 13000000, 432000000, 432, 13, 1, 8}, - { 19200000, 432000000, 90, 4, 1, 1}, - { 26000000, 432000000, 432, 26, 1, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 19200000, 216000000, 90, 4, 1, 1}, + { 26000000, 216000000, 432, 26, 1, 8}, + { 12000000, 432000000, 432, 12, 0, 8}, + { 13000000, 432000000, 432, 13, 0, 8}, + { 19200000, 432000000, 90, 4, 0, 1}, + { 26000000, 432000000, 432, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 19200000, 216000000, 135, 12, 1, 3}, - { 26000000, 216000000, 216, 26, 1, 4}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 19200000, 216000000, 135, 12, 0, 3}, + { 26000000, 216000000, 216, 26, 0, 4}, - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 0}, - { 13000000, 480000000, 960, 13, 2, 0}, - { 19200000, 480000000, 200, 4, 2, 0}, - { 26000000, 480000000, 960, 26, 2, 0}, + { 12000000, 480000000, 960, 12, 0, 0}, + { 13000000, 480000000, 960, 13, 0, 0}, + { 19200000, 480000000, 200, 4, 0, 0}, + { 26000000, 480000000, 960, 26, 0, 0}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, /* 912 MHz */ - { 12000000, 912000000, 912, 12, 1, 12}, - { 13000000, 912000000, 912, 13, 1, 12}, - { 19200000, 912000000, 760, 16, 1, 8}, - { 26000000, 912000000, 912, 26, 1, 12}, + { 12000000, 912000000, 912, 12, 0, 12}, + { 13000000, 912000000, 912, 13, 0, 12}, + { 19200000, 912000000, 760, 16, 0, 8}, + { 26000000, 912000000, 912, 26, 0, 12}, /* 816 MHz */ - { 12000000, 816000000, 816, 12, 1, 12}, - { 13000000, 816000000, 816, 13, 1, 12}, - { 19200000, 816000000, 680, 16, 1, 8}, - { 26000000, 816000000, 816, 26, 1, 12}, + { 12000000, 816000000, 816, 12, 0, 12}, + { 13000000, 816000000, 816, 13, 0, 12}, + { 19200000, 816000000, 680, 16, 0, 8}, + { 26000000, 816000000, 816, 26, 0, 12}, /* 760 MHz */ - { 12000000, 760000000, 760, 12, 1, 12}, - { 13000000, 760000000, 760, 13, 1, 12}, - { 19200000, 760000000, 950, 24, 1, 8}, - { 26000000, 760000000, 760, 26, 1, 12}, + { 12000000, 760000000, 760, 12, 0, 12}, + { 13000000, 760000000, 760, 13, 0, 12}, + { 19200000, 760000000, 950, 24, 0, 8}, + { 26000000, 760000000, 760, 26, 0, 12}, /* 750 MHz */ - { 12000000, 750000000, 750, 12, 1, 12}, - { 13000000, 750000000, 750, 13, 1, 12}, - { 19200000, 750000000, 625, 16, 1, 8}, - { 26000000, 750000000, 750, 26, 1, 12}, + { 12000000, 750000000, 750, 12, 0, 12}, + { 13000000, 750000000, 750, 13, 0, 12}, + { 19200000, 750000000, 625, 16, 0, 8}, + { 26000000, 750000000, 750, 26, 0, 12}, /* 608 MHz */ - { 12000000, 608000000, 608, 12, 1, 12}, - { 13000000, 608000000, 608, 13, 1, 12}, - { 19200000, 608000000, 380, 12, 1, 8}, - { 26000000, 608000000, 608, 26, 1, 12}, + { 12000000, 608000000, 608, 12, 0, 12}, + { 13000000, 608000000, 608, 13, 0, 12}, + { 19200000, 608000000, 380, 12, 0, 8}, + { 26000000, 608000000, 608, 26, 0, 12}, /* 456 MHz */ - { 12000000, 456000000, 456, 12, 1, 12}, - { 13000000, 456000000, 456, 13, 1, 12}, - { 19200000, 456000000, 380, 16, 1, 8}, - { 26000000, 456000000, 456, 26, 1, 12}, + { 12000000, 456000000, 456, 12, 0, 12}, + { 13000000, 456000000, 456, 13, 0, 12}, + { 19200000, 456000000, 380, 16, 0, 8}, + { 26000000, 456000000, 456, 26, 0, 12}, /* 312 MHz */ - { 12000000, 312000000, 312, 12, 1, 12}, - { 13000000, 312000000, 312, 13, 1, 12}, - { 19200000, 312000000, 260, 16, 1, 8}, - { 26000000, 312000000, 312, 26, 1, 12}, + { 12000000, 312000000, 312, 12, 0, 12}, + { 13000000, 312000000, 312, 13, 0, 12}, + { 19200000, 312000000, 260, 16, 0, 8}, + { 26000000, 312000000, 312, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { - { 12000000, 100000000, 200, 24, 1, 0 }, + { 12000000, 100000000, 200, 24, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index e0ee93c2db6c..02609d125e0e 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -374,164 +374,164 @@ static const struct utmi_clk_param utmi_parameters[] = { }; static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { - { 12000000, 1040000000, 520, 6, 1, 8}, - { 13000000, 1040000000, 480, 6, 1, 8}, - { 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */ - { 19200000, 1040000000, 325, 6, 1, 6}, - { 26000000, 1040000000, 520, 13, 1, 8}, + { 12000000, 1040000000, 520, 6, 0, 8}, + { 13000000, 1040000000, 480, 6, 0, 8}, + { 16800000, 1040000000, 495, 8, 0, 8}, /* actual: 1039.5 MHz */ + { 19200000, 1040000000, 325, 6, 0, 6}, + { 26000000, 1040000000, 520, 13, 0, 8}, - { 12000000, 832000000, 416, 6, 1, 8}, - { 13000000, 832000000, 832, 13, 1, 8}, - { 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */ - { 19200000, 832000000, 260, 6, 1, 8}, - { 26000000, 832000000, 416, 13, 1, 8}, + { 12000000, 832000000, 416, 6, 0, 8}, + { 13000000, 832000000, 832, 13, 0, 8}, + { 16800000, 832000000, 396, 8, 0, 8}, /* actual: 831.6 MHz */ + { 19200000, 832000000, 260, 6, 0, 8}, + { 26000000, 832000000, 416, 13, 0, 8}, - { 12000000, 624000000, 624, 12, 1, 8}, - { 13000000, 624000000, 624, 13, 1, 8}, - { 16800000, 600000000, 520, 14, 1, 8}, - { 19200000, 624000000, 520, 16, 1, 8}, - { 26000000, 624000000, 624, 26, 1, 8}, + { 12000000, 624000000, 624, 12, 0, 8}, + { 13000000, 624000000, 624, 13, 0, 8}, + { 16800000, 600000000, 520, 14, 0, 8}, + { 19200000, 624000000, 520, 16, 0, 8}, + { 26000000, 624000000, 624, 26, 0, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, - { 12000000, 520000000, 520, 12, 1, 8}, - { 13000000, 520000000, 520, 13, 1, 8}, - { 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */ - { 19200000, 520000000, 325, 12, 1, 6}, - { 26000000, 520000000, 520, 26, 1, 8}, + { 12000000, 520000000, 520, 12, 0, 8}, + { 13000000, 520000000, 520, 13, 0, 8}, + { 16800000, 520000000, 495, 16, 0, 8}, /* actual: 519.75 MHz */ + { 19200000, 520000000, 325, 12, 0, 6}, + { 26000000, 520000000, 520, 26, 0, 8}, - { 12000000, 416000000, 416, 12, 1, 8}, - { 13000000, 416000000, 416, 13, 1, 8}, - { 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */ - { 19200000, 416000000, 260, 12, 1, 6}, - { 26000000, 416000000, 416, 26, 1, 8}, + { 12000000, 416000000, 416, 12, 0, 8}, + { 13000000, 416000000, 416, 13, 0, 8}, + { 16800000, 416000000, 396, 16, 0, 8}, /* actual: 415.8 MHz */ + { 19200000, 416000000, 260, 12, 0, 6}, + { 26000000, 416000000, 416, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { - { 12000000, 666000000, 666, 12, 1, 8}, - { 13000000, 666000000, 666, 13, 1, 8}, - { 16800000, 666000000, 555, 14, 1, 8}, - { 19200000, 666000000, 555, 16, 1, 8}, - { 26000000, 666000000, 666, 26, 1, 8}, - { 12000000, 600000000, 600, 12, 1, 8}, - { 13000000, 600000000, 600, 13, 1, 8}, - { 16800000, 600000000, 500, 14, 1, 8}, - { 19200000, 600000000, 375, 12, 1, 6}, - { 26000000, 600000000, 600, 26, 1, 8}, + { 12000000, 666000000, 666, 12, 0, 8}, + { 13000000, 666000000, 666, 13, 0, 8}, + { 16800000, 666000000, 555, 14, 0, 8}, + { 19200000, 666000000, 555, 16, 0, 8}, + { 26000000, 666000000, 666, 26, 0, 8}, + { 12000000, 600000000, 600, 12, 0, 8}, + { 13000000, 600000000, 600, 13, 0, 8}, + { 16800000, 600000000, 500, 14, 0, 8}, + { 19200000, 600000000, 375, 12, 0, 6}, + { 26000000, 600000000, 600, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - { 12000000, 216000000, 432, 12, 2, 8}, - { 13000000, 216000000, 432, 13, 2, 8}, - { 16800000, 216000000, 360, 14, 2, 8}, - { 19200000, 216000000, 360, 16, 2, 8}, - { 26000000, 216000000, 432, 26, 2, 8}, + { 12000000, 216000000, 432, 12, 1, 8}, + { 13000000, 216000000, 432, 13, 1, 8}, + { 16800000, 216000000, 360, 14, 1, 8}, + { 19200000, 216000000, 360, 16, 1, 8}, + { 26000000, 216000000, 432, 26, 1, 8}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { - { 9600000, 564480000, 294, 5, 1, 4}, - { 9600000, 552960000, 288, 5, 1, 4}, - { 9600000, 24000000, 5, 2, 1, 1}, + { 9600000, 564480000, 294, 5, 0, 4}, + { 9600000, 552960000, 288, 5, 0, 4}, + { 9600000, 24000000, 5, 2, 0, 1}, - { 28800000, 56448000, 49, 25, 1, 1}, - { 28800000, 73728000, 64, 25, 1, 1}, - { 28800000, 24000000, 5, 6, 1, 1}, + { 28800000, 56448000, 49, 25, 0, 1}, + { 28800000, 73728000, 64, 25, 0, 1}, + { 28800000, 24000000, 5, 6, 0, 1}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { - { 12000000, 216000000, 216, 12, 1, 4}, - { 13000000, 216000000, 216, 13, 1, 4}, - { 16800000, 216000000, 180, 14, 1, 4}, - { 19200000, 216000000, 180, 16, 1, 4}, - { 26000000, 216000000, 216, 26, 1, 4}, + { 12000000, 216000000, 216, 12, 0, 4}, + { 13000000, 216000000, 216, 13, 0, 4}, + { 16800000, 216000000, 180, 14, 0, 4}, + { 19200000, 216000000, 180, 16, 0, 4}, + { 26000000, 216000000, 216, 26, 0, 4}, - { 12000000, 594000000, 594, 12, 1, 8}, - { 13000000, 594000000, 594, 13, 1, 8}, - { 16800000, 594000000, 495, 14, 1, 8}, - { 19200000, 594000000, 495, 16, 1, 8}, - { 26000000, 594000000, 594, 26, 1, 8}, + { 12000000, 594000000, 594, 12, 0, 8}, + { 13000000, 594000000, 594, 13, 0, 8}, + { 16800000, 594000000, 495, 14, 0, 8}, + { 19200000, 594000000, 495, 16, 0, 8}, + { 26000000, 594000000, 594, 26, 0, 8}, - { 12000000, 1000000000, 1000, 12, 1, 12}, - { 13000000, 1000000000, 1000, 13, 1, 12}, - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 12}, + { 12000000, 1000000000, 1000, 12, 0, 12}, + { 13000000, 1000000000, 1000, 13, 0, 12}, + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { - { 12000000, 480000000, 960, 12, 2, 12}, - { 13000000, 480000000, 960, 13, 2, 12}, - { 16800000, 480000000, 400, 7, 2, 5}, - { 19200000, 480000000, 200, 4, 2, 3}, - { 26000000, 480000000, 960, 26, 2, 12}, + { 12000000, 480000000, 960, 12, 0, 12}, + { 13000000, 480000000, 960, 13, 0, 12}, + { 16800000, 480000000, 400, 7, 0, 5}, + { 19200000, 480000000, 200, 4, 0, 3}, + { 26000000, 480000000, 960, 26, 0, 12}, { 0, 0, 0, 0, 0, 0 }, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { /* 1.7 GHz */ - { 12000000, 1700000000, 850, 6, 1, 8}, - { 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */ - { 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */ - { 26000000, 1700000000, 850, 13, 1, 8}, + { 12000000, 1700000000, 850, 6, 0, 8}, + { 13000000, 1700000000, 915, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 16800000, 1700000000, 708, 7, 0, 8}, /* actual: 1699.2 MHz */ + { 19200000, 1700000000, 885, 10, 0, 8}, /* actual: 1699.2 MHz */ + { 26000000, 1700000000, 850, 13, 0, 8}, /* 1.6 GHz */ - { 12000000, 1600000000, 800, 6, 1, 8}, - { 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */ - { 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */ - { 19200000, 1600000000, 500, 6, 1, 8}, - { 26000000, 1600000000, 800, 13, 1, 8}, + { 12000000, 1600000000, 800, 6, 0, 8}, + { 13000000, 1600000000, 738, 6, 0, 8}, /* actual: 1599.0 MHz */ + { 16800000, 1600000000, 857, 9, 0, 8}, /* actual: 1599.7 MHz */ + { 19200000, 1600000000, 500, 6, 0, 8}, + { 26000000, 1600000000, 800, 13, 0, 8}, /* 1.5 GHz */ - { 12000000, 1500000000, 750, 6, 1, 8}, - { 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */ - { 16800000, 1500000000, 625, 7, 1, 8}, - { 19200000, 1500000000, 625, 8, 1, 8}, - { 26000000, 1500000000, 750, 13, 1, 8}, + { 12000000, 1500000000, 750, 6, 0, 8}, + { 13000000, 1500000000, 923, 8, 0, 8}, /* actual: 1499.8 MHz */ + { 16800000, 1500000000, 625, 7, 0, 8}, + { 19200000, 1500000000, 625, 8, 0, 8}, + { 26000000, 1500000000, 750, 13, 0, 8}, /* 1.4 GHz */ - { 12000000, 1400000000, 700, 6, 1, 8}, - { 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */ - { 16800000, 1400000000, 1000, 12, 1, 8}, - { 19200000, 1400000000, 875, 12, 1, 8}, - { 26000000, 1400000000, 700, 13, 1, 8}, + { 12000000, 1400000000, 700, 6, 0, 8}, + { 13000000, 1400000000, 969, 9, 0, 8}, /* actual: 1399.7 MHz */ + { 16800000, 1400000000, 1000, 12, 0, 8}, + { 19200000, 1400000000, 875, 12, 0, 8}, + { 26000000, 1400000000, 700, 13, 0, 8}, /* 1.3 GHz */ - { 12000000, 1300000000, 975, 9, 1, 8}, - { 13000000, 1300000000, 1000, 10, 1, 8}, - { 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */ - { 26000000, 1300000000, 650, 13, 1, 8}, + { 12000000, 1300000000, 975, 9, 0, 8}, + { 13000000, 1300000000, 1000, 10, 0, 8}, + { 16800000, 1300000000, 928, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 19200000, 1300000000, 812, 12, 0, 8}, /* actual: 1299.2 MHz */ + { 26000000, 1300000000, 650, 13, 0, 8}, /* 1.2 GHz */ - { 12000000, 1200000000, 1000, 10, 1, 8}, - { 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */ - { 16800000, 1200000000, 1000, 14, 1, 8}, - { 19200000, 1200000000, 1000, 16, 1, 8}, - { 26000000, 1200000000, 600, 13, 1, 8}, + { 12000000, 1200000000, 1000, 10, 0, 8}, + { 13000000, 1200000000, 923, 10, 0, 8}, /* actual: 1199.9 MHz */ + { 16800000, 1200000000, 1000, 14, 0, 8}, + { 19200000, 1200000000, 1000, 16, 0, 8}, + { 26000000, 1200000000, 600, 13, 0, 8}, /* 1.1 GHz */ - { 12000000, 1100000000, 825, 9, 1, 8}, - { 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */ - { 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */ - { 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */ - { 26000000, 1100000000, 550, 13, 1, 8}, + { 12000000, 1100000000, 825, 9, 0, 8}, + { 13000000, 1100000000, 846, 10, 0, 8}, /* actual: 1099.8 MHz */ + { 16800000, 1100000000, 982, 15, 0, 8}, /* actual: 1099.8 MHz */ + { 19200000, 1100000000, 859, 15, 0, 8}, /* actual: 1099.5 MHz */ + { 26000000, 1100000000, 550, 13, 0, 8}, /* 1 GHz */ - { 12000000, 1000000000, 1000, 12, 1, 8}, - { 13000000, 1000000000, 1000, 13, 1, 8}, - { 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */ - { 19200000, 1000000000, 625, 12, 1, 8}, - { 26000000, 1000000000, 1000, 26, 1, 8}, + { 12000000, 1000000000, 1000, 12, 0, 8}, + { 13000000, 1000000000, 1000, 13, 0, 8}, + { 16800000, 1000000000, 833, 14, 0, 8}, /* actual: 999.6 MHz */ + { 19200000, 1000000000, 625, 12, 0, 8}, + { 26000000, 1000000000, 1000, 26, 0, 8}, { 0, 0, 0, 0, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 3c566e2d518a..b9691ddcbd9b 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -182,12 +182,14 @@ struct tegra_clk_pll_params { * TEGRA_PLL_FIXED - We are not supposed to change output frequency * of some plls. * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the + * base register. */ struct tegra_clk_pll { struct clk_hw hw; void __iomem *clk_base; void __iomem *pmc; - u8 flags; + u32 flags; unsigned long fixed_rate; spinlock_t *lock; u8 divn_shift; @@ -210,18 +212,19 @@ struct tegra_clk_pll { #define TEGRA_PLLM BIT(5) #define TEGRA_PLL_FIXED BIT(6) #define TEGRA_PLLE_CONFIGURE BIT(7) +#define TEGRA_PLL_LOCK_MISC BIT(8) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, - struct tegra_clk_pll_params *pll_params, u8 pll_flags, + struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); /** From dd93587be8dc8acf23a0d8a23efc74a91d8f0dfe Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:37 +0300 Subject: [PATCH 56/68] clk: tegra: Add TEGRA_PLL_BYPASS flag Not all PLLs in Tegra114 have a bypass bit. Adapt the common code to only use this bit when available. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 12 ++++++++---- drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 3feefb15e473..4ee6d03bc575 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -171,7 +171,8 @@ static void _clk_pll_enable(struct clk_hw *hw) clk_pll_enable_lock(pll); val = pll_readl_base(pll); - val &= ~PLL_BASE_BYPASS; + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; val |= PLL_BASE_ENABLE; pll_writel_base(val, pll); @@ -188,7 +189,9 @@ static void _clk_pll_disable(struct clk_hw *hw) u32 val; val = pll_readl_base(pll); - val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); + if (pll->flags & TEGRA_PLL_BYPASS) + val &= ~PLL_BASE_BYPASS; + val &= ~PLL_BASE_ENABLE; pll_writel_base(val, pll); if (pll->flags & TEGRA_PLLM) { @@ -459,7 +462,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, val = pll_readl_base(pll); - if (val & PLL_BASE_BYPASS) + if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS)) return parent_rate; if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) { @@ -671,6 +674,7 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; + pll_flags |= TEGRA_PLL_BYPASS; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -692,8 +696,8 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, { struct tegra_clk_pll *pll; struct clk *clk; - pll_flags |= TEGRA_PLL_LOCK_MISC; + pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index b9691ddcbd9b..fff520ae72fc 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -184,6 +184,7 @@ struct tegra_clk_pll_params { * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the * base register. + * TEGRA_PLL_BYPASS - PLL has bypass bit */ struct tegra_clk_pll { struct clk_hw hw; @@ -213,6 +214,7 @@ struct tegra_clk_pll { #define TEGRA_PLL_FIXED BIT(6) #define TEGRA_PLLE_CONFIGURE BIT(7) #define TEGRA_PLL_LOCK_MISC BIT(8) +#define TEGRA_PLL_BYPASS BIT(9) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; From 7ba28813b41120dd67329fd04dc732ea7fef05a0 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:38 +0300 Subject: [PATCH 57/68] clk: tegra: introduce TEGRA_PLL_HAS_LOCK_ENABLE Tegra114 PLLC2 and PLLC3 don't have a lock enable bit. The lock bits are always functional. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 5 +++++ drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 4ee6d03bc575..eaab060cda24 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -108,6 +108,9 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) if (!(pll->flags & TEGRA_PLL_USE_LOCK)) return; + if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE)) + return; + val = pll_readl_misc(pll); val |= BIT(pll->params->lock_enable_bit_idx); pll_writel_misc(val, pll); @@ -675,6 +678,7 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, struct clk *clk; pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -698,6 +702,7 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, struct clk *clk; pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fff520ae72fc..17ddb22f7a50 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -185,6 +185,7 @@ struct tegra_clk_pll_params { * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the * base register. * TEGRA_PLL_BYPASS - PLL has bypass bit + * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring */ struct tegra_clk_pll { struct clk_hw hw; @@ -215,6 +216,7 @@ struct tegra_clk_pll { #define TEGRA_PLLE_CONFIGURE BIT(7) #define TEGRA_PLL_LOCK_MISC BIT(8) #define TEGRA_PLL_BYPASS BIT(9) +#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10) extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; From 0b6525acd13f2d33cd3be86d0dbd2ddd1ffeda8f Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:39 +0300 Subject: [PATCH 58/68] clk: tegra: Add PLL post divider table Some PLLs in Tegra114 don't use a power of 2 mapping for the post divider. Introduce a table based approach and switch PLLU to it. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 38 +++++++++++++++++++++++++++------ drivers/clk/tegra/clk-tegra20.c | 7 ++++++ drivers/clk/tegra/clk-tegra30.c | 7 ++++++ drivers/clk/tegra/clk.h | 13 +++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index eaab060cda24..ccb367ee7e78 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -266,6 +266,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; unsigned long cfreq; u32 p_div = 0; @@ -299,7 +300,6 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, cfg->output_rate <<= 1) p_div++; - cfg->p = p_div; cfg->m = parent_rate / cfreq; cfg->n = cfg->output_rate / cfreq; cfg->cpcon = OUT_OF_TABLE_CPCON; @@ -312,8 +312,19 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, return -EINVAL; } - if (pll->flags & TEGRA_PLLU) - cfg->p ^= 1; + if (p_tohw) { + p_div = 1 << p_div; + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) { + cfg->p = p_tohw->hw_val; + break; + } + p_tohw++; + } + if (!p_tohw->pdiv) + return -EINVAL; + } else + cfg->p = p_div; return 0; } @@ -460,8 +471,10 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; u32 val; u64 rate = parent_rate; + int pdiv; val = pll_readl_base(pll); @@ -480,10 +493,23 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - if (pll->flags & TEGRA_PLLU) - cfg.p ^= 1; + if (p_tohw) { + while (p_tohw->pdiv) { + if (cfg.p == p_tohw->hw_val) { + pdiv = p_tohw->pdiv; + break; + } + p_tohw++; + } - cfg.m *= 1 << cfg.p; + if (!p_tohw->pdiv) { + WARN_ON(1); + pdiv = 1; + } + } else + pdiv = 1 << cfg.p; + + cfg.m *= pdiv; rate *= cfg.n; do_div(rate, cfg.m); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index c2a1c4cae47c..f215bf10c9ff 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -441,6 +441,12 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_params pll_u_params = { .input_min = 2000000, .input_max = 40000000, @@ -453,6 +459,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_bit_idx = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 02609d125e0e..fe768fe769b2 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -467,6 +467,12 @@ static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { { 0, 0, 0, 0, 0, 0 }, }; +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { { 12000000, 480000000, 960, 12, 0, 12}, { 13000000, 480000000, 960, 13, 0, 12}, @@ -640,6 +646,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_bit_idx = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .pdiv_tohw = pllu_p, }; static struct tegra_clk_pll_params pll_x_params = { diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 17ddb22f7a50..925da451bd19 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -116,6 +116,17 @@ struct tegra_clk_pll_freq_table { u8 cpcon; }; +/** + * struct pdiv_map - map post divider to hw value + * + * @pdiv: post divider + * @hw_val: value to be written to the PLL hw + */ +struct pdiv_map { + u8 pdiv; + u8 hw_val; +}; + /** * struct clk_pll_params - PLL parameters * @@ -146,6 +157,8 @@ struct tegra_clk_pll_params { u32 lock_bit_idx; u32 lock_enable_bit_idx; int lock_delay; + int max_p; + struct pdiv_map *pdiv_tohw; }; /** From 3e72771e210348fbd7ff0ea1b9e14cd88380c05b Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:40 +0300 Subject: [PATCH 59/68] clk: tegra: move from a lock bit idx to a lock mask PLLC2 and PLLC3 on Tegra114 have separate phaselock and frequencylock bits. So switch to a lock mask to be able to test both at the same time. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 6 +++--- drivers/clk/tegra/clk-tegra20.c | 20 ++++++++++---------- drivers/clk/tegra/clk-tegra30.c | 22 +++++++++++----------- drivers/clk/tegra/clk.h | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index ccb367ee7e78..0b963522479b 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -119,7 +119,7 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll) static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) { int i; - u32 val, lock_bit; + u32 val, lock_mask; void __iomem *lock_addr; if (!(pll->flags & TEGRA_PLL_USE_LOCK)) { @@ -133,11 +133,11 @@ static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll) else lock_addr += pll->params->base_reg; - lock_bit = BIT(pll->params->lock_bit_idx); + lock_mask = pll->params->lock_mask; for (i = 0; i < pll->params->lock_delay; i++) { val = readl_relaxed(lock_addr); - if (val & lock_bit) { + if ((val & lock_mask) == lock_mask) { udelay(PLL_POST_LOCK_DELAY); return 0; } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f215bf10c9ff..5c7b58b96911 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -86,8 +86,8 @@ #define PLLE_BASE 0xe8 #define PLLE_MISC 0xec -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLL_MISC_LOCK_ENABLE 18 #define PLLDU_MISC_LOCK_ENABLE 22 @@ -380,7 +380,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -394,7 +394,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -408,7 +408,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -422,7 +422,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -436,7 +436,7 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -456,7 +456,7 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, @@ -471,7 +471,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1200000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -485,7 +485,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 0, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 0, }; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index fe768fe769b2..735f964edc65 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -116,8 +116,8 @@ #define PLLDU_MISC_LOCK_ENABLE 22 #define PLLE_MISC_LOCK_ENABLE 9 -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) #define PLLE_AUX 0x48c #define PLLC_OUT 0x84 @@ -559,7 +559,7 @@ static struct tegra_clk_pll_params pll_c_params = { .vco_max = 1400000000, .base_reg = PLLC_BASE, .misc_reg = PLLC_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -573,7 +573,7 @@ static struct tegra_clk_pll_params pll_m_params = { .vco_max = 1200000000, .base_reg = PLLM_BASE, .misc_reg = PLLM_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -587,7 +587,7 @@ static struct tegra_clk_pll_params pll_p_params = { .vco_max = 1400000000, .base_reg = PLLP_BASE, .misc_reg = PLLP_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -601,7 +601,7 @@ static struct tegra_clk_pll_params pll_a_params = { .vco_max = 1400000000, .base_reg = PLLA_BASE, .misc_reg = PLLA_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -615,7 +615,7 @@ static struct tegra_clk_pll_params pll_d_params = { .vco_max = 1000000000, .base_reg = PLLD_BASE, .misc_reg = PLLD_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -629,7 +629,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .vco_max = 1000000000, .base_reg = PLLD2_BASE, .misc_reg = PLLD2_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, }; @@ -643,7 +643,7 @@ static struct tegra_clk_pll_params pll_u_params = { .vco_max = 960000000, .base_reg = PLLU_BASE, .misc_reg = PLLU_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, @@ -658,7 +658,7 @@ static struct tegra_clk_pll_params pll_x_params = { .vco_max = 1700000000, .base_reg = PLLX_BASE, .misc_reg = PLLX_MISC, - .lock_bit_idx = PLL_BASE_LOCK, + .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, }; @@ -672,7 +672,7 @@ static struct tegra_clk_pll_params pll_e_params = { .vco_max = 2400000000U, .base_reg = PLLE_BASE, .misc_reg = PLLE_MISC, - .lock_bit_idx = PLLE_MISC_LOCK, + .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 925da451bd19..3b498e0c8ae5 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -154,7 +154,7 @@ struct tegra_clk_pll_params { u32 base_reg; u32 misc_reg; u32 lock_reg; - u32 lock_bit_idx; + u32 lock_mask; u32 lock_enable_bit_idx; int lock_delay; int max_p; From c1d1939c5163088e5f12011c6b3a6a9fab40215f Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:41 +0300 Subject: [PATCH 60/68] clk: tegra: Add new fields and PLL types for Tegra114 Tegra114 introduces new PLL types. This requires new clocktypes as well as some new fields in the pll structure. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-pll.c | 839 ++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk.h | 50 ++- 2 files changed, 888 insertions(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 0b963522479b..17c2cc086eb4 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -79,6 +79,48 @@ #define PLLE_SS_CTRL 0x68 #define PLLE_SS_DISABLE (7 << 10) +#define PLLE_AUX_PLLP_SEL BIT(2) +#define PLLE_AUX_ENABLE_SWCTL BIT(4) +#define PLLE_AUX_SEQ_ENABLE BIT(24) +#define PLLE_AUX_PLLRE_SEL BIT(28) + +#define PLLE_MISC_PLLE_PTS BIT(8) +#define PLLE_MISC_IDDQ_SW_VALUE BIT(13) +#define PLLE_MISC_IDDQ_SW_CTRL BIT(14) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4 +#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT 2 +#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define PLLCX_MISC_STROBE BIT(31) +#define PLLCX_MISC_RESET BIT(30) +#define PLLCX_MISC_SDM_DIV_SHIFT 28 +#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT) +#define PLLCX_MISC_FILT_DIV_SHIFT 26 +#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT) +#define PLLCX_MISC_ALPHA_SHIFT 18 +#define PLLCX_MISC_DIV_LOW_RANGE \ + ((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x1 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_DIV_HIGH_RANGE \ + ((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \ + (0x2 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_COEF_LOW_RANGE \ + ((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT)) +#define PLLCX_MISC_KA_SHIFT 2 +#define PLLCX_MISC_KB_SHIFT 9 +#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \ + (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \ + PLLCX_MISC_DIV_LOW_RANGE | \ + PLLCX_MISC_RESET) +#define PLLCX_MISC1_DEFAULT 0x000d2308 +#define PLLCX_MISC2_DEFAULT 0x30211200 +#define PLLCX_MISC3_DEFAULT 0x200 + +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 +#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) + #define PMC_SATA_PWRGT 0x1ac #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -101,6 +143,24 @@ #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ +#define PLLXC_PDIV_MAX 14 + +/* non-monotonic mapping below is not a typo */ +static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ + /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 +}; + +#define PLLCX_PDIV_MAX 7 +static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { + /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */ + /* p: */ 1, 2, 3, 4, 6, 8, 12, 16 +}; +#endif + static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { u32 val; @@ -646,6 +706,520 @@ const struct clk_ops tegra_clk_plle_ops = { .enable = clk_plle_enable, }; +#ifdef CONFIG_ARCH_TEGRA_114_SOC + +static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, + unsigned long parent_rate) +{ + if (parent_rate > pll_params->cf_max) + return 2; + else + return 1; +} + +static int clk_pll_iddq_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + u32 val; + int ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl(pll->params->iddq_reg, pll); + val &= ~BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + _clk_pll_enable(hw); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return 0; +} + +static void clk_pll_iddq_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl(pll->params->iddq_reg, pll); + val |= BIT(pll->params->iddq_bit_idx); + pll_writel(val, pll->params->iddq_reg, pll); + udelay(2); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned int p; + + if (!rate) + return -EINVAL; + + p = DIV_ROUND_UP(pll->params->vco_min, rate); + cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); + cfg->p = p; + cfg->output_rate = rate * cfg->p; + cfg->n = cfg->output_rate * cfg->m / parent_rate; + + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) + return -EINVAL; + + return 0; +} + +static int _pll_ramp_calc_pll(struct clk_hw *hw, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + int err = 0; + + err = _get_table_rate(hw, cfg, rate, parent_rate); + if (err < 0) + err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); + else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { + WARN_ON(1); + err = -EINVAL; + goto out; + } + + if (!cfg->p || (cfg->p > pll->params->max_p)) + err = -EINVAL; + +out: + return err; +} + +static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table cfg, old_cfg; + unsigned long flags = 0; + int ret = 0; + u8 old_p; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + return ret; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _get_pll_mnp(pll, &old_cfg); + + old_p = pllxc_p[old_cfg.p]; + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { + cfg.p -= 1; + ret = _program_pll(hw, &cfg, rate); + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll_freq_table cfg; + int ret = 0; + u64 output_rate = *prate; + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); + if (ret < 0) + return ret; + + output_rate *= cfg.n; + do_div(output_rate, cfg.m * cfg.p); + + return output_rate; +} + +static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + state = clk_pll_is_enabled(hw); + if (state) { + if (rate != clk_get_rate(hw->clk)) { + pr_err("%s: Cannot change active PLLM\n", __func__); + ret = -EINVAL; + goto out; + } + goto out; + } + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + cfg.p -= 1; + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : + (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + + val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); + val &= ~(divn_mask(pll) | divm_mask(pll)); + val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); + writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); + } else + _update_pll_mnp(pll, &cfg); + + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _pllcx_strobe(struct tegra_clk_pll *pll) +{ + u32 val; + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); + udelay(2); + + val &= ~PLLCX_MISC_STROBE; + pll_writel_misc(val, pll); +} + +static int clk_pllc_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + int ret = 0; + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_enable(hw); + udelay(2); + + val = pll_readl_misc(pll); + val &= ~PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); + + _pllcx_strobe(pll); + + ret = clk_pll_wait_for_lock(pll); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void _clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + u32 val; + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLCX_MISC_RESET; + pll_writel_misc(val, pll); + udelay(2); +} + +static void clk_pllc_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pllc_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, + unsigned long input_rate, u32 n) +{ + u32 val, n_threshold; + + switch (input_rate) { + case 12000000: + n_threshold = 70; + break; + case 13000000: + case 26000000: + n_threshold = 71; + break; + case 16800000: + n_threshold = 55; + break; + case 19200000: + n_threshold = 48; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, input_rate); + return -EINVAL; + } + + val = pll_readl_misc(pll); + val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK); + val |= n <= n_threshold ? + PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE; + pll_writel_misc(val, pll); + + return 0; +} + +static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + u32 val; + u16 old_m, old_n; + u8 old_p; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); + if (ret < 0) + goto out; + + val = pll_readl_base(pll); + old_m = (val >> pll->divm_shift) & (divm_mask(pll)); + old_n = (val >> pll->divn_shift) & (divn_mask(pll)); + old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + + if (cfg.m != old_m) { + WARN_ON(1); + goto out; + } + + if (old_n == cfg.n && old_p == cfg.p) + goto out; + + cfg.p -= 1; + + state = clk_pll_is_enabled(hw); + if (state) + _clk_pllc_disable(hw); + + ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + if (ret < 0) + goto out; + + _update_pll_mnp(pll, &cfg); + + if (state) + ret = clk_pllc_enable(hw); + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static long _pllre_calc_rate(struct tegra_clk_pll *pll, + struct tegra_clk_pll_freq_table *cfg, + unsigned long rate, unsigned long parent_rate) +{ + u16 m, n; + u64 output_rate = parent_rate; + + m = _pll_fixed_mdiv(pll->params, parent_rate); + n = rate * m / parent_rate; + + output_rate *= n; + do_div(output_rate, m); + + if (cfg) { + cfg->m = m; + cfg->n = n; + } + + return output_rate; +} +static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg, old_cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + int state, ret = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _pllre_calc_rate(pll, &cfg, rate, parent_rate); + _get_pll_mnp(pll, &old_cfg); + cfg.p = old_cfg.p; + + if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) { + state = clk_pll_is_enabled(hw); + if (state) + _clk_pll_disable(hw); + + _update_pll_mnp(pll, &cfg); + + if (state) { + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + } + } + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll *pll = to_clk_pll(hw); + u64 rate = parent_rate; + + _get_pll_mnp(pll, &cfg); + + rate *= cfg.n; + do_div(rate, cfg.m); + + return rate; +} + +static long clk_pllre_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + + return _pllre_calc_rate(pll, NULL, rate, *prate); +} + +static int clk_plle_tegra114_enable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct tegra_clk_pll_freq_table sel; + u32 val; + int ret; + unsigned long flags = 0; + unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk)); + + if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate)) + return -EINVAL; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + val = pll_readl_base(pll); + val &= ~BIT(29); /* Disable lock override */ + pll_writel_base(val, pll); + + val = pll_readl(pll->params->aux_reg, pll); + val |= PLLE_AUX_ENABLE_SWCTL; + val &= ~PLLE_AUX_SEQ_ENABLE; + pll_writel(val, pll->params->aux_reg, pll); + udelay(1); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_LOCK_ENABLE; + val |= PLLE_MISC_IDDQ_SW_CTRL; + val &= ~PLLE_MISC_IDDQ_SW_VALUE; + val |= PLLE_MISC_PLLE_PTS; + val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK; + pll_writel_misc(val, pll); + udelay(5); + + val = pll_readl(PLLE_SS_CTRL, pll); + val |= PLLE_SS_DISABLE; + pll_writel(val, PLLE_SS_CTRL, pll); + + val = pll_readl_base(pll); + val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); + val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); + val |= sel.m << pll->divm_shift; + val |= sel.n << pll->divn_shift; + val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; + pll_writel_base(val, pll); + udelay(1); + + _clk_pll_enable(hw); + ret = clk_pll_wait_for_lock(pll); + + if (ret < 0) + goto out; + + /* TODO: enable hw control of xusb brick pll */ + +out: + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void clk_plle_tegra114_disable(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + u32 val; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + _clk_pll_disable(hw); + + val = pll_readl_misc(pll); + val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE; + pll_writel_misc(val, pll); + udelay(1); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} +#endif + static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, void __iomem *pmc, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, @@ -741,3 +1315,268 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, return clk; } + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +const struct clk_ops tegra_clk_pllxc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllxc_set_rate, +}; + +const struct clk_ops tegra_clk_pllm_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllm_set_rate, +}; + +const struct clk_ops tegra_clk_pllc_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pllc_enable, + .disable = clk_pllc_disable, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_ramp_round_rate, + .set_rate = clk_pllc_set_rate, +}; + +const struct clk_ops tegra_clk_pllre_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_iddq_enable, + .disable = clk_pll_iddq_disable, + .recalc_rate = clk_pllre_recalc_rate, + .round_rate = clk_pllre_round_rate, + .set_rate = clk_pllre_set_rate, +}; + +const struct clk_ops tegra_clk_plle_tegra114_ops = { + .is_enabled = clk_pll_is_enabled, + .enable = clk_plle_tegra114_enable, + .disable = clk_plle_tegra114_disable, + .recalc_rate = clk_pll_recalc_rate, +}; + + +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllxc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate) +{ + u32 val; + struct tegra_clk_pll *pll; + struct clk *clk; + + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* program minimum rate by default */ + + val = pll_readl_base(pll); + if (val & PLL_BASE_ENABLE) + WARN_ON(val & pll_params->iddq_bit_idx); + else { + int m; + + m = _pll_fixed_mdiv(pll_params, parent_rate); + val = m << PLL_BASE_DIVM_SHIFT; + val |= (pll_params->vco_min / parent_rate) + << PLL_BASE_DIVN_SHIFT; + pll_writel_base(val, pll); + } + + /* disable lock override */ + + val = pll_readl_misc(pll); + val &= ~BIT(29); + pll_writel_misc(val, pll); + + pll_flags |= TEGRA_PLL_LOCK_MISC; + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllre_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + + if (!pll_params->pdiv_tohw) + return ERR_PTR(-EINVAL); + + pll_flags |= TEGRA_PLL_BYPASS; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllm_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct clk *parent, *clk; + struct pdiv_map *p_tohw = pll_params->pdiv_tohw; + struct tegra_clk_pll *pll; + struct tegra_clk_pll_freq_table cfg; + unsigned long parent_rate; + + if (!p_tohw) + return ERR_PTR(-EINVAL); + + parent = __clk_lookup(parent_name); + if (IS_ERR(parent)) { + WARN(1, "parent clk %s of %s must be registered first\n", + name, parent_name); + return ERR_PTR(-EINVAL); + } + + pll_flags |= TEGRA_PLL_BYPASS; + pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, + freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + parent_rate = __clk_get_rate(parent); + + /* + * Most of PLLC register fields are shadowed, and can not be read + * directly from PLL h/w. Hence, actual PLLC boot state is unknown. + * Initialize PLL to default state: disabled, reset; shadow registers + * loaded with default parameters; dividers are preset for half of + * minimum VCO rate (the latter assured that shadowed divider settings + * are within supported range). + */ + + cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); + cfg.n = cfg.m * pll_params->vco_min / parent_rate; + + while (p_tohw->pdiv) { + if (p_tohw->pdiv == 2) { + cfg.p = p_tohw->hw_val; + break; + } + p_tohw++; + } + + if (!p_tohw->pdiv) { + WARN_ON(1); + return ERR_PTR(-EINVAL); + } + + pll_writel_base(0, pll); + _update_pll_mnp(pll, &cfg); + + pll_writel_misc(PLLCX_MISC_DEFAULT, pll); + pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll); + pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll); + pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll); + + _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_pllc_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock) +{ + struct tegra_clk_pll *pll; + struct clk *clk; + u32 val, val_aux; + + pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params, + TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); + + /* ensure parent is set to pll_re_vco */ + + val = pll_readl_base(pll); + val_aux = pll_readl(pll_params->aux_reg, pll); + + if (val & PLL_BASE_ENABLE) { + if (!(val_aux & PLLE_AUX_PLLRE_SEL)) + WARN(1, "pll_e enabled with unsupported parent %s\n", + (val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref"); + } else { + val_aux |= PLLE_AUX_PLLRE_SEL; + pll_writel(val, pll_params->aux_reg, pll); + } + + clk = _tegra_clk_register_pll(pll, name, parent_name, flags, + &tegra_clk_plle_tegra114_ops); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} +#endif diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 3b498e0c8ae5..8cedb092a239 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it @@ -156,6 +156,13 @@ struct tegra_clk_pll_params { u32 lock_reg; u32 lock_mask; u32 lock_enable_bit_idx; + u32 iddq_reg; + u32 iddq_bit_idx; + u32 aux_reg; + u32 dyn_ramp_reg; + u32 ext_misc_reg[3]; + int stepa_shift; + int stepb_shift; int lock_delay; int max_p; struct pdiv_map *pdiv_tohw; @@ -238,12 +245,53 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); + struct clk *tegra_clk_register_plle(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, unsigned long fixed_rate, struct tegra_clk_pll_params *pll_params, u32 pll_flags, struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, + void __iomem *clk_base, void __iomem *pmc, + unsigned long flags, unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + u32 pll_flags, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock, unsigned long parent_rate); + +struct clk *tegra_clk_register_plle_tegra114(const char *name, + const char *parent_name, + void __iomem *clk_base, unsigned long flags, + unsigned long fixed_rate, + struct tegra_clk_pll_params *pll_params, + struct tegra_clk_pll_freq_table *freq_table, + spinlock_t *lock); + /** * struct tegra_clk_pll_out - PLL divider down clock * From a26a029893096204f08a3ff5e262f99e1a75e273 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:42 +0300 Subject: [PATCH 61/68] clk: tegra: Add flags to tegra_clk_periph() We will need some tegra peripheral clocks with the CLK_IGNORE_UNUSED flag, most notably mselect, which is a bridge between AXI and most peripherals. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph.c | 11 ++++++----- drivers/clk/tegra/clk-tegra20.c | 2 +- drivers/clk/tegra/clk-tegra30.c | 2 +- drivers/clk/tegra/clk.h | 9 ++++++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 9dbd301ace77..b2309d37a963 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -173,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = { static struct clk *_tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, - void __iomem *clk_base, u32 offset, bool div) + void __iomem *clk_base, u32 offset, bool div, + unsigned long flags) { struct clk *clk; struct clk_init_data init; init.name = name; init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; - init.flags = div ? 0 : CLK_SET_RATE_PARENT; + init.flags = flags; init.parent_names = parent_names; init.num_parents = num_parents; @@ -205,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name, struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset) + u32 offset, unsigned long flags) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, true); + periph, clk_base, offset, true, flags); } struct clk *tegra_clk_register_periph_nodiv(const char *name, @@ -217,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, u32 offset) { return _tegra_clk_register_periph(name, parent_names, num_parents, - periph, clk_base, offset, false); + periph, clk_base, offset, false, CLK_SET_RATE_PARENT); } diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 5c7b58b96911..b0405b67f49c 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1019,7 +1019,7 @@ static void __init tegra20_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 735f964edc65..2dc0c5602613 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1668,7 +1668,7 @@ static void __init tegra30_periph_clk_init(void) data = &tegra_periph_clk_list[i]; clk = tegra_clk_register_periph(data->name, data->parent_names, data->num_parents, &data->periph, - clk_base, data->offset); + clk_base, data->offset, data->flags); clk_register_clkdev(clk, data->con_id, data->dev_id); clks[data->clk_id] = clk; } diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 8cedb092a239..fd12b77c985d 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -417,7 +417,7 @@ extern const struct clk_ops tegra_clk_periph_ops; struct clk *tegra_clk_register_periph(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, - u32 offset); + u32 offset, unsigned long flags); struct clk *tegra_clk_register_periph_nodiv(const char *name, const char **parent_names, int num_parents, struct tegra_clk_periph *periph, void __iomem *clk_base, @@ -460,12 +460,14 @@ struct tegra_periph_init_data { u32 offset; const char *con_id; const char *dev_id; + unsigned long flags; }; #define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\ + _flags) \ { \ .name = _name, \ .clk_id = _clk_id, \ @@ -480,6 +482,7 @@ struct tegra_periph_init_data { .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ + .flags = _flags \ } #define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ @@ -490,7 +493,7 @@ struct tegra_periph_init_data { _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ _div_shift, _div_width, _div_frac_width, _div_flags, \ _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ - NULL) + NULL, 0) /** * struct clk_super_mux - super clock From fdcccbd804088eb96881c9f6532de04868f9dbc1 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:44 +0300 Subject: [PATCH 62/68] clk: tegra: Workaround for Tegra114 MSENC problem Workaround a hardware bug in MSENC during clock enable. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-periph-gate.c | 9 +++++++++ drivers/clk/tegra/clk.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index d87e1cece9fa..bafee9895a24 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -43,6 +43,8 @@ static DEFINE_SPINLOCK(periph_ref_lock); #define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) +#define LVL2_CLK_GATE_OVRE 0x554 + /* Peripheral gate clock ops */ static int clk_periph_is_enabled(struct clk_hw *hw) { @@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw) } } + if (gate->flags & TEGRA_PERIPH_WAR_1005168) { + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE); + udelay(1); + writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); + } + spin_unlock_irqrestore(&periph_ref_lock, flags); return 0; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fd12b77c985d..fb48f0467b9d 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -358,6 +358,7 @@ struct tegra_clk_periph_regs { * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the * bus to flush the write operation in apb bus. This flag indicates * that this peripheral is in apb bus. + * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug */ struct tegra_clk_periph_gate { u32 magic; @@ -377,6 +378,7 @@ struct tegra_clk_periph_gate { #define TEGRA_PERIPH_NO_RESET BIT(0) #define TEGRA_PERIPH_MANUAL_RESET BIT(1) #define TEGRA_PERIPH_ON_APB BIT(2) +#define TEGRA_PERIPH_WAR_1005168 BIT(3) void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert); extern const struct clk_ops tegra_clk_periph_gate_ops; From 16d50f4da4123c04ef9ee2097668a80268147b37 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 4 Apr 2013 20:48:28 +0300 Subject: [PATCH 63/68] ARM: tegra: Define Tegra114 CAR binding The device tree binding models Tegra114 CAR (Clock And Reset) as a single monolithic clock provider. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- .../bindings/clock/nvidia,tegra114-car.txt | 303 ++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/nvidia,tegra114-car.txt diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra114-car.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra114-car.txt new file mode 100644 index 000000000000..d6cb083b90a2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra114-car.txt @@ -0,0 +1,303 @@ +NVIDIA Tegra114 Clock And Reset Controller + +This binding uses the common clock binding: +Documentation/devicetree/bindings/clock/clock-bindings.txt + +The CAR (Clock And Reset) Controller on Tegra is the HW module responsible +for muxing and gating Tegra's clocks, and setting their rates. + +Required properties : +- compatible : Should be "nvidia,tegra114-car" +- reg : Should contain CAR registers location and length +- clocks : Should contain phandle and clock specifiers for two clocks: + the 32 KHz "32k_in", and the board-specific oscillator "osc". +- #clock-cells : Should be 1. + In clock consumers, this cell represents the clock ID exposed by the CAR. + + The first 160 clocks are numbered to match the bits in the CAR's CLK_OUT_ENB + registers. These IDs often match those in the CAR's RST_DEVICES registers, + but not in all cases. Some bits in CLK_OUT_ENB affect multiple clocks. In + this case, those clocks are assigned IDs above 160 in order to highlight + this issue. Implementations that interpret these clock IDs as bit values + within the CLK_OUT_ENB or RST_DEVICES registers should be careful to + explicitly handle these special cases. + + The balance of the clocks controlled by the CAR are assigned IDs of 160 and + above. + + 0 unassigned + 1 unassigned + 2 unassigned + 3 unassigned + 4 rtc + 5 timer + 6 uarta + 7 unassigned (register bit affects uartb and vfir) + 8 unassigned + 9 sdmmc2 + 10 unassigned (register bit affects spdif_in and spdif_out) + 11 i2s1 + 12 i2c1 + 13 ndflash + 14 sdmmc1 + 15 sdmmc4 + 16 unassigned + 17 pwm + 18 i2s2 + 19 epp + 20 unassigned (register bit affects vi and vi_sensor) + 21 2d + 22 usbd + 23 isp + 24 3d + 25 unassigned + 26 disp2 + 27 disp1 + 28 host1x + 29 vcp + 30 i2s0 + 31 unassigned + + 32 unassigned + 33 unassigned + 34 apbdma + 35 unassigned + 36 kbc + 37 unassigned + 38 unassigned + 39 unassigned (register bit affects fuse and fuse_burn) + 40 kfuse + 41 sbc1 + 42 nor + 43 unassigned + 44 sbc2 + 45 unassigned + 46 sbc3 + 47 i2c5 + 48 dsia + 49 unassigned + 50 mipi + 51 hdmi + 52 csi + 53 unassigned + 54 i2c2 + 55 uartc + 56 mipi-cal + 57 emc + 58 usb2 + 59 usb3 + 60 msenc + 61 vde + 62 bsea + 63 bsev + + 64 unassigned + 65 uartd + 66 unassigned + 67 i2c3 + 68 sbc4 + 69 sdmmc3 + 70 unassigned + 71 owr + 72 afi + 73 csite + 74 unassigned + 75 unassigned + 76 la + 77 trace + 78 soc_therm + 79 dtv + 80 ndspeed + 81 i2cslow + 82 dsib + 83 tsec + 84 unassigned + 85 unassigned + 86 unassigned + 87 unassigned + 88 unassigned + 89 xusb_host + 90 unassigned + 91 msenc + 92 csus + 93 unassigned + 94 unassigned + 95 unassigned (bit affects xusb_dev and xusb_dev_src) + + 96 unassigned + 97 unassigned + 98 unassigned + 99 mselect + 100 tsensor + 101 i2s3 + 102 i2s4 + 103 i2c4 + 104 sbc5 + 105 sbc6 + 106 d_audio + 107 apbif + 108 dam0 + 109 dam1 + 110 dam2 + 111 hda2codec_2x + 112 unassigned + 113 audio0_2x + 114 audio1_2x + 115 audio2_2x + 116 audio3_2x + 117 audio4_2x + 118 spdif_2x + 119 actmon + 120 extern1 + 121 extern2 + 122 extern3 + 123 unassigned + 124 unassigned + 125 hda + 126 unassigned + 127 se + + 128 hda2hdmi + 129 unassigned + 130 unassigned + 131 unassigned + 132 unassigned + 133 unassigned + 134 unassigned + 135 unassigned + 136 unassigned + 137 unassigned + 138 unassigned + 139 unassigned + 140 unassigned + 141 unassigned + 142 unassigned + 143 unassigned (bit affects xusb_falcon_src, xusb_fs_src, + xusb_host_src and xusb_ss_src) + 144 cilab + 145 cilcd + 146 cile + 147 dsialp + 148 dsiblp + 149 unassigned + 150 dds + 151 unassigned + 152 dp2 + 153 amx + 154 adx + 155 unassigned (bit affects dfll_ref and dfll_soc) + 156 xusb_ss + + 192 uartb + 193 vfir + 194 spdif_in + 195 spdif_out + 196 vi + 197 vi_sensor + 198 fuse + 199 fuse_burn + 200 clk_32k + 201 clk_m + 202 clk_m_div2 + 203 clk_m_div4 + 204 pll_ref + 205 pll_c + 206 pll_c_out1 + 207 pll_c2 + 208 pll_c3 + 209 pll_m + 210 pll_m_out1 + 211 pll_p + 212 pll_p_out1 + 213 pll_p_out2 + 214 pll_p_out3 + 215 pll_p_out4 + 216 pll_a + 217 pll_a_out0 + 218 pll_d + 219 pll_d_out0 + 220 pll_d2 + 221 pll_d2_out0 + 222 pll_u + 223 pll_u_480M + 224 pll_u_60M + 225 pll_u_48M + 226 pll_u_12M + 227 pll_x + 228 pll_x_out0 + 229 pll_re_vco + 230 pll_re_out + 231 pll_e_out0 + 232 spdif_in_sync + 233 i2s0_sync + 234 i2s1_sync + 235 i2s2_sync + 236 i2s3_sync + 237 i2s4_sync + 238 vimclk_sync + 239 audio0 + 240 audio1 + 241 audio2 + 242 audio3 + 243 audio4 + 244 spdif + 245 clk_out_1 + 246 clk_out_2 + 247 clk_out_3 + 248 blink + 252 xusb_host_src + 253 xusb_falcon_src + 254 xusb_fs_src + 255 xusb_ss_src + 256 xusb_dev_src + 257 xusb_dev + 258 xusb_hs_src + 259 sclk + 260 hclk + 261 pclk + 262 cclk_g + 263 cclk_lp + 264 dfll_ref + 265 dfll_soc + +Example SoC include file: + +/ { + tegra_car: clock { + compatible = "nvidia,tegra114-car"; + reg = <0x60006000 0x1000>; + #clock-cells = <1>; + }; + + usb@c5004000 { + clocks = <&tegra_car 58>; /* usb2 */ + }; +}; + +Example board file: + +/ { + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + osc: clock@0 { + compatible = "fixed-clock"; + reg = <0>; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; + + clk_32k: clock@1 { + compatible = "fixed-clock"; + reg = <1>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + + &tegra_car { + clocks = <&clk_32k> <&osc>; + }; +}; From 2cb5efefd6f7d3e7df9a7430b910a80515821256 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:45 +0300 Subject: [PATCH 64/68] clk: tegra: Implement clocks for Tegra114 Implement clocks for Tegra114. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/Makefile | 1 + drivers/clk/tegra/clk-tegra114.c | 2085 ++++++++++++++++++++++++++++++ 2 files changed, 2086 insertions(+) create mode 100644 drivers/clk/tegra/clk-tegra114.c diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 2b41b0f4f731..f49fac2d193a 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -9,3 +9,4 @@ obj-y += clk-super.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c new file mode 100644 index 000000000000..e2a7fa16c5de --- /dev/null +++ b/drivers/clk/tegra/clk-tegra114.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define RST_DEVICES_X 0x28C +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_SET_V 0x430 +#define RST_DEVICES_CLR_V 0x434 +#define RST_DEVICES_SET_W 0x438 +#define RST_DEVICES_CLR_W 0x43c +#define RST_DEVICES_NUM 5 + +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CLK_OUT_ENB_X 0x280 +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_SET_V 0x440 +#define CLK_OUT_ENB_CLR_V 0x444 +#define CLK_OUT_ENB_SET_W 0x448 +#define CLK_OUT_ENB_CLR_W 0x44c +#define CLK_OUT_ENB_SET_X 0x284 +#define CLK_OUT_ENB_CLR_X 0x288 +#define CLK_OUT_ENB_NUM 6 + +#define PLLC_BASE 0x80 +#define PLLC_MISC2 0x88 +#define PLLC_MISC 0x8c +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc +#define PLLC3_MISC 0x500 +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLC_MISC_LOCK_ENABLE 24 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 +#define PLLRE_MISC_LOCK_ENABLE 30 + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 + +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) +#define PLLRE_MISC_LOCK BIT(24) +#define PLLCX_BASE_LOCK (BIT(26)|BIT(27)) + +#define PLLE_AUX 0x48c +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define AUDIO_SYNC_CLK_I2S0 0x4a0 +#define AUDIO_SYNC_CLK_I2S1 0x4a4 +#define AUDIO_SYNC_CLK_I2S2 0x4a8 +#define AUDIO_SYNC_CLK_I2S3 0x4ac +#define AUDIO_SYNC_CLK_I2S4 0x4b0 +#define AUDIO_SYNC_CLK_SPDIF 0x4b4 + +#define AUDIO_SYNC_DOUBLER 0x49c + +#define PMC_CLK_OUT_CNTRL 0x1a8 +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_CTRL 0 +#define PMC_CTRL_BLINK_ENB 7 + +#define OSC_CTRL 0x50 +#define OSC_CTRL_OSC_FREQ_SHIFT 28 +#define OSC_CTRL_PLL_REF_DIV_SHIFT 26 + +#define PLLXC_SW_MAX_P 6 + +#define CCLKG_BURST_POLICY 0x368 +#define CCLKLP_BURST_POLICY 0x370 +#define SCLK_BURST_POLICY 0x028 +#define SYSTEM_CLK_RATE 0x030 + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4) + +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) + +#define UTMIPLL_HW_PWRDN_CFG0 0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0) + +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_HDA 0x428 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_SBC5 0x3c8 +#define CLK_SOURCE_SBC6 0x3cc +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_NDSPEED 0x3f8 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_TRACE 0x634 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_UARTA_DBG 0x178 +#define CLK_SOURCE_UARTB_DBG 0x17c +#define CLK_SOURCE_UARTC_DBG 0x1a0 +#define CLK_SOURCE_UARTD_DBG 0x1c0 +#define CLK_SOURCE_UARTE_DBG 0x1c4 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIALP 0x620 +#define CLK_SOURCE_DSIBLP 0x624 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_D_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTERN1 0x3ec +#define CLK_SOURCE_EXTERN2 0x3f0 +#define CLK_SOURCE_EXTERN3 0x3f4 +#define CLK_SOURCE_I2CSLOW 0x3fc +#define CLK_SOURCE_SE 0x42c +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_XUSB_HOST_SRC 0x600 +#define CLK_SOURCE_XUSB_FALCON_SRC 0x604 +#define CLK_SOURCE_XUSB_FS_SRC 0x608 +#define CLK_SOURCE_XUSB_SS_SRC 0x610 +#define CLK_SOURCE_XUSB_DEV_SRC 0x60c +#define CLK_SOURCE_EMC 0x19c + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; + +static DEFINE_SPINLOCK(pll_d_lock); +static DEFINE_SPINLOCK(pll_d2_lock); +static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(pll_div_lock); +static DEFINE_SPINLOCK(pll_re_lock); +static DEFINE_SPINLOCK(clk_doubler_lock); +static DEFINE_SPINLOCK(clk_out_lock); +static DEFINE_SPINLOCK(sysrate_lock); + +static struct pdiv_map pllxc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 3, .hw_val = 2 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 5, .hw_val = 4 }, + { .pdiv = 6, .hw_val = 5 }, + { .pdiv = 8, .hw_val = 6 }, + { .pdiv = 10, .hw_val = 7 }, + { .pdiv = 12, .hw_val = 8 }, + { .pdiv = 16, .hw_val = 9 }, + { .pdiv = 12, .hw_val = 10 }, + { .pdiv = 16, .hw_val = 11 }, + { .pdiv = 20, .hw_val = 12 }, + { .pdiv = 24, .hw_val = 13 }, + { .pdiv = 32, .hw_val = 14 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { + { 12000000, 624000000, 104, 0, 2}, + { 12000000, 600000000, 100, 0, 2}, + { 13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + { 16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + { 19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + { 26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_params pll_c_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 600000000, + .vco_max = 1400000000, + .base_reg = PLLC_BASE, + .misc_reg = PLLC_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLC_MISC, + .iddq_bit_idx = PLLC_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLC_MISC2, + .stepa_shift = 17, + .stepb_shift = 9, + .pdiv_tohw = pllxc_p, +}; + +static struct pdiv_map pllc_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 4, .hw_val = 3 }, + { .pdiv = 8, .hw_val = 5 }, + { .pdiv = 16, .hw_val = 7 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = { + {12000000, 600000000, 100, 0, 2}, + {13000000, 600000000, 92, 0, 2}, /* actual: 598.0 MHz */ + {16800000, 600000000, 71, 0, 2}, /* actual: 596.4 MHz */ + {19200000, 600000000, 62, 0, 2}, /* actual: 595.2 MHz */ + {26000000, 600000000, 92, 1, 2}, /* actual: 598.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_c2_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC2_BASE, + .misc_reg = PLLC2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x4f0, + .ext_misc_reg[1] = 0x4f4, + .ext_misc_reg[2] = 0x4f8, +}; + +static struct tegra_clk_pll_params pll_c3_params = { + .input_min = 12000000, + .input_max = 48000000, + .cf_min = 12000000, + .cf_max = 19200000, + .vco_min = 600000000, + .vco_max = 1200000000, + .base_reg = PLLC3_BASE, + .misc_reg = PLLC3_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .pdiv_tohw = pllc_p, + .ext_misc_reg[0] = 0x504, + .ext_misc_reg[1] = 0x508, + .ext_misc_reg[2] = 0x50c, +}; + +static struct pdiv_map pllm_p[] = { + { .pdiv = 1, .hw_val = 0 }, + { .pdiv = 2, .hw_val = 1 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { + {12000000, 800000000, 66, 0, 1}, /* actual: 792.0 MHz */ + {13000000, 800000000, 61, 0, 1}, /* actual: 793.0 MHz */ + {16800000, 800000000, 47, 0, 1}, /* actual: 789.6 MHz */ + {19200000, 800000000, 41, 0, 1}, /* actual: 787.2 MHz */ + {26000000, 800000000, 61, 1, 1}, /* actual: 793.0 MHz */ + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_m_params = { + .input_min = 12000000, + .input_max = 500000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 400000000, + .vco_max = 1066000000, + .base_reg = PLLM_BASE, + .misc_reg = PLLM_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .max_p = 2, + .pdiv_tohw = pllm_p, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { + {12000000, 216000000, 432, 12, 1, 8}, + {13000000, 216000000, 432, 13, 1, 8}, + {16800000, 216000000, 360, 14, 1, 8}, + {19200000, 216000000, 360, 16, 1, 8}, + {26000000, 216000000, 432, 26, 1, 8}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_p_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLP_BASE, + .misc_reg = PLLP_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { + {9600000, 282240000, 147, 5, 0, 4}, + {9600000, 368640000, 192, 5, 0, 4}, + {9600000, 240000000, 200, 8, 0, 8}, + + {28800000, 282240000, 245, 25, 0, 8}, + {28800000, 368640000, 320, 25, 0, 8}, + {28800000, 240000000, 200, 24, 0, 8}, + {0, 0, 0, 0, 0, 0}, +}; + + +static struct tegra_clk_pll_params pll_a_params = { + .input_min = 2000000, + .input_max = 31000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 200000000, + .vco_max = 700000000, + .base_reg = PLLA_BASE, + .misc_reg = PLLA_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { + {12000000, 216000000, 864, 12, 2, 12}, + {13000000, 216000000, 864, 13, 2, 12}, + {16800000, 216000000, 720, 14, 2, 12}, + {19200000, 216000000, 720, 16, 2, 12}, + {26000000, 216000000, 864, 26, 2, 12}, + + {12000000, 594000000, 594, 12, 0, 12}, + {13000000, 594000000, 594, 13, 0, 12}, + {16800000, 594000000, 495, 14, 0, 12}, + {19200000, 594000000, 495, 16, 0, 12}, + {26000000, 594000000, 594, 26, 0, 12}, + + {12000000, 1000000000, 1000, 12, 0, 12}, + {13000000, 1000000000, 1000, 13, 0, 12}, + {19200000, 1000000000, 625, 12, 0, 12}, + {26000000, 1000000000, 1000, 26, 0, 12}, + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_d_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD_BASE, + .misc_reg = PLLD_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_d2_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 500000000, + .vco_max = 1000000000, + .base_reg = PLLD2_BASE, + .misc_reg = PLLD2_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, +}; + +static struct pdiv_map pllu_p[] = { + { .pdiv = 1, .hw_val = 1 }, + { .pdiv = 2, .hw_val = 0 }, + { .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { + {12000000, 480000000, 960, 12, 0, 12}, + {13000000, 480000000, 960, 13, 0, 12}, + {16800000, 480000000, 400, 7, 0, 5}, + {19200000, 480000000, 200, 4, 0, 3}, + {26000000, 480000000, 960, 26, 0, 12}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_u_params = { + .input_min = 2000000, + .input_max = 40000000, + .cf_min = 1000000, + .cf_max = 6000000, + .vco_min = 480000000, + .vco_max = 960000000, + .base_reg = PLLU_BASE, + .misc_reg = PLLU_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, + .lock_delay = 1000, + .pdiv_tohw = pllu_p, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { + /* 1 GHz */ + {12000000, 1000000000, 83, 0, 1}, /* actual: 996.0 MHz */ + {13000000, 1000000000, 76, 0, 1}, /* actual: 988.0 MHz */ + {16800000, 1000000000, 59, 0, 1}, /* actual: 991.2 MHz */ + {19200000, 1000000000, 52, 0, 1}, /* actual: 998.4 MHz */ + {26000000, 1000000000, 76, 1, 1}, /* actual: 988.0 MHz */ + + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_x_params = { + .input_min = 12000000, + .input_max = 800000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */ + .vco_min = 700000000, + .vco_max = 2400000000U, + .base_reg = PLLX_BASE, + .misc_reg = PLLX_MISC, + .lock_mask = PLL_BASE_LOCK, + .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLX_MISC3, + .iddq_bit_idx = PLLX_IDDQ_BIT, + .max_p = PLLXC_SW_MAX_P, + .dyn_ramp_reg = PLLX_MISC2, + .stepa_shift = 16, + .stepb_shift = 24, + .pdiv_tohw = pllxc_p, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { + /* PLLE special case: use cpcon field to store cml divider value */ + {336000000, 100000000, 100, 21, 16, 11}, + {312000000, 100000000, 200, 26, 24, 13}, + {0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_e_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 75000000, + .vco_min = 1600000000, + .vco_max = 2400000000U, + .base_reg = PLLE_BASE, + .misc_reg = PLLE_MISC, + .aux_reg = PLLE_AUX, + .lock_mask = PLLE_MISC_LOCK, + .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, + .lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_re_vco_params = { + .input_min = 12000000, + .input_max = 1000000000, + .cf_min = 12000000, + .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */ + .vco_min = 300000000, + .vco_max = 600000000, + .base_reg = PLLRE_BASE, + .misc_reg = PLLRE_MISC, + .lock_mask = PLLRE_MISC_LOCK, + .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE, + .lock_delay = 300, + .iddq_reg = PLLRE_MISC, + .iddq_bit_idx = PLLRE_IDDQ_BIT, +}; + +/* Peripheral clock registers */ + +static struct tegra_clk_periph_regs periph_l_regs = { + .enb_reg = CLK_OUT_ENB_L, + .enb_set_reg = CLK_OUT_ENB_SET_L, + .enb_clr_reg = CLK_OUT_ENB_CLR_L, + .rst_reg = RST_DEVICES_L, + .rst_set_reg = RST_DEVICES_SET_L, + .rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { + .enb_reg = CLK_OUT_ENB_H, + .enb_set_reg = CLK_OUT_ENB_SET_H, + .enb_clr_reg = CLK_OUT_ENB_CLR_H, + .rst_reg = RST_DEVICES_H, + .rst_set_reg = RST_DEVICES_SET_H, + .rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { + .enb_reg = CLK_OUT_ENB_U, + .enb_set_reg = CLK_OUT_ENB_SET_U, + .enb_clr_reg = CLK_OUT_ENB_CLR_U, + .rst_reg = RST_DEVICES_U, + .rst_set_reg = RST_DEVICES_SET_U, + .rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static struct tegra_clk_periph_regs periph_v_regs = { + .enb_reg = CLK_OUT_ENB_V, + .enb_set_reg = CLK_OUT_ENB_SET_V, + .enb_clr_reg = CLK_OUT_ENB_CLR_V, + .rst_reg = RST_DEVICES_V, + .rst_set_reg = RST_DEVICES_SET_V, + .rst_clr_reg = RST_DEVICES_CLR_V, +}; + +static struct tegra_clk_periph_regs periph_w_regs = { + .enb_reg = CLK_OUT_ENB_W, + .enb_set_reg = CLK_OUT_ENB_SET_W, + .enb_clr_reg = CLK_OUT_ENB_CLR_W, + .rst_reg = RST_DEVICES_W, + .rst_set_reg = RST_DEVICES_SET_W, + .rst_clr_reg = RST_DEVICES_CLR_W, +}; + +/* possible OSC frequencies in Hz */ +static unsigned long tegra114_input_freq[] = { + [0] = 13000000, + [1] = 16800000, + [4] = 19200000, + [5] = 38400000, + [8] = 12000000, + [9] = 48000000, + [12] = 260000000, +}; + +#define MASK(x) (BIT(x) - 1) + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_MUX_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, flags) + +#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id, flags)\ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, flags) + +#define TEGRA_INIT_DATA_INT8(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs,\ + _clk_num, periph_clk_enb_refcnt, 0, _clk_id, \ + _parents##_idx, 0) + +#define TEGRA_INIT_DATA_I2C(_name, _con_id, _dev_id, _parents, _offset,\ + _clk_num, _regs, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + 30, MASK(2), 0, 0, 16, 0, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, 0, _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ + _mux_shift, _mux_mask, _clk_num, _regs, \ + _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ + _mux_shift, _mux_mask, 0, 0, 0, 0, 0, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_XUSB(_name, _con_id, _dev_id, _parents, _offset, \ + _clk_num, _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset, \ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ + _clk_num, periph_clk_enb_refcnt, _gate_flags, \ + _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_AUDIO(_name, _con_id, _dev_id, _offset, _clk_num,\ + _regs, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, mux_d_audio_clk, \ + _offset, 16, 0xE01F, 0, 0, 8, 1, 0, _regs, _clk_num, \ + periph_clk_enb_refcnt, _gate_flags , _clk_id, \ + mux_d_audio_clk_idx, 0) + +enum tegra114_clk { + rtc = 4, timer = 5, uarta = 6, sdmmc2 = 9, i2s1 = 11, i2c1 = 12, + ndflash = 13, sdmmc1 = 14, sdmmc4 = 15, pwm = 17, i2s2 = 18, epp = 19, + gr_2d = 21, usbd = 22, isp = 23, gr_3d = 24, disp2 = 26, disp1 = 27, + host1x = 28, vcp = 29, i2s0 = 30, apbdma = 34, kbc = 36, kfuse = 40, + sbc1 = 41, nor = 42, sbc2 = 44, sbc3 = 46, i2c5 = 47, dsia = 48, + mipi = 50, hdmi = 51, csi = 52, i2c2 = 54, uartc = 55, mipi_cal = 56, + emc, usb2, usb3, vde = 61, bsea = 62, bsev = 63, uartd = 65, + i2c3 = 67, sbc4 = 68, sdmmc3 = 69, owr = 71, csite = 73, + la = 76, trace = 77, soc_therm = 78, dtv = 79, ndspeed = 80, + i2cslow = 81, dsib = 82, tsec = 83, xusb_host = 89, msenc = 91, + csus = 92, mselect = 99, tsensor = 100, i2s3 = 101, i2s4 = 102, + i2c4 = 103, sbc5 = 104, sbc6 = 105, d_audio, apbif = 107, dam0, dam1, + dam2, hda2codec_2x = 111, audio0_2x = 113, audio1_2x, audio2_2x, + audio3_2x, audio4_2x, spdif_2x, actmon = 119, extern1 = 120, + extern2 = 121, extern3 = 122, hda = 125, se = 127, hda2hdmi = 128, + cilab = 144, cilcd = 145, cile = 146, dsialp = 147, dsiblp = 148, + dds = 150, dp2 = 152, amx = 153, adx = 154, xusb_ss = 156, uartb = 192, + vfir, spdif_in, spdif_out, vi, vi_sensor, fuse, fuse_burn, clk_32k, + clk_m, clk_m_div2, clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_c2, + pll_c3, pll_m, pll_m_out1, pll_p, pll_p_out1, pll_p_out2, pll_p_out3, + pll_p_out4, pll_a, pll_a_out0, pll_d, pll_d_out0, pll_d2, pll_d2_out0, + pll_u, pll_u_480M, pll_u_60M, pll_u_48M, pll_u_12M, pll_x, pll_x_out0, + pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, + i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, + audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, + blink, xusb_host_src, xusb_falcon_src, xusb_fs_src, xusb_ss_src, + xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, + + /* Mux clocks */ + + audio0_mux = 300, audio1_mux, audio2_mux, audio3_mux, audio4_mux, + spdif_mux, clk_out_1_mux, clk_out_2_mux, clk_out_3_mux, dsia_mux, + dsib_mux, clk_max, +}; + +struct utmi_clk_param { + /* Oscillator Frequency in KHz */ + u32 osc_frequency; + /* UTMIP PLL Enable Delay Count */ + u8 enable_delay_count; + /* UTMIP PLL Stable count */ + u8 stable_count; + /* UTMIP PLL Active delay count */ + u8 active_delay_count; + /* UTMIP PLL Xtal frequency count */ + u8 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { + {.osc_frequency = 13000000, .enable_delay_count = 0x02, + .stable_count = 0x33, .active_delay_count = 0x05, + .xtal_freq_count = 0x7F}, + {.osc_frequency = 19200000, .enable_delay_count = 0x03, + .stable_count = 0x4B, .active_delay_count = 0x06, + .xtal_freq_count = 0xBB}, + {.osc_frequency = 12000000, .enable_delay_count = 0x02, + .stable_count = 0x2F, .active_delay_count = 0x04, + .xtal_freq_count = 0x76}, + {.osc_frequency = 26000000, .enable_delay_count = 0x04, + .stable_count = 0x66, .active_delay_count = 0x09, + .xtal_freq_count = 0xFE}, + {.osc_frequency = 16800000, .enable_delay_count = 0x03, + .stable_count = 0x41, .active_delay_count = 0x0A, + .xtal_freq_count = 0xA4}, +}; + +/* peripheral mux definitions */ + +#define MUX_I2S_SPDIF(_id) \ +static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ + #_id, "pll_p",\ + "clk_m"}; +MUX_I2S_SPDIF(audio0) +MUX_I2S_SPDIF(audio1) +MUX_I2S_SPDIF(audio2) +MUX_I2S_SPDIF(audio3) +MUX_I2S_SPDIF(audio4) +MUX_I2S_SPDIF(audio) + +#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm_clkm[] = { + "pll_p", "pll_c", "pll_m", "clk_m" +}; +#define mux_pllp_pllc_pllm_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" }; +#define mux_pllp_pllc_pllm_idx NULL + +static const char *mux_pllp_pllc_clk32_clkm[] = { + "pll_p", "pll_c", "clk_32k", "clk_m" +}; +#define mux_pllp_pllc_clk32_clkm_idx NULL + +static const char *mux_plla_pllc_pllp_clkm[] = { + "pll_a_out0", "pll_c", "pll_p", "clk_m" +}; +#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = { + "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m" +}; +static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, +}; + +static const char *mux_pllp_clkm[] = { + "pll_p", "clk_m" +}; +static u32 mux_pllp_clkm_idx[] = { + [0] = 0, [1] = 3, +}; + +static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = { + "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx + +static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = { + "pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c", + "pll_d2_out0", "clk_m" +}; +#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL + +static const char *mux_pllm_pllc_pllp_plla[] = { + "pll_m", "pll_c", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc_clkm[] = { + "pll_p", "pll_c", "pll_m" +}; +static u32 mux_pllp_pllc_clkm_idx[] = { + [0] = 0, [1] = 1, [2] = 3, +}; + +static const char *mux_pllp_pllc_clkm_clk32[] = { + "pll_p", "pll_c", "clk_m", "clk_32k" +}; +#define mux_pllp_pllc_clkm_clk32_idx NULL + +static const char *mux_plla_clk32_pllp_clkm_plle[] = { + "pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0" +}; +#define mux_plla_clk32_pllp_clkm_plle_idx NULL + +static const char *mux_clkm_pllp_pllc_pllre[] = { + "clk_m", "pll_p", "pll_c", "pll_re_out" +}; +static u32 mux_clkm_pllp_pllc_pllre_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 5, +}; + +static const char *mux_clkm_48M_pllp_480M[] = { + "clk_m", "pll_u_48M", "pll_p", "pll_u_480M" +}; +#define mux_clkm_48M_pllp_480M_idx NULL + +static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = { + "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref" +}; +static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { + [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, +}; + +static const char *mux_plld_out0_plld2_out0[] = { + "pll_d_out0", "pll_d2_out0", +}; +#define mux_plld_out0_plld2_out0_idx NULL + +static const char *mux_d_audio_clk[] = { + "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; +static u32 mux_d_audio_clk_idx[] = { + [0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001, + [5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007, +}; + +static const char *mux_pllmcp_clkm[] = { + "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", +}; + +static const struct clk_div_table pll_re_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 5 }, + { .val = 5, .div = 6 }, + { .val = 0, .div = 0 }, +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static unsigned long osc_freq; +static unsigned long pll_ref_freq; + +static int __init tegra114_osc_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + u32 val, pll_ref_div; + + val = readl_relaxed(clk_base + OSC_CTRL); + + osc_freq = tegra114_input_freq[val >> OSC_CTRL_OSC_FREQ_SHIFT]; + if (!osc_freq) { + WARN_ON(1); + return -EINVAL; + } + + /* clk_m */ + clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, + osc_freq); + clk_register_clkdev(clk, "clk_m", NULL); + clks[clk_m] = clk; + + /* pll_ref */ + val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; + pll_ref_div = 1 << val; + clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", + CLK_SET_RATE_PARENT, 1, pll_ref_div); + clk_register_clkdev(clk, "pll_ref", NULL); + clks[pll_ref] = clk; + + pll_ref_freq = osc_freq / pll_ref_div; + + return 0; +} + +static void __init tegra114_fixed_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* clk_32k */ + clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, + 32768); + clk_register_clkdev(clk, "clk_32k", NULL); + clks[clk_32k] = clk; + + /* clk_m_div2 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "clk_m_div2", NULL); + clks[clk_m_div2] = clk; + + /* clk_m_div4 */ + clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", + CLK_SET_RATE_PARENT, 1, 4); + clk_register_clkdev(clk, "clk_m_div4", NULL); + clks[clk_m_div4] = clk; + +} + +static __init void tegra114_utmi_param_configure(void __iomem *clk_base) +{ + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { + if (osc_freq == utmi_parameters[i].osc_frequency) + break; + } + + if (i >= ARRAY_SIZE(utmi_parameters)) { + pr_err("%s: Unexpected oscillator freq %lu\n", __func__, + osc_freq); + return; + } + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL stable and active counts */ + /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */ + reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); + reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); + + reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i]. + active_delay_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; + reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + + /* Program UTMIP PLL delay and oscillator frequency counts */ + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + + reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i]. + enable_delay_count); + + reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); + reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i]. + xtal_freq_count); + + /* Remove power downs from UTMIP PLL control bits */ + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + /* Setup HW control of UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; + reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; + writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + + udelay(1); + + /* Setup SW override of UTMIPLL assuming USB2.0 + ports are assigned to USB2 */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; + reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + + udelay(1); + + /* Enable HW control UTMIPLL */ + reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); + reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; + writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); +} + +static void __init _clip_vco_min(struct tegra_clk_pll_params *pll_params) +{ + pll_params->vco_min = + DIV_ROUND_UP(pll_params->vco_min, pll_ref_freq) * pll_ref_freq; +} + +static int __init _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val; + u32 step_a, step_b; + + switch (pll_ref_freq) { + case 12000000: + case 13000000: + case 26000000: + step_a = 0x2B; + step_b = 0x0B; + break; + case 16800000: + step_a = 0x1A; + step_b = 0x09; + break; + case 19200000: + step_a = 0x12; + step_b = 0x08; + break; + default: + pr_err("%s: Unexpected reference rate %lu\n", + __func__, pll_ref_freq); + WARN_ON(1); + return -EINVAL; + } + + val = step_a << pll_params->stepa_shift; + val |= step_b << pll_params->stepb_shift; + writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg); + + return 0; +} + +static void __init _init_iddq(struct tegra_clk_pll_params *pll_params, + void __iomem *clk_base) +{ + u32 val, val_iddq; + + val = readl_relaxed(clk_base + pll_params->base_reg); + val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + + if (val & BIT(30)) + WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); + else { + val_iddq |= BIT(pll_params->iddq_bit_idx); + writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg); + } +} + +static void __init tegra114_pll_init(void __iomem *clk_base, + void __iomem *pmc) +{ + u32 val; + struct clk *clk; + + /* PLLC */ + _clip_vco_min(&pll_c_params); + if (_setup_dynamic_ramp(&pll_c_params, clk_base) >= 0) { + _init_iddq(&pll_c_params, clk_base); + clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base, + pmc, 0, 0, &pll_c_params, TEGRA_PLL_USE_LOCK, + pll_c_freq_table, NULL); + clk_register_clkdev(clk, "pll_c", NULL); + clks[pll_c] = clk; + + /* PLLC_OUT1 */ + clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", + clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", + clk_base + PLLC_OUT, 1, 0, + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_c_out1", NULL); + clks[pll_c_out1] = clk; + } + + /* PLLC2 */ + _clip_vco_min(&pll_c2_params); + clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, 0, + &pll_c2_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c2", NULL); + clks[pll_c2] = clk; + + /* PLLC3 */ + _clip_vco_min(&pll_c3_params); + clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0, 0, + &pll_c3_params, TEGRA_PLL_USE_LOCK, + pll_cx_freq_table, NULL); + clk_register_clkdev(clk, "pll_c3", NULL); + clks[pll_c3] = clk; + + /* PLLP */ + clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc, 0, + 408000000, &pll_p_params, + TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, + pll_p_freq_table, NULL); + clk_register_clkdev(clk, "pll_p", NULL); + clks[pll_p] = clk; + + /* PLLP_OUT1 */ + clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", + clk_base + PLLP_OUTA, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out1", NULL); + clks[pll_p_out1] = clk; + + /* PLLP_OUT2 */ + clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", + clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", + clk_base + PLLP_OUTA, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out2", NULL); + clks[pll_p_out2] = clk; + + /* PLLP_OUT3 */ + clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", + clk_base + PLLP_OUTB, 1, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out3", NULL); + clks[pll_p_out3] = clk; + + /* PLLP_OUT4 */ + clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", + clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | + TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, + &pll_div_lock); + clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", + clk_base + PLLP_OUTB, 17, 16, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, + &pll_div_lock); + clk_register_clkdev(clk, "pll_p_out4", NULL); + clks[pll_p_out4] = clk; + + /* PLLM */ + _clip_vco_min(&pll_m_params); + clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc, + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, + &pll_m_params, TEGRA_PLL_USE_LOCK, + pll_m_freq_table, NULL); + clk_register_clkdev(clk, "pll_m", NULL); + clks[pll_m] = clk; + + /* PLLM_OUT1 */ + clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", + clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", + clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_m_out1", NULL); + clks[pll_m_out1] = clk; + + /* PLLM_UD */ + clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", + CLK_SET_RATE_PARENT, 1, 1); + + /* PLLX */ + _clip_vco_min(&pll_x_params); + if (_setup_dynamic_ramp(&pll_x_params, clk_base) >= 0) { + _init_iddq(&pll_x_params, clk_base); + clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, + pmc, CLK_IGNORE_UNUSED, 0, &pll_x_params, + TEGRA_PLL_USE_LOCK, pll_x_freq_table, NULL); + clk_register_clkdev(clk, "pll_x", NULL); + clks[pll_x] = clk; + } + + /* PLLX_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_x_out0", NULL); + clks[pll_x_out0] = clk; + + /* PLLU */ + val = readl(clk_base + pll_u_params.base_reg); + val &= ~BIT(24); /* disable PLLU_OVERRIDE */ + writel(val, clk_base + pll_u_params.base_reg); + + clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc, 0, + 0, &pll_u_params, TEGRA_PLLU | + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_u_freq_table, &pll_u_lock); + clk_register_clkdev(clk, "pll_u", NULL); + clks[pll_u] = clk; + + tegra114_utmi_param_configure(clk_base); + + /* PLLU_480M */ + clk = clk_register_gate(NULL, "pll_u_480M", "pll_u", + CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, + 22, 0, &pll_u_lock); + clk_register_clkdev(clk, "pll_u_480M", NULL); + clks[pll_u_480M] = clk; + + /* PLLU_60M */ + clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u", + CLK_SET_RATE_PARENT, 1, 8); + clk_register_clkdev(clk, "pll_u_60M", NULL); + clks[pll_u_60M] = clk; + + /* PLLU_48M */ + clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u", + CLK_SET_RATE_PARENT, 1, 10); + clk_register_clkdev(clk, "pll_u_48M", NULL); + clks[pll_u_48M] = clk; + + /* PLLU_12M */ + clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u", + CLK_SET_RATE_PARENT, 1, 40); + clk_register_clkdev(clk, "pll_u_12M", NULL); + clks[pll_u_12M] = clk; + + /* PLLD */ + clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0, + 0, &pll_d_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d_lock); + clk_register_clkdev(clk, "pll_d", NULL); + clks[pll_d] = clk; + + /* PLLD_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d_out0", NULL); + clks[pll_d_out0] = clk; + + /* PLLD2 */ + clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0, + 0, &pll_d2_params, + TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | + TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d2_lock); + clk_register_clkdev(clk, "pll_d2", NULL); + clks[pll_d2] = clk; + + /* PLLD2_OUT0 */ + clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll_d2_out0", NULL); + clks[pll_d2_out0] = clk; + + /* PLLA */ + clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc, 0, + 0, &pll_a_params, TEGRA_PLL_HAS_CPCON | + TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL); + clk_register_clkdev(clk, "pll_a", NULL); + clks[pll_a] = clk; + + /* PLLA_OUT0 */ + clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", + clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, + 8, 8, 1, NULL); + clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", + clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT, 0, NULL); + clk_register_clkdev(clk, "pll_a_out0", NULL); + clks[pll_a_out0] = clk; + + /* PLLRE */ + _clip_vco_min(&pll_re_vco_params); + clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc, + 0, 0, &pll_re_vco_params, TEGRA_PLL_USE_LOCK, + NULL, &pll_re_lock, pll_ref_freq); + clk_register_clkdev(clk, "pll_re_vco", NULL); + clks[pll_re_vco] = clk; + + clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0, + clk_base + PLLRE_BASE, 16, 4, 0, + pll_re_div_table, &pll_re_lock); + clk_register_clkdev(clk, "pll_re_out", NULL); + clks[pll_re_out] = clk; + + /* PLLE */ + clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_re_vco", + clk_base, 0, 100000000, &pll_e_params, + pll_e_freq_table, NULL); + clk_register_clkdev(clk, "pll_e_out0", NULL); + clks[pll_e_out0] = clk; +} + +static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", + "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; + +static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern1", +}; + +static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern2", +}; + +static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2", + "clk_m_div4", "extern3", +}; + +static void __init tegra114_audio_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* spdif_in_sync */ + clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000, + 24000000); + clk_register_clkdev(clk, "spdif_in_sync", NULL); + clks[spdif_in_sync] = clk; + + /* i2s0_sync */ + clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s0_sync", NULL); + clks[i2s0_sync] = clk; + + /* i2s1_sync */ + clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s1_sync", NULL); + clks[i2s1_sync] = clk; + + /* i2s2_sync */ + clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s2_sync", NULL); + clks[i2s2_sync] = clk; + + /* i2s3_sync */ + clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s3_sync", NULL); + clks[i2s3_sync] = clk; + + /* i2s4_sync */ + clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000); + clk_register_clkdev(clk, "i2s4_sync", NULL); + clks[i2s4_sync] = clk; + + /* vimclk_sync */ + clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000); + clk_register_clkdev(clk, "vimclk_sync", NULL); + clks[vimclk_sync] = clk; + + /* audio0 */ + clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, + NULL); + clks[audio0_mux] = clk; + clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S0, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio0", NULL); + clks[audio0] = clk; + + /* audio1 */ + clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, + NULL); + clks[audio1_mux] = clk; + clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S1, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio1", NULL); + clks[audio1] = clk; + + /* audio2 */ + clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, + NULL); + clks[audio2_mux] = clk; + clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S2, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio2", NULL); + clks[audio2] = clk; + + /* audio3 */ + clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, + NULL); + clks[audio3_mux] = clk; + clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S3, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio3", NULL); + clks[audio3] = clk; + + /* audio4 */ + clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, + NULL); + clks[audio4_mux] = clk; + clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, + clk_base + AUDIO_SYNC_CLK_I2S4, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "audio4", NULL); + clks[audio4] = clk; + + /* spdif */ + clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, + ARRAY_SIZE(mux_audio_sync_clk), 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, + NULL); + clks[spdif_mux] = clk; + clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, + clk_base + AUDIO_SYNC_CLK_SPDIF, 4, + CLK_GATE_SET_TO_DISABLE, NULL); + clk_register_clkdev(clk, "spdif", NULL); + clks[spdif] = clk; + + /* audio0_2x */ + clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio0_div", "audio0_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 113, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio0_2x", NULL); + clks[audio0_2x] = clk; + + /* audio1_2x */ + clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio1_div", "audio1_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 114, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio1_2x", NULL); + clks[audio1_2x] = clk; + + /* audio2_2x */ + clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio2_div", "audio2_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 115, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio2_2x", NULL); + clks[audio2_2x] = clk; + + /* audio3_2x */ + clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio3_div", "audio3_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 116, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio3_2x", NULL); + clks[audio3_2x] = clk; + + /* audio4_2x */ + clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("audio4_div", "audio4_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 117, &periph_v_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, "audio4_2x", NULL); + clks[audio4_2x] = clk; + + /* spdif_2x */ + clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif", + CLK_SET_RATE_PARENT, 2, 1); + clk = tegra_clk_register_divider("spdif_div", "spdif_doubler", + clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, + 0, &clk_doubler_lock); + clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div", + TEGRA_PERIPH_NO_RESET, clk_base, + CLK_SET_RATE_PARENT, 118, + &periph_v_regs, periph_clk_enb_refcnt); + clk_register_clkdev(clk, "spdif_2x", NULL); + clks[spdif_2x] = clk; +} + +static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) +{ + struct clk *clk; + + /* clk_out_1 */ + clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, + &clk_out_lock); + clks[clk_out_1_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 2, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern1", "clk_out_1"); + clks[clk_out_1] = clk; + + /* clk_out_2 */ + clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, + &clk_out_lock); + clks[clk_out_2_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 10, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern2", "clk_out_2"); + clks[clk_out_2] = clk; + + /* clk_out_3 */ + clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, + ARRAY_SIZE(clk_out1_parents), 0, + pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, + &clk_out_lock); + clks[clk_out_3_mux] = clk; + clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, + pmc_base + PMC_CLK_OUT_CNTRL, 18, 0, + &clk_out_lock); + clk_register_clkdev(clk, "extern3", "clk_out_3"); + clks[clk_out_3] = clk; + + /* blink */ + clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, + pmc_base + PMC_DPD_PADS_ORIDE, + PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); + clk = clk_register_gate(NULL, "blink", "blink_override", 0, + pmc_base + PMC_CTRL, + PMC_CTRL_BLINK_ENB, 0, NULL); + clk_register_clkdev(clk, "blink", NULL); + clks[blink] = clk; + +} + +static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", + "pll_p_out3", "pll_p_out2", "unused", + "clk_32k", "pll_m_out1" }; + +static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x" }; + +static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", + "pll_p", "pll_p_out4", "unused", + "unused", "pll_x", "pll_x_out0" }; + +static void __init tegra114_super_clk_init(void __iomem *clk_base) +{ + struct clk *clk; + + /* CCLKG */ + clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, + ARRAY_SIZE(cclk_g_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKG_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "cclk_g", NULL); + clks[cclk_g] = clk; + + /* CCLKLP */ + clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents, + ARRAY_SIZE(cclk_lp_parents), + CLK_SET_RATE_PARENT, + clk_base + CCLKLP_BURST_POLICY, + 0, 4, 8, 9, NULL); + clk_register_clkdev(clk, "cclk_lp", NULL); + clks[cclk_lp] = clk; + + /* SCLK */ + clk = tegra_clk_register_super_mux("sclk", sclk_parents, + ARRAY_SIZE(sclk_parents), + CLK_SET_RATE_PARENT, + clk_base + SCLK_BURST_POLICY, + 0, 4, 0, 0, NULL); + clk_register_clkdev(clk, "sclk", NULL); + clks[sclk] = clk; + + /* HCLK */ + clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, + clk_base + SYSTEM_CLK_RATE, 4, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "hclk", NULL); + clks[hclk] = clk; + + /* PCLK */ + clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, + clk_base + SYSTEM_CLK_RATE, 0, 2, 0, + &sysrate_lock); + clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, + 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); + clk_register_clkdev(clk, "pclk", NULL); + clks[pclk] = clk; +} + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { + TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0), + TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), + TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), + TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3), + TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4), + TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), + TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), + TEGRA_INIT_DATA_MUX("pwm", NULL, "pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, &periph_l_regs, TEGRA_PERIPH_ON_APB, pwm), + TEGRA_INIT_DATA_MUX("adx", NULL, "adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, &periph_w_regs, TEGRA_PERIPH_ON_APB, adx), + TEGRA_INIT_DATA_MUX("amx", NULL, "amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, &periph_w_regs, TEGRA_PERIPH_ON_APB, amx), + TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda), + TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda2codec_2x), + TEGRA_INIT_DATA_MUX("sbc1", NULL, "tegra11-spi.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), + TEGRA_INIT_DATA_MUX("sbc2", NULL, "tegra11-spi.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), + TEGRA_INIT_DATA_MUX("sbc3", NULL, "tegra11-spi.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), + TEGRA_INIT_DATA_MUX("sbc4", NULL, "tegra11-spi.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), + TEGRA_INIT_DATA_MUX("sbc5", NULL, "tegra11-spi.4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5), + TEGRA_INIT_DATA_MUX("sbc6", NULL, "tegra11-spi.5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6), + TEGRA_INIT_DATA_MUX8("ndflash", NULL, "tegra_nand", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX8("ndspeed", NULL, "tegra_nand_speed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), + TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), + TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), + TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), + TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), + TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), + TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), + TEGRA_INIT_DATA_MUX_FLAGS("csite", NULL, "csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la), + TEGRA_INIT_DATA_MUX("trace", NULL, "trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, &periph_u_regs, TEGRA_PERIPH_ON_APB, trace), + TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), + TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), + TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), + TEGRA_INIT_DATA_I2C("i2c1", "div-clk", "tegra11-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, i2c1), + TEGRA_INIT_DATA_I2C("i2c2", "div-clk", "tegra11-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, i2c2), + TEGRA_INIT_DATA_I2C("i2c3", "div-clk", "tegra11-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, i2c3), + TEGRA_INIT_DATA_I2C("i2c4", "div-clk", "tegra11-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, i2c4), + TEGRA_INIT_DATA_I2C("i2c5", "div-clk", "tegra11-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, i2c5), + TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta), + TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb), + TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc), + TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd), + TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, &periph_l_regs, 0, gr_3d), + TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr_2d), + TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), + TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), + TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), + TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc), + TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec), + TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), + TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), + TEGRA_INIT_DATA_MUX("cilab", "cilab", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, &periph_w_regs, 0, cilab), + TEGRA_INIT_DATA_MUX("cilcd", "cilcd", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, &periph_w_regs, 0, cilcd), + TEGRA_INIT_DATA_MUX("cile", "cile", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, &periph_w_regs, 0, cile), + TEGRA_INIT_DATA_MUX("dsialp", "dsialp", "tegradc.0", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, &periph_w_regs, 0, dsialp), + TEGRA_INIT_DATA_MUX("dsiblp", "dsiblp", "tegradc.1", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, &periph_w_regs, 0, dsiblp), + TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor), + TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon), + TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1), + TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2), + TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3), + TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), + TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se), + TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm), + TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src), + TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src), + TEGRA_INIT_DATA_XUSB("xusb_fs_src", "fs_src", "tegra_xhci", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_fs_src), + TEGRA_INIT_DATA_XUSB("xusb_ss_src", "ss_src", "tegra_xhci", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_ss_src), + TEGRA_INIT_DATA_XUSB("xusb_dev_src", "dev_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, &periph_u_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_dev_src), + TEGRA_INIT_DATA_AUDIO("d_audio", "d_audio", "tegra30-ahub", CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, TEGRA_PERIPH_ON_APB, d_audio), + TEGRA_INIT_DATA_AUDIO("dam0", NULL, "tegra30-dam.0", CLK_SOURCE_DAM0, 108, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam0), + TEGRA_INIT_DATA_AUDIO("dam1", NULL, "tegra30-dam.1", CLK_SOURCE_DAM1, 109, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam1), + TEGRA_INIT_DATA_AUDIO("dam2", NULL, "tegra30-dam.2", CLK_SOURCE_DAM2, 110, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam2), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { + TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, &periph_l_regs, 0, disp1), + TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, &periph_l_regs, 0, disp2), +}; + +static __init void tegra114_periph_clk_init(void __iomem *clk_base) +{ + struct tegra_periph_init_data *data; + struct clk *clk; + int i; + u32 val; + + /* apbdma */ + clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, + 0, 34, &periph_h_regs, + periph_clk_enb_refcnt); + clks[apbdma] = clk; + + /* rtc */ + clk = tegra_clk_register_periph_gate("rtc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 4, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "rtc-tegra"); + clks[rtc] = clk; + + /* kbc */ + clk = tegra_clk_register_periph_gate("kbc", "clk_32k", + TEGRA_PERIPH_ON_APB | + TEGRA_PERIPH_NO_RESET, clk_base, + 0, 36, &periph_h_regs, + periph_clk_enb_refcnt); + clks[kbc] = clk; + + /* timer */ + clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, + 0, 5, &periph_l_regs, + periph_clk_enb_refcnt); + clk_register_clkdev(clk, NULL, "timer"); + clks[timer] = clk; + + /* kfuse */ + clk = tegra_clk_register_periph_gate("kfuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 40, + &periph_h_regs, periph_clk_enb_refcnt); + clks[kfuse] = clk; + + /* fuse */ + clk = tegra_clk_register_periph_gate("fuse", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse] = clk; + + /* fuse_burn */ + clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 39, + &periph_h_regs, periph_clk_enb_refcnt); + clks[fuse_burn] = clk; + + /* apbif */ + clk = tegra_clk_register_periph_gate("apbif", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 107, + &periph_v_regs, periph_clk_enb_refcnt); + clks[apbif] = clk; + + /* hda2hdmi */ + clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 128, + &periph_w_regs, periph_clk_enb_refcnt); + clks[hda2hdmi] = clk; + + /* vcp */ + clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base, 0, + 29, &periph_l_regs, + periph_clk_enb_refcnt); + clks[vcp] = clk; + + /* bsea */ + clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, + 0, 62, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsea] = clk; + + /* bsev */ + clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, + 0, 63, &periph_h_regs, + periph_clk_enb_refcnt); + clks[bsev] = clk; + + /* mipi-cal */ + clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base, + 0, 56, &periph_h_regs, + periph_clk_enb_refcnt); + clks[mipi_cal] = clk; + + /* usbd */ + clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, + 0, 22, &periph_l_regs, + periph_clk_enb_refcnt); + clks[usbd] = clk; + + /* usb2 */ + clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, + 0, 58, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb2] = clk; + + /* usb3 */ + clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, + 0, 59, &periph_h_regs, + periph_clk_enb_refcnt); + clks[usb3] = clk; + + /* csi */ + clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, + 0, 52, &periph_h_regs, + periph_clk_enb_refcnt); + clks[csi] = clk; + + /* isp */ + clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, + 23, &periph_l_regs, + periph_clk_enb_refcnt); + clks[isp] = clk; + + /* csus */ + clk = tegra_clk_register_periph_gate("csus", "clk_m", + TEGRA_PERIPH_NO_RESET, clk_base, 0, 92, + &periph_u_regs, periph_clk_enb_refcnt); + clks[csus] = clk; + + /* dds */ + clk = tegra_clk_register_periph_gate("dds", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 150, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dds] = clk; + + /* dp2 */ + clk = tegra_clk_register_periph_gate("dp2", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 152, + &periph_w_regs, periph_clk_enb_refcnt); + clks[dp2] = clk; + + /* dtv */ + clk = tegra_clk_register_periph_gate("dtv", "clk_m", + TEGRA_PERIPH_ON_APB, clk_base, 0, 79, + &periph_u_regs, periph_clk_enb_refcnt); + clks[dtv] = clk; + + /* dsia */ + clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock); + clks[dsia_mux] = clk; + clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base, + 0, 48, &periph_h_regs, + periph_clk_enb_refcnt); + clks[dsia] = clk; + + /* dsib */ + clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock); + clks[dsib_mux] = clk; + clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base, + 0, 82, &periph_u_regs, + periph_clk_enb_refcnt); + clks[dsib] = clk; + + /* xusb_hs_src */ + val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); + val |= BIT(25); /* always select PLLU_60M */ + writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); + + clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, + 1, 1); + clks[xusb_hs_src] = clk; + + /* xusb_host */ + clk = tegra_clk_register_periph_gate("xusb_host", "xusb_host_src", 0, + clk_base, 0, 89, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_ss */ + clk = tegra_clk_register_periph_gate("xusb_ss", "xusb_ss_src", 0, + clk_base, 0, 156, &periph_w_regs, + periph_clk_enb_refcnt); + clks[xusb_host] = clk; + + /* xusb_dev */ + clk = tegra_clk_register_periph_gate("xusb_dev", "xusb_dev_src", 0, + clk_base, 0, 95, &periph_u_regs, + periph_clk_enb_refcnt); + clks[xusb_dev] = clk; + + /* emc */ + clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, + ARRAY_SIZE(mux_pllmcp_clkm), 0, + clk_base + CLK_SOURCE_EMC, + 29, 3, 0, NULL); + clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, + CLK_IGNORE_UNUSED, 57, &periph_h_regs, + periph_clk_enb_refcnt); + clks[emc] = clk; + + for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { + data = &tegra_periph_clk_list[i]; + clk = tegra_clk_register_periph(data->name, data->parent_names, + data->num_parents, &data->periph, + clk_base, data->offset, data->flags); + clks[data->clk_id] = clk; + } + + for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { + data = &tegra_periph_nodiv_clk_list[i]; + clk = tegra_clk_register_periph_nodiv(data->name, + data->parent_names, data->num_parents, + &data->periph, clk_base, data->offset); + clks[data->clk_id] = clk; + } +} + +static struct tegra_cpu_car_ops tegra114_cpu_car_ops; + +static const struct of_device_id pmc_match[] __initconst = { + { .compatible = "nvidia,tegra114-pmc" }, + {}, +}; + +static __initdata struct tegra_clk_init_table init_table[] = { + {uarta, pll_p, 408000000, 0}, + {uartb, pll_p, 408000000, 0}, + {uartc, pll_p, 408000000, 0}, + {uartd, pll_p, 408000000, 1}, + {pll_a, clk_max, 564480000, 1}, + {pll_a_out0, clk_max, 11289600, 1}, + {extern1, pll_a_out0, 0, 1}, + {clk_out_1_mux, extern1, 0, 1}, + {clk_out_1, clk_max, 0, 1}, + {i2s0, pll_a_out0, 11289600, 0}, + {i2s1, pll_a_out0, 11289600, 0}, + {i2s2, pll_a_out0, 11289600, 0}, + {i2s3, pll_a_out0, 11289600, 0}, + {i2s4, pll_a_out0, 11289600, 0}, + {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ +}; + +static void __init tegra114_clock_apply_init_table(void) +{ + tegra_init_from_table(init_table, clks, clk_max); +} + +void __init tegra114_clock_init(struct device_node *np) +{ + struct device_node *node; + int i; + + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("ioremap tegra114 CAR failed\n"); + return; + } + + node = of_find_matching_node(NULL, pmc_match); + if (!node) { + pr_err("Failed to find pmc node\n"); + WARN_ON(1); + return; + } + + pmc_base = of_iomap(node, 0); + if (!pmc_base) { + pr_err("Can't map pmc registers\n"); + WARN_ON(1); + return; + } + + if (tegra114_osc_clk_init(clk_base) < 0) + return; + + tegra114_fixed_clk_init(clk_base); + tegra114_pll_init(clk_base, pmc_base); + tegra114_periph_clk_init(clk_base); + tegra114_audio_clk_init(clk_base); + tegra114_pmc_clk_init(pmc_base); + tegra114_super_clk_init(clk_base); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + if (IS_ERR(clks[i])) { + pr_err + ("Tegra114 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + } + if (!clks[i]) + clks[i] = ERR_PTR(-EINVAL); + } + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + tegra_clk_apply_init_table = tegra114_clock_apply_init_table; + + tegra_cpu_car_ops = &tegra114_cpu_car_ops; +} From 27aa99dc0ec188771bd22f2188e56a571368303e Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:46 +0300 Subject: [PATCH 65/68] clk: tegra: devicetree match for nvidia,tegra114-car Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk.c | 1 + drivers/clk/tegra/clk.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 70b7a4783bee..923ca7ee4694 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -77,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, static const struct of_device_id tegra_dt_clk_match[] = { { .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init }, { .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init }, + { .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init }, { } }; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index fb48f0467b9d..e0565620d68e 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -583,6 +583,12 @@ void tegra30_clock_init(struct device_node *np); static inline void tegra30_clock_init(struct device_node *np) {} #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_clock_init(struct device_node *np); +#else +static inline void tegra114_clock_init(struct device_node *np) {} +#endif /* CONFIG_ARCH_TEGRA114_SOC */ + typedef void (*tegra_clk_apply_init_table_func)(void); extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; From 672d889c0a8969e8cc63d62c28b7073ae1370471 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:48 +0300 Subject: [PATCH 66/68] ARM: dt: Add references to tegra_car clocks Add references to tegra_car clocks for the basic device nodes. Also remove the clock-frequency property of the serial node as the UART driver can now use the clock framework to obtain the frequency. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra114-dalmore.dts | 1 - arch/arm/boot/dts/tegra114-pluto.dts | 1 - arch/arm/boot/dts/tegra114.dtsi | 8 +++++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts index 6ebc1b704190..616990dc92db 100644 --- a/arch/arm/boot/dts/tegra114-dalmore.dts +++ b/arch/arm/boot/dts/tegra114-dalmore.dts @@ -12,7 +12,6 @@ serial@70006300 { status = "okay"; - clock-frequency = <408000000>; }; pmc { diff --git a/arch/arm/boot/dts/tegra114-pluto.dts b/arch/arm/boot/dts/tegra114-pluto.dts index 5deb8692b350..6bbc8efae9c0 100644 --- a/arch/arm/boot/dts/tegra114-pluto.dts +++ b/arch/arm/boot/dts/tegra114-pluto.dts @@ -12,7 +12,6 @@ serial@70006300 { status = "okay"; - clock-frequency = <408000000>; }; pmc { diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index c0b527d15fda..c1110a9b2a91 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -24,10 +24,11 @@ 0 42 0x04 0 121 0x04 0 122 0x04>; + clocks = <&tegra_car 5>; }; tegra_car: clock { - compatible = "nvidia,tegra114-car, nvidia,tegra30-car"; + compatible = "nvidia,tegra114-car"; reg = <0x60006000 0x1000>; #clock-cells = <1>; }; @@ -66,6 +67,7 @@ reg-shift = <2>; interrupts = <0 36 0x04>; status = "disabled"; + clocks = <&tegra_car 6>; }; serial@70006040 { @@ -74,6 +76,7 @@ reg-shift = <2>; interrupts = <0 37 0x04>; status = "disabled"; + clocks = <&tegra_car 192>; }; serial@70006200 { @@ -82,6 +85,7 @@ reg-shift = <2>; interrupts = <0 46 0x04>; status = "disabled"; + clocks = <&tegra_car 55>; }; serial@70006300 { @@ -90,12 +94,14 @@ reg-shift = <2>; interrupts = <0 90 0x04>; status = "disabled"; + clocks = <&tegra_car 65>; }; rtc { compatible = "nvidia,tegra114-rtc", "nvidia,tegra20-rtc"; reg = <0x7000e000 0x100>; interrupts = <0 2 0x04>; + clocks = <&tegra_car 4>; }; pmc { From c604283f52855a4568c18cfd4011bdcfeccf2c52 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 3 Apr 2013 17:40:49 +0300 Subject: [PATCH 67/68] clk: tegra: Remove forced clk_enable of uartd The UART driver enables the console uart clock, so we don't need to do that anymore in this file. Signed-off-by: Peter De Schrijver Acked-by: Mike Turquette Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index e2a7fa16c5de..0db81dd05fce 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -2011,7 +2011,7 @@ static __initdata struct tegra_clk_init_table init_table[] = { {uarta, pll_p, 408000000, 0}, {uartb, pll_p, 408000000, 0}, {uartc, pll_p, 408000000, 0}, - {uartd, pll_p, 408000000, 1}, + {uartd, pll_p, 408000000, 0}, {pll_a, clk_max, 564480000, 1}, {pll_a_out0, clk_max, 11289600, 1}, {extern1, pll_a_out0, 0, 1}, From 964ea47572b89589b61b553e44bbe9907d4f12a6 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 4 Apr 2013 17:13:54 -0600 Subject: [PATCH 68/68] clk: tegra: fix enum tegra114_clk to match binding A gap exists in the binding's clock ID definitions. Fix the clock driver to be consistent. This allows pclk to be looked up through device tree and prevents: ERROR: could not get clock /pmc:pclk(0) Signed-off-by: Stephen Warren --- drivers/clk/tegra/clk-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 0db81dd05fce..d78e16ee161c 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -760,7 +760,7 @@ enum tegra114_clk { pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, - blink, xusb_host_src, xusb_falcon_src, xusb_fs_src, xusb_ss_src, + blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src, xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, /* Mux clocks */