[PATCH] Switch the i.MX27's PLL in a safe manner

Juergen Beisert jbe at pengutronix.de
Fri Oct 8 05:02:43 EDT 2010


Changing PLL settings is somehow tricky on the i.MX27: Whenever the clock
speed of the main PLL is changed, the clock stops for about 100 us until the
PLL locks into the new frequency. While this clock stop, also the SDRAM
controller cannot refresh the memory, because it uses the same clock source.
This can lead into data loss and random system crashes.

This patch divides the PLL setting in two steps. First step is to re-program
the PLL and clock settings to values possible at a core supply of 1.25 V.
Second step is to increase the core power supply to 1.45 V and switch the
CPU clock to the specified 400 MHz.

Signed-off-by: Juergen Beisert <jbe at pengutronix.de>
---
 arch/arm/boards/pcm038/Makefile   |    2 +-
 arch/arm/boards/pcm038/lowlevel.c |    7 ++++
 arch/arm/boards/pcm038/pcm038.c   |   65 +++++++++++++++++++---------------
 arch/arm/boards/pcm038/pll.h      |   70 +++++++++++++++++++++++++++++++++++++
 arch/arm/boards/pcm038/pll_init.S |   48 -------------------------
 5 files changed, 114 insertions(+), 78 deletions(-)
 create mode 100644 arch/arm/boards/pcm038/pll.h
 delete mode 100644 arch/arm/boards/pcm038/pll_init.S

diff --git a/arch/arm/boards/pcm038/Makefile b/arch/arm/boards/pcm038/Makefile
index a681dda..970804e 100644
--- a/arch/arm/boards/pcm038/Makefile
+++ b/arch/arm/boards/pcm038/Makefile
@@ -1,3 +1,3 @@
 
-obj-y += lowlevel.o pll_init.o
+obj-y += lowlevel.o
 obj-y += pcm038.o
diff --git a/arch/arm/boards/pcm038/lowlevel.c b/arch/arm/boards/pcm038/lowlevel.c
index eb85e8f..b50e1c8 100644
--- a/arch/arm/boards/pcm038/lowlevel.c
+++ b/arch/arm/boards/pcm038/lowlevel.c
@@ -31,6 +31,8 @@
 #include <asm/system.h>
 #include <asm-generic/memory_layout.h>
 
