Subject: [PATCH] ARM: OMAP: Fix reprogramming of dpll1 rate

From: [email-masked] (Tony Lindgren)
Date: Fri, 11 Nov 2011 11:16:31 -0800

Commit a66cb3454f220f49f900646ebdc76cb943319eb7 (ARM: OMAP: Map SRAM
later on with ioremap_exec()) moved the SRAM init to happen later
to remove a dependency to early SoC detection for map_io.

This broke booting on some boards not using Kconfig option for
OMAP_CLOCKS_SET_BY_BOOTLOADER as the dpll1 reprogramming would
cause the following error:

kernel BUG at arch/arm/plat-omap/sram.c:226!
Internal error: Oops - undefined instruction: 0 [#1] PREEMPT
Modules linked in:

CPU: 0    Not tainted  (3.2.0-rc1-e3 #9)
PC is at omap_sram_reprogram_clock+0x28/0x30
LR is at omap1_select_table_rate+0x88/0xb4
pc : [<c001b0c4>]    lr : [<c0019f54>]    psr: 600000d3
sp : c035bf10  ip : c035bf20  fp : c035bf1c
r10: c035bfd4  r9 : 54029252  r8 : c03f8120
r7 : c0362b50  r6 : 00b71b00  r5 : c03873cc  r4 : c0362b40
r3 : 00000000  r2 : c0362b40  r1 : 0000010a  r0 : 00002cb0
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32  ISA ARM  Segment kernel
Control: 0000317f  Table: 10004000  DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc035a270)
Stack: (0xc035bf10 to 0xc035c000)
bf00:                                     c035bf3c c035bf20 c0019f54 c001b0ac
bf20: 00001000 00002cb3 00000004 c035ed4c c035bf74 c035bf40 c033ea24 c0019edc
bf40: c02f526c 00000002 00000015 bc058c9b 93111a16 c035335c 02000000 c035ed4c
bf60: c035ed4c c03f8120 c035bf84 c035bf78 c00194c4 c033e8ec c035bfc4 c035bf88
bf80: c033bc24 c00194a0 c035bf90 c035bf98 00000000 00000000 00000000 00000000
bfa0: 00000001 00000000 c0354678 c035ece4 10004000 103532f4 c035bff4 c035bfc8
bfc0: c0338574 c033b598 00000000 00000000 00000000 c035467c 0000317d c035c03c
bfe0: c0354678 c035ece4 00000000 c035bff8 10008040 c0338508 00000000 00000000
Backtrace:
[<c001b09c>] (omap_sram_reprogram_clock+0x0/0x30) from [<c0019f54>] (omap1_select_table_rate+0x88/0xb4)
[<c0019ecc>] (omap1_select_table_rate+0x0/0xb4) from [<c033ea24>] (omap1_clk_init+0x148/0x334)
 r7:c035ed4c r6:00000004 r5:00002cb3 r4:00001000
[<c033e8dc>] (omap1_clk_init+0x0/0x334) from [<c00194c4>] (omap1_init_early+0x34/0x48)
 r8:c03f8120 r7:c035ed4c r6:c035ed4c r5:02000000 r4:c035335c
[<c0019490>] (omap1_init_early+0x0/0x48) from [<c033bc24>] (setup_arch+0x69c/0x79c)
[<c033b588>] (setup_arch+0x0/0x79c) from [<c0338574>] (start_kernel+0x7c/0x2f4)
[<c03384f8>] (start_kernel+0x0/0x2f4) from [<10008040>] (0x10008040)
 r7:c035ece4 r6:c0354678 r5:c035c03c r4:0000317d
Code: 0a000002 e1a0e00f e12fff13 e89da800 (e7f001f2)

Fix this by adding omap1_clk_late_init() that only reprograms dpll1
if the bootloader rate is less than 60MHz. This also allows removing
of the OMAP_CLOCKS_SET_BY_BOOTLOADER option.

Reported-by: Aaro Koskinen <[email-masked]>
Signed-off-by: Tony Lindgren <[email-masked]>

---
This seems to work when forced on OSK5912 at least. Moving the dpll1
reprogramming to happen later should not mess up drivers as it's still
before driver inits. Also the system timer should not change as the
source is ck_ref.
---
diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig
index 7b63462..a7e7775 100644
--- a/arch/arm/configs/omap1_defconfig
+++ b/arch/arm/configs/omap1_defconfig
@@ -48,7 +48,6 @@ CONFIG_MACH_SX1=y
 CONFIG_MACH_NOKIA770=y
 CONFIG_MACH_AMS_DELTA=y
 CONFIG_MACH_OMAP_GENERIC=y
-CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER=y
 CONFIG_OMAP_ARM_216MHZ=y
 CONFIG_OMAP_ARM_195MHZ=y
 CONFIG_OMAP_ARM_192MHZ=y
diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig
index e0a0281..73f287d 100644
--- a/arch/arm/mach-omap1/Kconfig
+++ b/arch/arm/mach-omap1/Kconfig
@@ -171,14 +171,6 @@ config MACH_OMAP_GENERIC
 comment "OMAP CPU Speed"
 	depends on ARCH_OMAP1
 
-config OMAP_CLOCKS_SET_BY_BOOTLOADER
-	bool "OMAP clocks set by bootloader"
-	depends on ARCH_OMAP1
-	help
-	  Enable this option to prevent the kernel from overriding the clock
-	  frequencies programmed by bootloader for MPU, DSP, MMUs, TC,
-	  internal LCD controller and MPU peripherals.
-
 config OMAP_ARM_216MHZ
 	bool "OMAP ARM 216 MHz CPU (1710 only)"
         depends on ARCH_OMAP1 && ARCH_OMAP16XX
diff --git a/arch/arm/mach-omap1/clock.h b/arch/arm/mach-omap1/clock.h
index eaf09ef..16b1423 100644
--- a/arch/arm/mach-omap1/clock.h
+++ b/arch/arm/mach-omap1/clock.h
@@ -17,7 +17,8 @@
 
 #include <plat/clock.h>
 
-extern int __init omap1_clk_init(void);
+int omap1_clk_init(void);
+void omap1_clk_late_init(void);
 extern int omap1_clk_enable(struct clk *clk);
 extern void omap1_clk_disable(struct clk *clk);
 extern long omap1_clk_round_rate(struct clk *clk, unsigned long rate);
diff --git a/arch/arm/mach-omap1/clock_data.c b/arch/arm/mach-omap1/clock_data.c
index 92400b9..1297bb5 100644
--- a/arch/arm/mach-omap1/clock_data.c
+++ b/arch/arm/mach-omap1/clock_data.c
@@ -767,6 +767,15 @@ static struct clk_functions omap1_clk_functions = {
 	.clk_disable_unused	= omap1_clk_disable_unused,
 };
 
+static void __init omap1_show_rates(void)
+{
+	pr_notice("Clocking rate (xtal/DPLL1/MPU): "
+			"%ld.%01ld/%ld.%01ld/%ld.%01ld MHz\n",
+		ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10,
+		ck_dpll1.rate / 1000000, (ck_dpll1.rate / 100000) % 10,
+		arm_ck.rate / 1000000, (arm_ck.rate / 100000) % 10);
+}
+
 int __init omap1_clk_init(void)
 {
 	struct omap_clk *c;
@@ -835,9 +844,12 @@ int __init omap1_clk_init(void)
 	/* We want to be in syncronous scalable mode */
 	omap_writew(0x1000, ARM_SYSST);
 
-#ifdef CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER
-	/* Use values set by bootloader. Determine PLL rate and recalculate
-	 * dependent clocks as if kernel had changed PLL or divisors.
+
+	/*
+	 * Initially use the values set by bootloader. Determine PLL rate and
+	 * recalculate dependent clocks as if kernel had changed PLL or
+	 * divisors. See also omap1_clk_late_init() that can reprogram dpll1
+	 * after the SRAM is initialized.
 	 */
 	{
 		unsigned pll_ctl_val = omap_readw(DPLL_CTL);
@@ -862,25 +874,10 @@ int __init omap1_clk_init(void)
 			}
 		}
 	}
