uboot/drivers/serial/serial_zynq.c
<<
>>
Prefs
   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
  44struct 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
  55static 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
  64struct uart_zynq_params {
  65        u32 baudrate;
  66        u32 clock;
  67};
  68
  69static 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 */
  81static 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. */
 120static 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
 136static 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
 151static void uart_zynq_serial_puts(const char *s, const int port)
 152{
 153        while (*s)
 154                uart_zynq_serial_putc(*s++, port);
 155}
 156
 157static 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
 164static 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/* Multi serial device functions */
 174#define DECLARE_PSSERIAL_FUNCTIONS(port) \
 175        int uart_zynq##port##_init(void) \
 176                                { return uart_zynq_serial_init(port); } \
 177        void uart_zynq##port##_setbrg(void) \
 178                                { return uart_zynq_serial_setbrg(port); } \
 179        int uart_zynq##port##_getc(void) \
 180                                { return uart_zynq_serial_getc(port); } \
 181        int uart_zynq##port##_tstc(void) \
 182                                { return uart_zynq_serial_tstc(port); } \
 183        void uart_zynq##port##_putc(const char c) \
 184                                { uart_zynq_serial_putc(c, port); } \
 185        void uart_zynq##port##_puts(const char *s) \
 186                                { uart_zynq_serial_puts(s, port); }
 187
 188/* Serial device descriptor */
 189#define INIT_PSSERIAL_STRUCTURE(port, __name) { \
 190          .name   = __name,                     \
 191          .start  = uart_zynq##port##_init,     \
 192          .stop   = NULL,                       \
 193          .setbrg = uart_zynq##port##_setbrg,   \
 194          .getc   = uart_zynq##port##_getc,     \
 195          .tstc   = uart_zynq##port##_tstc,     \
 196          .putc   = uart_zynq##port##_putc,     \
 197          .puts   = uart_zynq##port##_puts,     \
 198}
 199
 200DECLARE_PSSERIAL_FUNCTIONS(0);
 201struct serial_device uart_zynq_serial0_device =
 202        INIT_PSSERIAL_STRUCTURE(0, "ttyPS0");
 203DECLARE_PSSERIAL_FUNCTIONS(1);
 204struct serial_device uart_zynq_serial1_device =
 205        INIT_PSSERIAL_STRUCTURE(1, "ttyPS1");
 206
 207__weak struct serial_device *default_serial_console(void)
 208{
 209        if (uart_zynq_ports[0])
 210                return &uart_zynq_serial0_device;
 211        if (uart_zynq_ports[1])
 212                return &uart_zynq_serial1_device;
 213
 214        return NULL;
 215}
 216
 217void zynq_serial_initalize(void)
 218{
 219#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0
 220        serial_register(&uart_zynq_serial0_device);
 221#endif
 222#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1
 223        serial_register(&uart_zynq_serial1_device);
 224#endif
 225}
 226