From aed2fbef5e9a0ab5a7cd01e742039a962f0b24ef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 22 Sep 2014 17:30:57 -0600 Subject: [PATCH] dm: serial: Tidy up the pl01x driver Adjust the driver so that leaf functions take a pointer to the serial port register base. Put all the global configuration in the init function, and use the same settings from then on. This makes it much easier to move to driver model without duplicating the code, since with driver model we use platform data rather than global settings. The driver is compiled with either the CONFIG_PL010_SERIAL or CONFIG_PL011_SERIAL option and this determines the uart type. With driver model this needs to come in from platform data, so create a new CONFIG_PL01X_SERIAL config which brings in the driver, and adjust the driver to support both peripheral variants. Signed-off-by: Simon Glass Tested-by: Stephen Warren --- drivers/serial/Makefile | 5 +- drivers/serial/serial_pl01x.c | 300 ++++++++++-------- ...serial_pl01x.h => serial_pl01x_internal.h} | 0 include/serial_pl01x.h | 27 ++ 4 files changed, 192 insertions(+), 140 deletions(-) rename drivers/serial/{serial_pl01x.h => serial_pl01x_internal.h} (100%) create mode 100644 include/serial_pl01x.h diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index b4f299bb0e..17c56ea66e 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -7,8 +7,11 @@ ifdef CONFIG_DM_SERIAL obj-y += serial-uclass.o +obj-$(CONFIG_PL01X_SERIAL) += serial_pl01x.o else obj-y += serial.o +obj-$(CONFIG_PL010_SERIAL) += serial_pl01x.o +obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o endif @@ -25,8 +28,6 @@ obj-$(CONFIG_IMX_SERIAL) += serial_imx.o obj-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o obj-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o obj-$(CONFIG_MXC_UART) += serial_mxc.o -obj-$(CONFIG_PL010_SERIAL) += serial_pl01x.o -obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o obj-$(CONFIG_PXA_SERIAL) += serial_pxa.o obj-$(CONFIG_SA1100_SERIAL) += serial_sa1100.o obj-$(CONFIG_S3C24X0_SERIAL) += serial_s3c24x0.o diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index dfb610e1a9..665a0e48b1 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -12,125 +12,86 @@ /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ #include +#include #include #include #include +#include #include -#include "serial_pl01x.h" +#include "serial_pl01x_internal.h" -/* - * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1 - * Integrator CP has two UARTs, use the first one, at 38400-8-N-1 - * Versatile PB has four UARTs. - */ -#define CONSOLE_PORT CONFIG_CONS_INDEX static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS; +static enum pl01x_type pl01x_type __attribute__ ((section(".data"))); +static struct pl01x_regs *base_regs __attribute__ ((section(".data"))); #define NUM_PORTS (sizeof(port)/sizeof(port[0])) -static void pl01x_putc (int portnum, char c); -static int pl01x_getc (int portnum); -static int pl01x_tstc (int portnum); -unsigned int baudrate = CONFIG_BAUDRATE; DECLARE_GLOBAL_DATA_PTR; -static struct pl01x_regs *pl01x_get_regs(int portnum) +static int pl01x_putc(struct pl01x_regs *regs, char c) { - return (struct pl01x_regs *) port[portnum]; -} - -#ifdef CONFIG_PL010_SERIAL - -static int pl01x_serial_init(void) -{ - struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); - unsigned int divisor; - - /* First, disable everything */ - writel(0, ®s->pl010_cr); + /* Wait until there is space in the FIFO */ + if (readl(®s->fr) & UART_PL01x_FR_TXFF) + return -EAGAIN; - /* Set baud rate */ - switch (baudrate) { - case 9600: - divisor = UART_PL010_BAUD_9600; - break; + /* Send the character */ + writel(c, ®s->dr); - case 19200: - divisor = UART_PL010_BAUD_9600; - break; + return 0; +} - case 38400: - divisor = UART_PL010_BAUD_38400; - break; +static int pl01x_getc(struct pl01x_regs *regs) +{ + unsigned int data; - case 57600: - divisor = UART_PL010_BAUD_57600; - break; + /* Wait until there is data in the FIFO */ + if (readl(®s->fr) & UART_PL01x_FR_RXFE) + return -EAGAIN; - case 115200: - divisor = UART_PL010_BAUD_115200; - break; + data = readl(®s->dr); - default: - divisor = UART_PL010_BAUD_38400; + /* Check for an error flag */ + if (data & 0xFFFFFF00) { + /* Clear the error */ + writel(0xFFFFFFFF, ®s->ecr); + return -1; } - writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); - writel(divisor & 0xff, ®s->pl010_lcrl); - - /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ - writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, ®s->pl010_lcrh); - - /* Finally, enable the UART */ - writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); - - return 0; + return (int) data; } -#endif /* CONFIG_PL010_SERIAL */ - -#ifdef CONFIG_PL011_SERIAL +static int pl01x_tstc(struct pl01x_regs *regs) +{ + WATCHDOG_RESET(); + return !(readl(®s->fr) & UART_PL01x_FR_RXFE); +} -static int pl01x_serial_init(void) +static int pl01x_generic_serial_init(struct pl01x_regs *regs, + enum pl01x_type type) { - struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); - unsigned int temp; - unsigned int divider; - unsigned int remainder; - unsigned int fraction; unsigned int lcr; #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT - /* Empty RX fifo if necessary */ - if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { - while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) - readl(®s->dr); + if (type == TYPE_PL011) { + /* Empty RX fifo if necessary */ + if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { + while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) + readl(®s->dr); + } } #endif /* First, disable everything */ - writel(0, ®s->pl011_cr); - - /* - * Set baud rate - * - * IBRD = UART_CLK / (16 * BAUD_RATE) - * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE)) - */ - temp = 16 * baudrate; - divider = CONFIG_PL011_CLOCK / temp; - remainder = CONFIG_PL011_CLOCK % temp; - temp = (8 * remainder) / baudrate; - fraction = (temp >> 1) + (temp & 1); - - writel(divider, ®s->pl011_ibrd); - writel(fraction, ®s->pl011_fbrd); + writel(0, ®s->pl010_cr); /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; writel(lcr, ®s->pl011_lcrh); + switch (type) { + case TYPE_PL010: + break; + case TYPE_PL011: { #ifdef CONFIG_PL011_SERIAL_RLCR - { int i; /* @@ -144,90 +105,151 @@ static int pl01x_serial_init(void) writel(lcr, ®s->pl011_rlcr); /* lcrh needs to be set again for change to be effective */ writel(lcr, ®s->pl011_lcrh); - } #endif - /* Finally, enable the UART */ - writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE | - UART_PL011_CR_RTS, ®s->pl011_cr); + break; + } + default: + return -EINVAL; + } return 0; } -#endif /* CONFIG_PL011_SERIAL */ - -static void pl01x_serial_putc(const char c) +static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type, + int clock, int baudrate) { - if (c == '\n') - pl01x_putc (CONSOLE_PORT, '\r'); + switch (type) { + case TYPE_PL010: { + unsigned int divisor; + + switch (baudrate) { + case 9600: + divisor = UART_PL010_BAUD_9600; + break; + case 19200: + divisor = UART_PL010_BAUD_9600; + break; + case 38400: + divisor = UART_PL010_BAUD_38400; + break; + case 57600: + divisor = UART_PL010_BAUD_57600; + break; + case 115200: + divisor = UART_PL010_BAUD_115200; + break; + default: + divisor = UART_PL010_BAUD_38400; + } + + writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); + writel(divisor & 0xff, ®s->pl010_lcrl); + + /* Finally, enable the UART */ + writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); + break; + } + case TYPE_PL011: { + unsigned int temp; + unsigned int divider; + unsigned int remainder; + unsigned int fraction; - pl01x_putc (CONSOLE_PORT, c); -} + /* + * Set baud rate + * + * IBRD = UART_CLK / (16 * BAUD_RATE) + * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) + * / (16 * BAUD_RATE)) + */ + temp = 16 * baudrate; + divider = clock / temp; + remainder = clock % temp; + temp = (8 * remainder) / baudrate; + fraction = (temp >> 1) + (temp & 1); + + writel(divider, ®s->pl011_ibrd); + writel(fraction, ®s->pl011_fbrd); + + /* Finally, enable the UART */ + writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | + UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr); + break; + } + default: + return -EINVAL; + } -static int pl01x_serial_getc(void) -{ - return pl01x_getc (CONSOLE_PORT); + return 0; } -static int pl01x_serial_tstc(void) +#ifndef CONFIG_DM_SERIAL +static void pl01x_serial_init_baud(int baudrate) { - return pl01x_tstc (CONSOLE_PORT); + int clock = 0; + +#if defined(CONFIG_PL010_SERIAL) + pl01x_type = TYPE_PL010; +#elif defined(CONFIG_PL011_SERIAL) + pl01x_type = TYPE_PL011; + clock = CONFIG_PL011_CLOCK; +#endif + base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX]; + + pl01x_generic_serial_init(base_regs, pl01x_type); + pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate); } -static void pl01x_serial_setbrg(void) +/* + * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1 + * Integrator CP has two UARTs, use the first one, at 38400-8-N-1 + * Versatile PB has four UARTs. + */ +int pl01x_serial_init(void) { - struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); + pl01x_serial_init_baud(CONFIG_BAUDRATE); - baudrate = gd->baudrate; - /* - * Flush FIFO and wait for non-busy before changing baudrate to avoid - * crap in console - */ - while (!(readl(®s->fr) & UART_PL01x_FR_TXFE)) - WATCHDOG_RESET(); - while (readl(®s->fr) & UART_PL01x_FR_BUSY) - WATCHDOG_RESET(); - serial_init(); + return 0; } -static void pl01x_putc (int portnum, char c) +static void pl01x_serial_putc(const char c) { - struct pl01x_regs *regs = pl01x_get_regs(portnum); - - /* Wait until there is space in the FIFO */ - while (readl(®s->fr) & UART_PL01x_FR_TXFF) - WATCHDOG_RESET(); + if (c == '\n') + while (pl01x_putc(base_regs, '\r') == -EAGAIN); - /* Send the character */ - writel(c, ®s->dr); + while (pl01x_putc(base_regs, c) == -EAGAIN); } -static int pl01x_getc (int portnum) +static int pl01x_serial_getc(void) { - struct pl01x_regs *regs = pl01x_get_regs(portnum); - unsigned int data; + while (1) { + int ch = pl01x_getc(base_regs); - /* Wait until there is data in the FIFO */ - while (readl(®s->fr) & UART_PL01x_FR_RXFE) - WATCHDOG_RESET(); + if (ch == -EAGAIN) { + WATCHDOG_RESET(); + continue; + } - data = readl(®s->dr); - - /* Check for an error flag */ - if (data & 0xFFFFFF00) { - /* Clear the error */ - writel(0xFFFFFFFF, ®s->ecr); - return -1; + return ch; } - - return (int) data; } -static int pl01x_tstc (int portnum) +static int pl01x_serial_tstc(void) { - struct pl01x_regs *regs = pl01x_get_regs(portnum); + return pl01x_tstc(base_regs); +} - WATCHDOG_RESET(); - return !(readl(®s->fr) & UART_PL01x_FR_RXFE); +static void pl01x_serial_setbrg(void) +{ + /* + * Flush FIFO and wait for non-busy before changing baudrate to avoid + * crap in console + */ + while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE)) + WATCHDOG_RESET(); + while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY) + WATCHDOG_RESET(); + pl01x_serial_init_baud(gd->baudrate); } static struct serial_device pl01x_serial_drv = { @@ -250,3 +272,5 @@ __weak struct serial_device *default_serial_console(void) { return &pl01x_serial_drv; } + +#endif /* nCONFIG_DM_SERIAL */ diff --git a/drivers/serial/serial_pl01x.h b/drivers/serial/serial_pl01x_internal.h similarity index 100% rename from drivers/serial/serial_pl01x.h rename to drivers/serial/serial_pl01x_internal.h diff --git a/include/serial_pl01x.h b/include/serial_pl01x.h new file mode 100644 index 0000000000..5e068f390b --- /dev/null +++ b/include/serial_pl01x.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014 Google, Inc + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __serial_pl01x_h +#define __serial_pl01x_h + +enum pl01x_type { + TYPE_PL010, + TYPE_PL011, +}; + +/* + *Information about a serial port + * + * @base: Register base address + * @type: Port type + * @clock: Input clock rate, used for calculating the baud rate divisor + */ +struct pl01x_serial_platdata { + unsigned long base; + enum pl01x_type type; + unsigned int clock; +}; + +#endif -- 2.39.5