arm: lpc32xx: Improve early serial initialization
authorKevin Wells <wellsk40@gmail.com>
Fri, 18 Feb 2011 23:05:51 +0000 (15:05 -0800)
committerKevin Wells <wellsk40@gmail.com>
Fri, 18 Feb 2011 23:05:51 +0000 (15:05 -0800)
For the standard serial ports, run the FIFO clear to work around the HW
bug even if the UARTs are not used. For the high speed UARTs, revise the
base clock speed to use PCLK, not the OSC clock. Some code reorganization
for size optimization.

arch/arm/mach-lpc32xx/serial.c

index a0e9ab4..78763ec 100644 (file)
@@ -38,7 +38,6 @@ static struct plat_serial8250_port serial_std_platform_data[] = {
                .membase        = io_p2v(LPC32XX_UART5_BASE),
                .mapbase        = LPC32XX_UART5_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR5,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART |
@@ -50,7 +49,6 @@ static struct plat_serial8250_port serial_std_platform_data[] = {
                .membase        = io_p2v(LPC32XX_UART3_BASE),
                .mapbase        = LPC32XX_UART3_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR3,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART |
@@ -62,7 +60,6 @@ static struct plat_serial8250_port serial_std_platform_data[] = {
                .membase        = io_p2v(LPC32XX_UART4_BASE),
                .mapbase        = LPC32XX_UART4_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR4,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART |
@@ -74,7 +71,6 @@ static struct plat_serial8250_port serial_std_platform_data[] = {
                .membase        = io_p2v(LPC32XX_UART6_BASE),
                .mapbase        = LPC32XX_UART6_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR6,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART |
@@ -88,41 +84,51 @@ struct uartinit {
        char *uart_ck_name;
        u32 ck_mode_mask;
        void __iomem *pdiv_clk_reg;
+       resource_size_t mapbase;
+       bool enabled;
 };
 
 static struct uartinit uartinit_data[] __initdata = {
-#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT
        {
                .uart_ck_name = "uart5_ck",
                .ck_mode_mask =
                        LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5),
                .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL,
-       },
+               .mapbase = LPC32XX_UART5_BASE,
+#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT
+               .enabled = true,
 #endif
-#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT
+       },
        {
                .uart_ck_name = "uart3_ck",
                .ck_mode_mask =
                        LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3),
                .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL,
-       },
+               .mapbase = LPC32XX_UART3_BASE,
+#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT
+               .enabled = true,
 #endif
-#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT
+       },
        {
                .uart_ck_name = "uart4_ck",
                .ck_mode_mask =
                        LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4),
                .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL,
-       },
+               .mapbase = LPC32XX_UART4_BASE,
+#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT
+               .enabled = true,
 #endif
-#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT
+       },
        {
                .uart_ck_name = "uart6_ck",
                .ck_mode_mask =
                        LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6),
                .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL,
-       },
+               .mapbase = LPC32XX_UART6_BASE,
+#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT
+               .enabled = true,
 #endif
