uboot/drivers/serial/serial_s5p.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2009 SAMSUNG Electronics
   3 * Minkyu Kang <mk7.kang@samsung.com>
   4 * Heungjun Kim <riverful.kim@samsung.com>
   5 *
   6 * based on drivers/serial/s3c64xx.c
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (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, MA  02111-1307  USA
  21 *
  22 */
  23
  24#include <common.h>
  25#include <linux/compiler.h>
  26#include <asm/io.h>
  27#include <asm/arch/uart.h>
  28#include <asm/arch/clk.h>
  29#include <serial.h>
  30
  31DECLARE_GLOBAL_DATA_PTR;
  32
  33static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
  34{
  35        u32 offset = dev_index * sizeof(struct s5p_uart);
  36        return (struct s5p_uart *)(samsung_get_base_uart() + offset);
  37}
  38
  39/*
  40 * The coefficient, used to calculate the baudrate on S5P UARTs is
  41 * calculated as
  42 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
  43 * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
  44 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
  45 */
  46static const int udivslot[] = {
  47        0,
  48        0x0080,
  49        0x0808,
  50        0x0888,
  51        0x2222,
  52        0x4924,
  53        0x4a52,
  54        0x54aa,
  55        0x5555,
  56        0xd555,
  57        0xd5d5,
  58        0xddd5,
  59        0xdddd,
  60        0xdfdd,
  61        0xdfdf,
  62        0xffdf,
  63};
  64
  65void serial_setbrg_dev(const int dev_index)
  66{
  67        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
  68        u32 uclk = get_uart_clk(dev_index);
  69        u32 baudrate = gd->baudrate;
  70        u32 val;
  71
  72        val = uclk / baudrate;
  73
  74        writel(val / 16 - 1, &uart->ubrdiv);
  75
  76        if (s5p_uart_divslot())
  77                writew(udivslot[val % 16], &uart->rest.slot);
  78        else
  79                writeb(val % 16, &uart->rest.value);
  80}
  81
  82/*
  83 * Initialise the serial port with the given baudrate. The settings
  84 * are always 8 data bits, no parity, 1 stop bit, no start bits.
  85 */
  86int serial_init_dev(const int dev_index)
  87{
  88        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
  89
  90        /* reset and enable FIFOs, set triggers to the maximum */
  91        writel(0, &uart->ufcon);
  92        writel(0, &uart->umcon);
  93        /* 8N1 */
  94        writel(0x3, &uart->ulcon);
  95        /* No interrupts, no DMA, pure polling */
  96        writel(0x245, &uart->ucon);
  97
  98        serial_setbrg_dev(dev_index);
  99
 100        return 0;
 101}
 102
 103static int serial_err_check(const int dev_index, int op)
 104{
 105        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 106        unsigned int mask;
 107
 108        /*
 109         * UERSTAT
 110         * Break Detect [3]
 111         * Frame Err    [2] : receive operation
 112         * Parity Err   [1] : receive operation
 113         * Overrun Err  [0] : receive operation
 114         */
 115        if (op)
 116                mask = 0x8;
 117        else
 118                mask = 0xf;
 119
 120        return readl(&uart->uerstat) & mask;
 121}
 122
 123/*
 124 * Read a single byte from the serial port. Returns 1 on success, 0
 125 * otherwise. When the function is succesfull, the character read is
 126 * written into its argument c.
 127 */
 128int serial_getc_dev(const int dev_index)
 129{
 130        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 131
 132        /* wait for character to arrive */
 133        while (!(readl(&uart->utrstat) & 0x1)) {
 134                if (serial_err_check(dev_index, 0))
 135                        return 0;
 136        }
 137
 138        return (int)(readb(&uart->urxh) & 0xff);
 139}
 140
 141/*
 142 * Output a single byte to the serial port.
 143 */
 144void serial_putc_dev(const char c, const int dev_index)
 145{
 146        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 147
 148        /* wait for room in the tx FIFO */
 149        while (!(readl(&uart->utrstat) & 0x2)) {
 150                if (serial_err_check(dev_index, 1))
 151                        return;
 152        }
 153
 154        writeb(c, &uart->utxh);
 155
 156        /* If \n, also do \r */
 157        if (c == '\n')
 158                serial_putc('\r');
 159}
 160
 161/*
 162 * Test whether a character is in the RX buffer
 163 */
 164int serial_tstc_dev(const int dev_index)
 165{
 166        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 167
 168        return (int)(readl(&uart->utrstat) & 0x1);
 169}
 170
 171void serial_puts_dev(const char *s, const int dev_index)
 172{
 173        while (*s)
 174                serial_putc_dev(*s++, dev_index);
 175}
 176
 177/* Multi serial device functions */
 178#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
 179int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
 180void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
 181int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
 182int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
 183void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
 184void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
 185
 186#define INIT_S5P_SERIAL_STRUCTURE(port, __name) {       \
 187        .name   = __name,                               \
 188        .start  = s5p_serial##port##_init,              \
 189        .stop   = NULL,                                 \
 190        .setbrg = s5p_serial##port##_setbrg,            \
 191        .getc   = s5p_serial##port##_getc,              \
 192        .tstc   = s5p_serial##port##_tstc,              \
 193        .putc   = s5p_serial##port##_putc,              \
 194        .puts   = s5p_serial##port##_puts,              \
 195}
 196
 197DECLARE_S5P_SERIAL_FUNCTIONS(0);
 198struct serial_device s5p_serial0_device =
 199        INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0");
 200DECLARE_S5P_SERIAL_FUNCTIONS(1);
 201struct serial_device s5p_serial1_device =
 202        INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1");
 203DECLARE_S5P_SERIAL_FUNCTIONS(2);
 204struct serial_device s5p_serial2_device =
 205        INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2");
 206DECLARE_S5P_SERIAL_FUNCTIONS(3);
 207struct serial_device s5p_serial3_device =
 208        INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3");
 209
 210__weak struct serial_device *default_serial_console(void)
 211{
 212#if defined(CONFIG_SERIAL0)
 213        return &s5p_serial0_device;
 214#elif defined(CONFIG_SERIAL1)
 215        return &s5p_serial1_device;
 216#elif defined(CONFIG_SERIAL2)
 217        return &s5p_serial2_device;
 218#elif defined(CONFIG_SERIAL3)
 219        return &s5p_serial3_device;
 220#else
 221#error "CONFIG_SERIAL? missing."
 222#endif
 223}
 224
 225void s5p_serial_initialize(void)
 226{
 227        serial_register(&s5p_serial0_device);
 228        serial_register(&s5p_serial1_device);
 229        serial_register(&s5p_serial2_device);
 230        serial_register(&s5p_serial3_device);
 231}
 232