1/* 2 * arch/arm/mach-lpc32xx/serial.c 3 * 4 * Author: Kevin Wells <kevin.wells@nxp.com> 5 * 6 * Copyright (C) 2010 NXP Semiconductors 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19#include <linux/kernel.h> 20#include <linux/types.h> 21#include <linux/serial.h> 22#include <linux/serial_core.h> 23#include <linux/serial_reg.h> 24#include <linux/serial_8250.h> 25#include <linux/clk.h> 26#include <linux/io.h> 27 28#include <mach/hardware.h> 29#include <mach/platform.h> 30#include "common.h" 31 32#define LPC32XX_SUART_FIFO_SIZE 64 33 34/* Standard 8250/16550 compatible serial ports */ 35static struct plat_serial8250_port serial_std_platform_data[] = { 36#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT 37 { 38 .membase = io_p2v(LPC32XX_UART5_BASE), 39 .mapbase = LPC32XX_UART5_BASE, 40 .irq = IRQ_LPC32XX_UART_IIR5, 41 .uartclk = LPC32XX_MAIN_OSC_FREQ, 42 .regshift = 2, 43 .iotype = UPIO_MEM32, 44 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | 45 UPF_SKIP_TEST, 46 }, 47#endif 48#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT 49 { 50 .membase = io_p2v(LPC32XX_UART3_BASE), 51 .mapbase = LPC32XX_UART3_BASE, 52 .irq = IRQ_LPC32XX_UART_IIR3, 53 .uartclk = LPC32XX_MAIN_OSC_FREQ, 54 .regshift = 2, 55 .iotype = UPIO_MEM32, 56 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | 57 UPF_SKIP_TEST, 58 }, 59#endif 60#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT 61 { 62 .membase = io_p2v(LPC32XX_UART4_BASE), 63 .mapbase = LPC32XX_UART4_BASE, 64 .irq = IRQ_LPC32XX_UART_IIR4, 65 .uartclk = LPC32XX_MAIN_OSC_FREQ, 66 .regshift = 2, 67 .iotype = UPIO_MEM32, 68 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | 69 UPF_SKIP_TEST, 70 }, 71#endif 72#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT 73 { 74 .membase = io_p2v(LPC32XX_UART6_BASE), 75 .mapbase = LPC32XX_UART6_BASE, 76 .irq = IRQ_LPC32XX_UART_IIR6, 77 .uartclk = LPC32XX_MAIN_OSC_FREQ, 78 .regshift = 2, 79 .iotype = UPIO_MEM32, 80 .flags = UPF_BOOT_AUTOCONF | UPF_BUGGY_UART | 81 UPF_SKIP_TEST, 82 }, 83#endif 84 { }, 85}; 86 87struct uartinit { 88 char *uart_ck_name; 89 u32 ck_mode_mask; 90 void __iomem *pdiv_clk_reg; 91}; 92 93static struct uartinit uartinit_data[] __initdata = { 94#ifdef CONFIG_ARCH_LPC32XX_UART5_SELECT 95 { 96 .uart_ck_name = "uart5_ck", 97 .ck_mode_mask = 98 LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5), 99 .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL, 100 }, 101#endif 102#ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT 103 { 104 .uart_ck_name = "uart3_ck", 105 .ck_mode_mask = 106 LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3), 107 .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL, 108 }, 109#endif 110#ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT 111 { 112 .uart_ck_name = "uart4_ck", 113 .ck_mode_mask = 114 LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4), 115 .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL, 116 }, 117#endif 118#ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT 119 { 120 .uart_ck_name = "uart6_ck", 121 .ck_mode_mask = 122 LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6), 123 .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL, 124 }, 125#endif 126}; 127 128static struct platform_device serial_std_platform_device = { 129 .name = "serial8250", 130 .id = 0, 131 .dev = { 132 .platform_data = serial_std_platform_data, 133 }, 134}; 135 136static struct platform_device *lpc32xx_serial_devs[] __initdata = { 137 &serial_std_platform_device, 138}; 139 140void __init lpc32xx_serial_init(void) 141{ 142 u32 tmp, clkmodes = 0; 143 struct clk *clk; 144 unsigned int puart; 145 int i, j; 146 147 /* UART clocks are off, let clock driver manage them */ 148 __raw_writel(0, LPC32XX_CLKPWR_UART_CLK_CTRL); 149 150 for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { 151 clk = clk_get(NULL, uartinit_data[i].uart_ck_name); 152 if (!IS_ERR(clk)) { 153 clk_enable(clk); 154 serial_std_platform_data[i].uartclk = 155 clk_get_rate(clk); 156 } 157 158 /* Fall back on main osc rate if clock rate return fails */ 159 if (serial_std_platform_data[i].uartclk == 0) 160 serial_std_platform_data[i].uartclk = 161 LPC32XX_MAIN_OSC_FREQ; 162 163 /* Setup UART clock modes for all UARTs, disable autoclock */ 164 clkmodes |= uartinit_data[i].ck_mode_mask; 165 166 /* pre-UART clock divider set to 1 */ 167 __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg); 168 } 169 170 /* This needs to be done after all UART clocks are setup */ 171 __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE); 172 for (i = 0; i < ARRAY_SIZE(uartinit_data) - 1; i++) { 173 puart = serial_std_platform_data[i].mapbase; 174 __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); 175 __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart)); 176 j = LPC32XX_SUART_FIFO_SIZE; 177 while (j--) 178 tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart)); 179 __raw_writel(0, LPC32XX_UART_IIR_FCR(puart)); 180 } 181 182 /* Disable UART5->USB transparent mode or USB won't work */ 183 tmp = __raw_readl(LPC32XX_UARTCTL_CTRL); 184 tmp &= ~LPC32XX_UART_U5_ROUTE_TO_USB; 185 __raw_writel(tmp, LPC32XX_UARTCTL_CTRL); 186 187 platform_add_devices(lpc32xx_serial_devs, 188 ARRAY_SIZE(lpc32xx_serial_devs)); 189} 190