-#else
-	/* Find the highest supported frequency and enable it */
-	if (omap1_select_table_rate(&virtual_ck_mpu, ~0)) {
-		printk(KERN_ERR "System frequencies not set. Check your config.\n");
-		/* Guess sane values (60MHz) */
-		omap_writew(0x2290, DPLL_CTL);
-		omap_writew(cpu_is_omap7xx() ? 0x3005 : 0x1005, ARM_CKCTL);
-		ck_dpll1.rate = 60000000;
-	}
-#endif
 	propagate_rate(&ck_dpll1);
 	/* Cache rates for clocks connected to ck_ref (not dpll1) */
 	propagate_rate(&ck_ref);
-	printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): "
-		"%ld.%01ld/%ld.%01ld/%ld.%01ld MHz\n",
-	       ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10,
-	       ck_dpll1.rate / 1000000, (ck_dpll1.rate / 100000) % 10,
-	       arm_ck.rate / 1000000, (arm_ck.rate / 100000) % 10);
-
+	omap1_show_rates();
 	if (machine_is_omap_perseus2() || machine_is_omap_fsample()) {
 		/* Select slicer output as OMAP input clock */
 		omap_writew(omap_readw(OMAP7XX_PCC_UPLD_CTRL) & ~0x1,
@@ -925,3 +922,21 @@ int __init omap1_clk_init(void)
 
 	return 0;
 }
+
+#define OMAP1_DPLL1_SANE_VALUE	60000000
+
+void __init omap1_clk_late_init(void)
+{
+	if (ck_dpll1.rate >= OMAP1_DPLL1_SANE_VALUE)
+		return;
+
+	/* Find the highest supported frequency and enable it */
+	if (omap1_select_table_rate(&virtual_ck_mpu, ~0)) {
+		pr_err("System frequencies not set, using default. Check your config.\n");
+		omap_writew(0x2290, DPLL_CTL);
+		omap_writew(cpu_is_omap7xx() ? 0x3005 : 0x1005, ARM_CKCTL);
+		ck_dpll1.rate = OMAP1_DPLL1_SANE_VALUE;
+	}
+	propagate_rate(&ck_dpll1);
+	omap1_show_rates();
+}
diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c
index 48ef988..475cb2f 100644
--- a/arch/arm/mach-omap1/devices.c
+++ b/arch/arm/mach-omap1/devices.c
@@ -30,6 +30,8 @@
 #include <plat/omap7xx.h>
 #include <plat/mcbsp.h>
 
+#include "clock.h"
+
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_RTC_DRV_OMAP) || defined(CONFIG_RTC_DRV_OMAP_MODULE)
@@ -293,6 +295,7 @@ static int __init omap1_init_devices(void)
 		return -ENODEV;
 
 	omap_sram_init();
+	omap1_clk_late_init();
 
 	/* please keep these calls, and their implementations above,
 	 * in alphabetical order so they're easier to sort through.