Commit 194846f398ef38d5e24c239d264a3f637c685e53

Authored by Michal Simek
Committed by Albert ARIBAUD
1 parent 76abfa5781

serial: Add Zynq serial driver

The driver is used on Xilinx Zynq platform.

Signed-off-by: Michal Simek <monstr@monstr.eu>
CC: Joe Hershberger <joe.hershberger@gmail.com>
CC: Marek Vasut <marex@denx.de>
Acked-by: Marek Vasut <marex@denx.de>

Showing 4 changed files with 261 additions and 0 deletions Side-by-side Diff

... ... @@ -122,6 +122,14 @@
122 122 serial_register(&uartlite_serial3_device);
123 123 # endif /* XILINX_UARTLITE_BASEADDR3 */
124 124 #endif /* CONFIG_XILINX_UARTLITE */
  125 +#if defined(CONFIG_ZYNQ_SERIAL)
  126 +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0
  127 + serial_register(&uart_zynq_serial0_device);
  128 +# endif
  129 +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1
  130 + serial_register(&uart_zynq_serial1_device);
  131 +# endif
  132 +#endif
125 133 serial_assign(default_serial_console()->name);
126 134 }
127 135  
drivers/serial/Makefile
... ... @@ -56,6 +56,7 @@
56 56 COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o
57 57 COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o
58 58 COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
  59 +COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