+       },
 };
 
 static struct platform_device serial_std_platform_device = {
@@ -140,7 +146,6 @@ static struct uart_port serial_hspd_platform_data[] = {
                .membase        = io_p2v(LPC32XX_HS_UART1_BASE),
                .mapbase        = LPC32XX_HS_UART1_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR1,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF,
@@ -151,7 +156,6 @@ static struct uart_port serial_hspd_platform_data[] = {
                .membase        = io_p2v(LPC32XX_HS_UART2_BASE),
                .mapbase        = LPC32XX_HS_UART2_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR2,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF,
@@ -162,7 +166,6 @@ static struct uart_port serial_hspd_platform_data[] = {
                .membase        = io_p2v(LPC32XX_HS_UART7_BASE),
                .mapbase        = LPC32XX_HS_UART7_BASE,
                .irq            = IRQ_LPC32XX_UART_IIR7,
-               .uartclk        = LPC32XX_MAIN_OSC_FREQ,
                .regshift       = 2,
                .iotype         = UPIO_MEM32,
                .flags          = UPF_BOOT_AUTOCONF,
@@ -186,50 +189,89 @@ static struct platform_device *lpc32xx_serial_devs[] __initdata = {
 
 void __init lpc32xx_serial_init(void)
 {
-       u32 tmp, clkmodes = 0;
+       u32 tmp, tmpclk, clkmodes = 0, clkmodesdone = 0;
        struct clk *clk;
-       unsigned int puart;
-       int i, j;
-
-       /* UART clocks are off, let clock driver manage them */
-       __raw_writel(0, LPC32XX_CLKPWR_UART_CLK_CTRL);
+       resource_size_t puart;
+       int i, j, k = 0;
 
+       /*
+        * For each standard UART, enable the UART clock, and force
+        * FIFO flush. This works around a HW bug with the 8250 where
+        * the FIFOs may not work correctly. This should be done even if
+        * the standard UARTs are not used. The clocks will be disabled
+        * when done.
+        */
        for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) {
                clk = clk_get(NULL, uartinit_data[i].uart_ck_name);
                if (!IS_ERR(clk)) {
                        clk_enable(clk);
-                       serial_std_platform_data[i].uartclk =
-                               clk_get_rate(clk);
-               }
+                       tmpclk = clk_get_rate(clk);
 
-               /* Fall back on main osc rate if clock rate return fails */
-               if (serial_std_platform_data[i].uartclk == 0)
-                       serial_std_platform_data[i].uartclk =
-                               LPC32XX_MAIN_OSC_FREQ;
+                       /*
+                        * Fall back on main osc rate if clock rate return
+                        * fails
+                        */
+                       if (tmpclk == 0)
+                               tmpclk = LPC32XX_MAIN_OSC_FREQ;
 
-               /* Setup UART clock modes for all UARTs, disable autoclock */
-               clkmodes |= uartinit_data[i].ck_mode_mask;
+                       /* pre-UART clock divider set to 1 */
+                       __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg);
 
-               /* pre-UART clock divider set to 1 */
-               __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg);
+                       /* Setup UART clock modes for all UART to always on */
+                       clkmodes = uartinit_data[i].ck_mode_mask;
+                       __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE);
+
+                       /*
+                        * Force a flush of the RX FIFOs to work around a
+                        * HW bug
+                        */
+                       puart = uartinit_data[i].mapbase;
+                       __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
+                       __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
+                       j = LPC32XX_SUART_FIFO_SIZE;
+                       while (j--)
+                               tmp = __raw_readl(
+                                       LPC32XX_UART_DLL_FIFO(puart));
+                       __raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
+
+                       /*
+                        * The 8250 serial drivers do not use any type of
+                        * clock management, so the clocks need to be
+                        * pre-enabled here before the 8250 driver attempts
+                        * to access the peripherals. If a UART is enabled,
+                        * just leave the clock on.
+                        */
+                       if (uartinit_data[i].enabled) {
+                               clkmodesdone |= clkmodes;
+                               serial_std_platform_data[k++].uartclk = tmpclk;
+                       } else {
+                               clk_disable(clk);
+                               clk_put(clk);
+                       }
+               }
        }
 
-       /* This needs to be done after all UART clocks are setup */
-       __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE);
-       for (i = 0; i < ARRAY_SIZE(uartinit_data) - 1; i++) {
-               /* Force a flush of the RX FIFOs to work around a HW bug */
-               puart = serial_std_platform_data[i].mapbase;
-               __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart));
-               __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart));
-               j = LPC32XX_SUART_FIFO_SIZE;
-               while (j--)
-                       tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart));
-               __raw_writel(0, LPC32XX_UART_IIR_FCR(puart));
+       /* Enable clocks only for enabled UARTs */
+       __raw_writel(clkmodesdone, LPC32XX_UARTCTL_CLKMODE);
+
+       /*
+        * Get current PCLK rate and use it for the base clock for all the
+        * high speed UARTS
+        */
+       tmpclk = 0;
+       clk = clk_get(NULL, "pclk_ck");
+       if (!IS_ERR(clk)) {
+               tmpclk = clk_get_rate(clk);
+               clk_put(clk);
        }
+       if (!tmpclk)
+               tmpclk = LPC32XX_MAIN_OSC_FREQ;
 
         /* Setup of HSUART devices */
-        for (i = 0; i < ARRAY_SIZE(serial_hspd_platform_data); i++)
+        for (i = 0; i < ARRAY_SIZE(serial_hspd_platform_data) - 1; i++) {
                 serial_hspd_platform_data[i].line = i;
+                serial_hspd_platform_data[i].uartclk = tmpclk;
+       }
 
        /* Disable UART5->USB transparent mode or USB won't work */
        tmp = __raw_readl(LPC32XX_UARTCTL_CTRL);