diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/Kconfig | 59 | ||||
-rw-r--r-- | drivers/serial/ns16550.c | 98 |
2 files changed, 137 insertions, 20 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index c94353ba6ac..1686a1f951e 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -7,6 +7,65 @@ config DM_SERIAL implements serial_putc() etc. The uclass interface is defined in include/serial.h. +config DEBUG_UART + bool "Enable an early debug UART for debugging" + help + The debug UART is intended for use very early in U-Boot to debug + problems when an ICE or other debug mechanism is not available. + + To use it you should: + - Make sure your UART supports this interface + - Enable CONFIG_DEBUG_UART + - Enable the CONFIG for your UART to tell it to provide this interface + (e.g. CONFIG_DEBUG_UART_NS16550) + - Define the required settings as needed (see below) + - Call debug_uart_init() before use + - Call debug_uart_putc() to output a character + + Depending on your platform it may be possible to use this UART before + a stack is available. + + If your UART does not support this interface you can probably add + support quite easily. Remember that you cannot use driver model and + it is preferred to use no stack. + + You must not use this UART once driver model is working and the + serial drivers are up and running (done in serial_init()). Otherwise + the drivers may conflict and you will get strange output. + +choice + prompt "Select which UART will provide the debug UART" + depends on DEBUG_UART + +config DEBUG_UART_NS16550 + bool "ns16550" + help + Select this to enable a debug UART using the ns16550 driver. You + will need to provide parameters to make this work. The driver will + be available until the real driver model serial is running. + +endchoice + +config DEBUG_UART_BASE + hex "Base address of UART" + depends on DEBUG_UART + help + This is the base address of your UART for memory-mapped UARTs. + + A default should be provided by your board, but if not you will need + to use the correct value here. + +config DEBUG_UART_CLOCK + int "UART input clock" + depends on DEBUG_UART + help + The UART input clock determines the speed of the internal UART + circuitry. The baud rate is derived from this by dividing the input + clock down. + + A default should be provided by your board, but if not you will need + to use the correct value here. + config UNIPHIER_SERIAL bool "UniPhier on-chip UART support" depends on ARCH_UNIPHIER && DM_SERIAL diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 70c946249f0..eb00f1ca8a2 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -55,17 +55,9 @@ DECLARE_GLOBAL_DATA_PTR; #endif /* CONFIG_SYS_NS16550_IER */ #ifdef CONFIG_DM_SERIAL -static void ns16550_writeb(NS16550_t port, int offset, int value) -{ - struct ns16550_platdata *plat = port->plat; - unsigned char *addr; - offset *= 1 << plat->reg_shift; - addr = map_sysmem(plat->base, 0) + offset; - /* - * As far as we know it doesn't make sense to support selection of - * these options at run-time, so use the existing CONFIG options. - */ +static inline void serial_out_shift(unsigned char *addr, int shift, int value) +{ #ifdef CONFIG_SYS_NS16550_PORT_MAPPED outb(value, (ulong)addr); #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) @@ -73,19 +65,14 @@ static void ns16550_writeb(NS16550_t port, int offset, int value) #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) out_be32(addr, value); #elif defined(CONFIG_SYS_BIG_ENDIAN) - writeb(value, addr + (1 << plat->reg_shift) - 1); + writeb(value, addr + (1 << shift) - 1); #else writeb(value, addr); #endif } -static int ns16550_readb(NS16550_t port, int offset) +static inline int serial_in_shift(unsigned char *addr, int shift) { - struct ns16550_platdata *plat = port->plat; - unsigned char *addr; - - offset *= 1 << plat->reg_shift; - addr = map_sysmem(plat->base, 0) + offset; #ifdef CONFIG_SYS_NS16550_PORT_MAPPED return inb((ulong)addr); #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) @@ -93,12 +80,37 @@ static int ns16550_readb(NS16550_t port, int offset) #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) return in_be32(addr); #elif defined(CONFIG_SYS_BIG_ENDIAN) - return readb(addr + (1 << plat->reg_shift) - 1); + return readb(addr + (1 << reg_shift) - 1); #else return readb(addr); #endif } +static void ns16550_writeb(NS16550_t port, int offset, int value) +{ + struct ns16550_platdata *plat = port->plat; + unsigned char *addr; + + offset *= 1 << plat->reg_shift; + addr = map_sysmem(plat->base, 0) + offset; + /* + * As far as we know it doesn't make sense to support selection of + * these options at run-time, so use the existing CONFIG options. + */ + serial_out_shift(addr, plat->reg_shift, value); +} + +static int ns16550_readb(NS16550_t port, int offset) +{ + struct ns16550_platdata *plat = port->plat; + unsigned char *addr; + + offset *= 1 << plat->reg_shift; + addr = map_sysmem(plat->base, 0) + offset; + + return serial_in_shift(addr, plat->reg_shift); +} + /* We can clean these up once everything is moved to driver model */ #define serial_out(value, addr) \ ns16550_writeb(com_port, addr - (unsigned char *)com_port, value) @@ -106,10 +118,15 @@ static int ns16550_readb(NS16550_t port, int offset) ns16550_readb(com_port, addr - (unsigned char *)com_port) #endif -int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) +static inline int calc_divisor(NS16550_t port, int clock, int baudrate) { const unsigned int mode_x_div = 16; + return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); +} + +int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) +{ #ifdef CONFIG_OMAP1510 /* If can't cleanly clock 115200 set div to 1 */ if ((clock == 12000000) && (baudrate == 115200)) { @@ -119,7 +136,7 @@ int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) port->osc_12m_sel = 0; /* clear if previsouly set */ #endif - return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); + return calc_divisor(port, clock, baudrate); } static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) @@ -219,6 +236,47 @@ int NS16550_tstc(NS16550_t com_port) #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ +#ifdef CONFIG_DEBUG_UART_NS16550 + +#include <debug_uart.h> + +void debug_uart_init(void) +{ + struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; + int baud_divisor; + + /* + * We copy the code from above because it is already horribly messy. + * Trying to refactor to nicely remove the duplication doesn't seem + * feasible. The better fix is to move all users of this driver to + * driver model. + */ + baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE); + + serial_out_shift(&com_port->ier, 0, CONFIG_SYS_NS16550_IER); + serial_out_shift(&com_port->mcr, 0, UART_MCRVAL); + serial_out_shift(&com_port->fcr, 0, UART_FCRVAL); + + serial_out_shift(&com_port->lcr, 0, UART_LCR_BKSE | UART_LCRVAL); + serial_out_shift(&com_port->dll, 0, baud_divisor & 0xff); + serial_out_shift(&com_port->dlm, 0, (baud_divisor >> 8) & 0xff); + serial_out_shift(&com_port->lcr, 0, UART_LCRVAL); +} + +static inline void _debug_uart_putc(int ch) +{ + struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; + + while (!(serial_in_shift(&com_port->lsr, 0) & UART_LSR_THRE)) + ; + serial_out_shift(&com_port->thr, 0, ch); +} + +DEBUG_UART_FUNCS + +#endif + #ifdef CONFIG_DM_SERIAL static int ns16550_serial_putc(struct udevice *dev, const char ch) { |