59 60  
60 61 ifndef CONFIG_SPL_BUILD
61 62 COBJS-$(CONFIG_USB_TTY) += usbtty.o
drivers/serial/serial_zynq.c
  1 +/*
  2 + * Copyright (C) 2012 Michal Simek <monstr@monstr.eu>
  3 + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
  4 + *
  5 + * See file CREDITS for list of people who contributed to this
  6 + * project.
  7 + *
  8 + * This program is free software; you can redistribute it and/or
  9 + * modify it under the terms of the GNU General Public License as
  10 + * published by the Free Software Foundation; either version 2 of
  11 + * the License, or (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 + * You should have received a copy of the GNU General Public License
  19 + * along with this program; if not, write to the Free Software
  20 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21 + * MA 02111-1307 USA
  22 + */
  23 +
  24 +#include <common.h>
  25 +#include <watchdog.h>
  26 +#include <asm/io.h>
  27 +#include <linux/compiler.h>
  28 +#include <serial.h>
  29 +
  30 +#define ZYNQ_UART_SR_TXFULL 0x00000010 /* TX FIFO full */
  31 +#define ZYNQ_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
  32 +
  33 +#define ZYNQ_UART_CR_TX_EN 0x00000010 /* TX enabled */
  34 +#define ZYNQ_UART_CR_RX_EN 0x00000004 /* RX enabled */
  35 +#define ZYNQ_UART_CR_TXRST 0x00000002 /* TX logic reset */
  36 +#define ZYNQ_UART_CR_RXRST 0x00000001 /* RX logic reset */
  37 +
  38 +#define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */
  39 +
  40 +/* Some clock/baud constants */
  41 +#define ZYNQ_UART_BDIV 15 /* Default/reset BDIV value */
  42 +#define ZYNQ_UART_BASECLK 3125000L /* master / (bdiv + 1) */
  43 +
  44 +struct uart_zynq {
  45 + u32 control; /* Control Register [8:0] */
  46 + u32 mode; /* Mode Register [10:0] */
  47 + u32 reserved1[4];
  48 + u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
  49 + u32 reserved2[4];
  50 + u32 channel_sts; /* Channel Status [11:0] */
  51 + u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
  52 + u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
  53 +};
  54 +
  55 +static struct uart_zynq *uart_zynq_ports[2] = {
  56 +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0
  57 + [0] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR0,
  58 +#endif
  59 +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1
  60 + [1] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR1,
  61 +#endif
  62 +};
  63 +
  64 +struct uart_zynq_params {
  65 + u32 baudrate;
  66 + u32 clock;
  67 +};
  68 +
  69 +static struct uart_zynq_params uart_zynq_ports_param[2] = {
  70 +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0) && defined(CONFIG_ZYNQ_SERIAL_CLOCK0)
  71 + [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0,
  72 + [0].clock = CONFIG_ZYNQ_SERIAL_CLOCK0,
  73 +#endif
  74 +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1) && defined(CONFIG_ZYNQ_SERIAL_CLOCK1)
  75 + [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1,
  76 + [1].clock = CONFIG_ZYNQ_SERIAL_CLOCK1,
  77 +#endif
  78 +};
  79 +
  80 +/* Set up the baud rate in gd struct */
  81 +static void uart_zynq_serial_setbrg(const int port)
  82 +{
  83 + /* Calculation results. */
  84 + unsigned int calc_bauderror, bdiv, bgen;
  85 + unsigned long calc_baud = 0;
  86 + unsigned long baud = uart_zynq_ports_param[port].baudrate;
  87 + unsigned long clock = uart_zynq_ports_param[port].clock;
  88 + struct uart_zynq *regs = uart_zynq_ports[port];
  89 +
  90 + /* master clock
  91 + * Baud rate = ------------------
  92 + * bgen * (bdiv + 1)
  93 + *
  94 + * Find acceptable values for baud generation.
  95 + */
  96 + for (bdiv = 4; bdiv < 255; bdiv++) {
  97 + bgen = clock / (baud * (bdiv + 1));
  98 + if (bgen < 2 || bgen > 65535)
  99 + continue;
  100 +
  101 + calc_baud = clock / (bgen * (bdiv + 1));
  102 +
  103 + /*
  104 + * Use first calculated baudrate with
  105 + * an acceptable (<3%) error
  106 + */
  107 + if (baud > calc_baud)
  108 + calc_bauderror = baud - calc_baud;
  109 + else
  110 + calc_bauderror = calc_baud - baud;
  111 + if (((calc_bauderror * 100) / baud) < 3)
  112 + break;
  113 + }
  114 +
  115 + writel(bdiv, &regs->baud_rate_divider);
  116 + writel(bgen, &regs->baud_rate_gen);
  117 +}
  118 +
  119 +/* Initialize the UART, with...some settings. */
  120 +static int uart_zynq_serial_init(const int port)
  121 +{
  122 + struct uart_zynq *regs = uart_zynq_ports[port];
  123 +
  124 + if (!regs)
  125 + return -1;
  126 +
  127 + /* RX/TX enabled & reset */
  128 + writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \
  129 + ZYNQ_UART_CR_RXRST, &regs->control);
  130 + writel(ZYNQ_UART_MR_PARITY_NONE, &regs->mode); /* 8 bit, no parity */
  131 + uart_zynq_serial_setbrg(port);
  132 +
  133 + return 0;
  134 +}
  135 +
  136 +static void uart_zynq_serial_putc(const char c, const int port)
  137 +{
  138 + struct uart_zynq *regs = uart_zynq_ports[port];
  139 +
  140 + while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
  141 + WATCHDOG_RESET();
  142 +
  143 + if (c == '\n') {
  144 + writel('\r', &regs->tx_rx_fifo);
  145 + while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
  146 + WATCHDOG_RESET();
  147 + }
  148 + writel(c, &regs->tx_rx_fifo);
  149 +}
  150 +
  151 +static void uart_zynq_serial_puts(const char *s, const int port)
  152 +{
  153 + while (*s)
  154 + uart_zynq_serial_putc(*s++, port);
  155 +}
  156 +
  157 +static int uart_zynq_serial_tstc(const int port)
  158 +{
  159 + struct uart_zynq *regs = uart_zynq_ports[port];
  160 +
  161 + return (readl(&regs->channel_sts) & ZYNQ_UART_SR_RXEMPTY) == 0;
  162 +}
  163 +
  164 +static int uart_zynq_serial_getc(const int port)
  165 +{
  166 + struct uart_zynq *regs = uart_zynq_ports[port];
  167 +
  168 + while (!uart_zynq_serial_tstc(port))
  169 + WATCHDOG_RESET();
  170 + return readl(&regs->tx_rx_fifo);
  171 +}
  172 +
  173 +#if !defined(CONFIG_SERIAL_MULTI)
  174 +int serial_init(void)
  175 +{
  176 + return uart_zynq_serial_init(0);
  177 +}
  178 +
  179 +void serial_setbrg(void)
  180 +{
  181 + uart_zynq_serial_setbrg(0);
  182 +}
  183 +
  184 +void serial_putc(const char c)
  185 +{
  186 + uart_zynq_serial_putc(c, 0);
  187 +}
  188 +
  189 +void serial_puts(const char *s)
  190 +{
  191 + uart_zynq_serial_puts(s, 0);
  192 +}
  193 +
  194 +int serial_getc(void)
  195 +{
  196 + return uart_zynq_serial_getc(0);
  197 +}
  198 +
  199 +int serial_tstc(void)
  200 +{
  201 + return uart_zynq_serial_tstc(0);
  202 +}
  203 +#else
  204 +/* Multi serial device functions */
  205 +#define DECLARE_PSSERIAL_FUNCTIONS(port) \
  206 + int uart_zynq##port##_init(void) \
  207 + { return uart_zynq_serial_init(port); } \
  208 + void uart_zynq##port##_setbrg(void) \
  209 + { return uart_zynq_serial_setbrg(port); } \
  210 + int uart_zynq##port##_getc(void) \
  211 + { return uart_zynq_serial_getc(port); } \
  212 + int uart_zynq##port##_tstc(void) \
  213 + { return uart_zynq_serial_tstc(port); } \
  214 + void uart_zynq##port##_putc(const char c) \
  215 + { uart_zynq_serial_putc(c, port); } \
  216 + void uart_zynq##port##_puts(const char *s) \
  217 + { uart_zynq_serial_puts(s, port); }
  218 +
  219 +/* Serial device descriptor */
  220 +#define INIT_PSSERIAL_STRUCTURE(port, __name) { \
  221 + .name = __name, \
  222 + .init = uart_zynq##port##_init, \
  223 + .uninit = NULL, \
  224 + .setbrg = uart_zynq##port##_setbrg, \
  225 + .getc = uart_zynq##port##_getc, \
  226 + .tstc = uart_zynq##port##_tstc, \
  227 + .putc = uart_zynq##port##_putc, \
  228 + .puts = uart_zynq##port##_puts, \
  229 +}
  230 +
  231 +DECLARE_PSSERIAL_FUNCTIONS(0);
  232 +struct serial_device uart_zynq_serial0_device =
  233 + INIT_PSSERIAL_STRUCTURE(0, "ttyPS0");
  234 +DECLARE_PSSERIAL_FUNCTIONS(1);
  235 +struct serial_device uart_zynq_serial1_device =
  236 + INIT_PSSERIAL_STRUCTURE(1, "ttyPS1");
  237 +
  238 +__weak struct serial_device *default_serial_console(void)
  239 +{
  240 + if (uart_zynq_ports[0])
  241 + return &uart_zynq_serial0_device;
  242 + if (uart_zynq_ports[1])
  243 + return &uart_zynq_serial1_device;
  244 +
  245 + return NULL;
  246 +}
  247 +#endif
... ... @@ -90,6 +90,11 @@
90 90 extern struct serial_device bfin_serial3_device;
91 91 #endif
92 92  
  93 +#if defined(CONFIG_ZYNQ_SERIAL)
  94 +extern struct serial_device uart_zynq_serial0_device;
  95 +extern struct serial_device uart_zynq_serial1_device;
  96 +#endif
  97 +
93 98 extern void serial_register(struct serial_device *);
94 99 extern void serial_initialize(void);
95 100 extern void serial_stdio_init(void);