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 <fdtdec.h>
  26#include <linux/compiler.h>
  27#include <asm/io.h>
  28#include <asm/arch/uart.h>
  29#include <asm/arch/clk.h>
  30#include <serial.h>
  31
  32DECLARE_GLOBAL_DATA_PTR;
  33
  34#define RX_FIFO_COUNT_MASK      0xff
  35#define RX_FIFO_FULL_MASK       (1 << 8)
  36#define TX_FIFO_FULL_MASK       (1 << 24)
  37
  38/* Information about a serial port */
  39struct fdt_serial {
  40        u32 base_addr;  /* address of registers in physical memory */
  41        u8 port_id;     /* uart port number */
  42        u8 enabled;     /* 1 if enabled, 0 if disabled */
  43} config __attribute__ ((section(".data")));
  44
  45static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
  46{
  47#ifdef CONFIG_OF_CONTROL
  48        return (struct s5p_uart *)(config.base_addr);
  49#else
  50        u32 offset = dev_index * sizeof(struct s5p_uart);
  51        return (struct s5p_uart *)(samsung_get_base_uart() + offset);
  52#endif
  53}
  54
  55/*
  56 * The coefficient, used to calculate the baudrate on S5P UARTs is
  57 * calculated as
  58 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
  59 * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
  60 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
  61 */
  62static const int udivslot[] = {
  63        0,
  64        0x0080,
  65        0x0808,
  66        0x0888,
  67        0x2222,
  68        0x4924,
  69        0x4a52,
  70        0x54aa,
  71        0x5555,
  72        0xd555,
  73        0xd5d5,
  74        0xddd5,
  75        0xdddd,
  76        0xdfdd,
  77        0xdfdf,
  78        0xffdf,
  79};
  80
  81void serial_setbrg_dev(const int dev_index)
  82{
  83        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
  84        u32 uclk = get_uart_clk(dev_index);
  85        u32 baudrate = gd->baudrate;
  86        u32 val;
  87
  88#if defined(CONFIG_SILENT_CONSOLE) && \
  89                defined(CONFIG_OF_CONTROL) && \
  90                !defined(CONFIG_SPL_BUILD)
  91        if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0))
  92                gd->flags |= GD_FLG_SILENT;
  93#endif
  94
  95        if (!config.enabled)
  96                return;
  97
  98        val = uclk / baudrate;
  99
 100        writel(val / 16 - 1, &uart->ubrdiv);
 101
 102        if (s5p_uart_divslot())
 103                writew(udivslot[val % 16], &uart->rest.slot);
 104        else
 105                writeb(val % 16, &uart->rest.value);
 106}
 107
 108/*
 109 * Initialise the serial port with the given baudrate. The settings
 110 * are always 8 data bits, no parity, 1 stop bit, no start bits.
 111 */
 112int serial_init_dev(const int dev_index)
 113{
 114        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 115
 116        /* enable FIFOs */
 117        writel(0x1, &uart->ufcon);
 118        writel(0, &uart->umcon);
 119        /* 8N1 */
 120        writel(0x3, &uart->ulcon);
 121        /* No interrupts, no DMA, pure polling */
 122        writel(0x245, &uart->ucon);
 123
 124        serial_setbrg_dev(dev_index);
 125
 126        return 0;
 127}
 128
 129static int serial_err_check(const int dev_index, int op)
 130{
 131        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 132        unsigned int mask;
 133
 134        /*
 135         * UERSTAT
 136         * Break Detect [3]
 137         * Frame Err    [2] : receive operation
 138         * Parity Err   [1] : receive operation
 139         * Overrun Err  [0] : receive operation
 140         */
 141        if (op)
 142                mask = 0x8;
 143        else
 144                mask = 0xf;
 145
 146        return readl(&uart->uerstat) & mask;
 147}
 148
 149/*
 150 * Read a single byte from the serial port. Returns 1 on success, 0
 151 * otherwise. When the function is succesfull, the character read is
 152 * written into its argument c.
 153 */
 154int serial_getc_dev(const int dev_index)
 155{
 156        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 157
 158        if (!config.enabled)
 159                return 0;
 160
 161        /* wait for character to arrive */
 162        while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
 163                                         RX_FIFO_FULL_MASK))) {
 164                if (serial_err_check(dev_index, 0))
 165                        return 0;
 166        }
 167
 168        return (int)(readb(&uart->urxh) & 0xff);
 169}
 170
 171/*
 172 * Output a single byte to the serial port.
 173 */
 174void serial_putc_dev(const char c, const int dev_index)
 175{
 176        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 177
 178        if (!config.enabled)
 179                return;
 180
 181        /* wait for room in the tx FIFO */
 182        while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) {
 183                if (serial_err_check(dev_index, 1))
 184                        return;
 185        }
 186
 187        writeb(c, &uart->utxh);
 188
 189        /* If \n, also do \r */
 190        if (c == '\n')
 191                serial_putc('\r');
 192}
 193
 194/*
 195 * Test whether a character is in the RX buffer
 196 */
 197int serial_tstc_dev(const int dev_index)
 198{
 199        struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
 200
 201        if (!config.enabled)
 202                return 0;
 203
 204        return (int)(readl(&uart->utrstat) & 0x1);
 205}
 206
 207void serial_puts_dev(const char *s, const int dev_index)
 208{
 209        while (*s)
 210                serial_putc_dev(*s++, dev_index);
 211}
 212
 213/* Multi serial device functions */
 214#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
 215int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
 216void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
 217int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
 218int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
 219void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
 220void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
 221
 222#define INIT_S5P_SERIAL_STRUCTURE(port, __name) {       \
 223        .name   = __name,                               \
 224        .start  = s5p_serial##port##_init,              \
 225        .stop   = NULL,                                 \
 226        .setbrg = s5p_serial##port##_setbrg,            \
 227        .getc   = s5p_serial##port##_getc,              \
 228        .tstc   = s5p_serial##port##_tstc,              \
 229        .putc   = s5p_serial##port##_putc,              \
 230        .puts   = s5p_serial##port##_puts,              \
 231}
 232
 233DECLARE_S5P_SERIAL_FUNCTIONS(0);
 234struct serial_device s5p_serial0_device =
 235        INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0");
 236DECLARE_S5P_SERIAL_FUNCTIONS(1);
 237struct serial_device s5p_serial1_device =
 238        INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1");
 239DECLARE_S5P_SERIAL_FUNCTIONS(2);
 240struct serial_device s5p_serial2_device =
 241        INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2");
 242DECLARE_S5P_SERIAL_FUNCTIONS(3);
 243struct serial_device s5p_serial3_device =
 244        INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3");
 245
 246#ifdef CONFIG_OF_CONTROL
 247int fdtdec_decode_console(int *index, struct fdt_serial *uart)
 248{
 249        const void *blob = gd->fdt_blob;
 250        int node;
 251
 252        node = fdt_path_offset(blob, "console");
 253        if (node < 0)
 254                return node;
 255
 256        uart->base_addr = fdtdec_get_addr(blob, node, "reg");
 257        if (uart->base_addr == FDT_ADDR_T_NONE)
 258                return -FDT_ERR_NOTFOUND;
 259
 260        uart->port_id = fdtdec_get_int(blob, node, "id", -1);
 261        uart->enabled = fdtdec_get_is_enabled(blob, node);
 262
 263        return 0;
 264}
 265#endif
 266
 267__weak struct serial_device *default_serial_console(void)
 268{
 269#ifdef CONFIG_OF_CONTROL
 270        int index = 0;
 271
 272        if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) {
 273                debug("Cannot decode default console node\n");
 274                return NULL;
 275        }
 276
 277        switch (config.port_id) {
 278        case 0:
 279                return &s5p_serial0_device;
 280        case 1:
 281                return &s5p_serial1_device;
 282        case 2:
 283                return &s5p_serial2_device;
 284        case 3:
 285                return &s5p_serial3_device;
 286        default:
 287                debug("Unknown config.port_id: %d", config.port_id);
 288                break;
 289        }
 290
 291        return NULL;
 292#else
 293        config.enabled = 1;
 294#if defined(CONFIG_SERIAL0)
 295        return &s5p_serial0_device;
 296#elif defined(CONFIG_SERIAL1)
 297        return &s5p_serial1_device;
 298#elif defined(CONFIG_SERIAL2)
 299        return &s5p_serial2_device;
 300#elif defined(CONFIG_SERIAL3)
 301        return &s5p_serial3_device;
 302#else
 303#error "CONFIG_SERIAL? missing."
 304#endif
 305#endif
 306}
 307
 308void s5p_serial_initialize(void)
 309{
 310        serial_register(&s5p_serial0_device);
 311        serial_register(&s5p_serial1_device);
 312        serial_register(&s5p_serial2_device);
 313        serial_register(&s5p_serial3_device);
 314}
 315