+#include "pll.h"
+
 #ifdef CONFIG_NAND_IMX_BOOT
 static void __bare_init __naked insdram(void)
 {
@@ -68,6 +70,11 @@ void __bare_init __naked board_init_lowlevel(void)
 	if (r > 0xa0000000 && r < 0xb0000000)
 		board_init_lowlevel_return();
 
+	/* re-program the PLL prior(!) starting the SDRAM controller */
+	MPCTL0 = MPCTL0_VAL;
+	SPCTL0 = SPCTL0_VAL;
+	CSCR = CSCR_VAL | CSCR_UPDATE_DIS | CSCR_MPLL_RESTART | CSCR_SPLL_RESTART;
+
 	/*
 	 * DDR on CSD0
 	 */
diff --git a/arch/arm/boards/pcm038/pcm038.c b/arch/arm/boards/pcm038/pcm038.c
index fda3262..3a9b413 100644
--- a/arch/arm/boards/pcm038/pcm038.c
+++ b/arch/arm/boards/pcm038/pcm038.c
@@ -44,6 +44,8 @@
 #include <mach/spi.h>
 #include <mach/iomux-mx27.h>
 
+#include "pll.h"
+
 static struct device_d cfi_dev = {
 	.id	  = -1,
 	.name     = "cfi_flash",
@@ -379,11 +381,6 @@ static struct device_d pcm038_serial_device = {
 
 static int pcm038_console_init(void)
 {
-	/* bring PLLs to reset default */
-	MPCTL0 = 0x00211803;
-	SPCTL0 = 0x1002700c;
-	CSCR = 0x33fc1307;
-
 	register_device(&pcm038_serial_device);
 
 	return 0;
@@ -391,40 +388,50 @@ static int pcm038_console_init(void)
 
 console_initcall(pcm038_console_init);
 
-extern void *pcm038_pll_init, *pcm038_pll_init_end;
-
-static int pcm038_power_init(void)
+/**
+ * The spctl0 register is a beast: Seems you can read it
+ * only one times without writing it again.
+ */
+static inline uint32_t get_pll_spctl10(void)
 {
-	int ret;
-	void *vram = (void*)0xffff4c00;
-	void (*pllfunc)(void) = vram;
+	uint32_t reg;
 
-	printf("initialising PLLs: 0x%p 0x%p\n", &pcm038_pll_init);
+	reg = SPCTL0;
+	SPCTL0 = reg;
 
-	memcpy(vram, &pcm038_pll_init, 0x100);
+	return reg;
+}
 
-	console_flush();
+/**
+ * If the PLL settings are in place switch the CPU core frequency to the max. value
+ */
+static int pcm038_power_init(void)
+{
+	uint32_t spctl0;
+	int ret;
 
-	ret = pmic_power();
-	if (ret) {
-		printf("Failed to initialize PMIC. Will continue with low CPU speed\n");
-		return 0;
+	spctl0 = get_pll_spctl10();
+
+	/* PLL registers already set to their final values? */
+	if (spctl0 == SPCTL0_VAL && MPCTL0 == MPCTL0_VAL) {
+		console_flush();
+		ret = pmic_power();
+		if (ret == 0) {
+			/* wait for required power level to run the CPU at 400 MHz */
+			udelay(100000);
+			CSCR = CSCR_VAL_FINAL;
+			PCDR0 = 0x130410c3;
+			PCDR1 = 0x09030911;
+			/* Clocks have changed. Notify clients */
+			clock_notifier_call_chain();
+		} else {
+			printf("Failed to initialize PMIC. Will continue with low CPU speed\n");
+		}
 	}
 
-	/* wait for good power level */
-	udelay(100000);
-
-	pllfunc();
-
 	/* clock gating enable */
 	GPCR = 0x00050f08;
 
-	PCDR0 = 0x130410c3;
-	PCDR1 = 0x09030911;
-
-	/* Clocks have changed. Notify clients */
-	clock_notifier_call_chain();
-
 	return 0;
 }
 
diff --git a/arch/arm/boards/pcm038/pll.h b/arch/arm/boards/pcm038/pll.h
new file mode 100644
index 0000000..13a7989
--- /dev/null
+++ b/arch/arm/boards/pcm038/pll.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/**
+ * @file
+ * @brief phyCORE-i.MX27 specific PLL setup
+ */
+
+#ifndef __PCM038_PLL_H
+#define __PCM038_PLL_H
+
+/* define the PLL setting we want to run the system  */
+
+/* main clock divider settings immediately after reset (at 1.25 V core supply) */
+#define CSCR_VAL (CSCR_USB_DIV(3) |	\
+		CSCR_SD_CNT(3) |	\
+		CSCR_MSHC_SEL |		\
+		CSCR_H264_SEL |		\
+		CSCR_SSI1_SEL |		\
+		CSCR_SSI2_SEL |		\
+		CSCR_SP_SEL | /* 26 MHz reference */ \
+		CSCR_MCU_SEL | /* 26 MHz reference */ \
+		CSCR_ARM_DIV(0) | /* CPU runs at MPLL/3 clock */ \
+		CSCR_AHB_DIV(1) | /* AHB runs at MPLL/6 clock */ \
+		CSCR_SPEN |		\
+		CSCR_MPEN)
+
+/* main clock divider settings after core voltage increases to 1.45 V */
+#define CSCR_VAL_FINAL (CSCR_USB_DIV(3) |	\
+		CSCR_SD_CNT(3) |	\
+		CSCR_MSHC_SEL |		\
+		CSCR_H264_SEL |		\
+		CSCR_SSI1_SEL |		\
+		CSCR_SSI2_SEL |		\
+		CSCR_SP_SEL | /* 26 MHz reference */ \
+		CSCR_MCU_SEL | /* 26 MHz reference */ \
+		CSCR_ARM_SRC_MPLL | /* use main MPLL clock */ \
+		CSCR_ARM_DIV(0) | /* CPU run at full MPLL clock */ \
+		CSCR_AHB_DIV(1) | /* AHB runs at MPLL/6 clock */ \
+		CSCR_SPEN |		\
+		CSCR_MPEN)
+
+/* MPLL should provide a 399 MHz clock from the 26 MHz reference */
+#define MPCTL0_VAL (IMX_PLL_PD(0) |	\
+		IMX_PLL_MFD(51) |	\
+		IMX_PLL_MFI(7) |	\
+		IMX_PLL_MFN(35))
+
+/* SPLL should provide a 240 MHz clock from the 26 MHz reference */
+#define SPCTL0_VAL (IMX_PLL_PD(1) |	\
+		IMX_PLL_MFD(12) |	\
+		IMX_PLL_MFI(9) |	\
+		IMX_PLL_MFN(3))
+
+
+#endif	/* __PCM038_PLL_H */
diff --git a/arch/arm/boards/pcm038/pll_init.S b/arch/arm/boards/pcm038/pll_init.S
deleted file mode 100644
index 0c1ff13..0000000
--- a/arch/arm/boards/pcm038/pll_init.S
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <config.h>
-#include <mach/imx-regs.h>
-#include <mach/imx-pll.h>
-#include <linux/linkage.h>
-
-#define writel(val, reg) \
-	ldr		r0,	=reg;	\
-	ldr		r1,	=val;	\
-	str		r1,   [r0];
-
-#define CSCR_VAL CSCR_USB_DIV(3) |	\
-		 CSCR_SD_CNT(3) |	\
-		 CSCR_MSHC_SEL |	\
-		 CSCR_H264_SEL |	\
-		 CSCR_SSI1_SEL |	\
-		 CSCR_SSI2_SEL |	\
-		 CSCR_MCU_SEL |		\
-		 CSCR_ARM_SRC_MPLL |	\
-		 CSCR_SP_SEL |		\
-		 CSCR_ARM_DIV(0) |	\
-		 CSCR_FPM_EN |		\
-		 CSCR_SPEN |		\
-		 CSCR_MPEN |		\
-		 CSCR_AHB_DIV(1)
-
-ENTRY(pcm038_pll_init)
-
-	writel(IMX_PLL_PD(0) |
-		 IMX_PLL_MFD(51) |
-		 IMX_PLL_MFI(7) |
-		 IMX_PLL_MFN(35), MPCTL0) /* 399 MHz */
-
-	writel(IMX_PLL_PD(1) |
-		 IMX_PLL_MFD(12) |
-		 IMX_PLL_MFI(9) |
-		 IMX_PLL_MFN(3), SPCTL0) /* SPLL = 2 * 26 * 4.61538 MHz = 240 MHz */
-
-	writel(CSCR_VAL | CSCR_MPLL_RESTART | CSCR_SPLL_RESTART, CSCR)
-
-	ldr r2, =16000
-1:
-	subs    r2, r2, #1
-	nop
-	bcs 1b
-
-	mov     pc, lr
-ENDPROC(pcm038_pll_init)
-
-- 
1.7.2.3




More information about the barebox mailing list