|
@@ -12,125 +12,86 @@
|
|
|
/* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
|
|
|
|
|
|
#include <common.h>
|
|
|
+#include <errno.h>
|
|
|
#include <watchdog.h>
|
|
|
#include <asm/io.h>
|
|
|
#include <serial.h>
|
|
|
+#include <serial_pl01x.h>
|
|
|
#include <linux/compiler.h>
|
|
|
-#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 */
|