123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /*
- * Copyright (C) 2012 Michal Simek <monstr@monstr.eu>
- * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
- #include <common.h>
- #include <fdtdec.h>
- #include <watchdog.h>
- #include <asm/io.h>
- #include <linux/compiler.h>
- #include <serial.h>
- #include <asm/arch/clk.h>
- #include <asm/arch/hardware.h>
- DECLARE_GLOBAL_DATA_PTR;
- #define ZYNQ_UART_SR_TXFULL 0x00000010 /* TX FIFO full */
- #define ZYNQ_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
- #define ZYNQ_UART_CR_TX_EN 0x00000010 /* TX enabled */
- #define ZYNQ_UART_CR_RX_EN 0x00000004 /* RX enabled */
- #define ZYNQ_UART_CR_TXRST 0x00000002 /* TX logic reset */
- #define ZYNQ_UART_CR_RXRST 0x00000001 /* RX logic reset */
- #define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */
- struct uart_zynq {
- u32 control; /* Control Register [8:0] */
- u32 mode; /* Mode Register [10:0] */
- u32 reserved1[4];
- u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
- u32 reserved2[4];
- u32 channel_sts; /* Channel Status [11:0] */
- u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
- u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
- };
- static struct uart_zynq *uart_zynq_ports[2] = {
- [0] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR0,
- [1] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR1,
- };
- #if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0)
- # define CONFIG_ZYNQ_SERIAL_BAUDRATE0 CONFIG_BAUDRATE
- #endif
- #if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1)
- # define CONFIG_ZYNQ_SERIAL_BAUDRATE1 CONFIG_BAUDRATE
- #endif
- struct uart_zynq_params {
- u32 baudrate;
- };
- static struct uart_zynq_params uart_zynq_ports_param[2] = {
- [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0,
- [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1,
- };
- /* Set up the baud rate in gd struct */
- static void uart_zynq_serial_setbrg(const int port)
- {
- /* Calculation results. */
- unsigned int calc_bauderror, bdiv, bgen;
- unsigned long calc_baud = 0;
- unsigned long baud = uart_zynq_ports_param[port].baudrate;
- unsigned long clock = get_uart_clk(port);
- struct uart_zynq *regs = uart_zynq_ports[port];
- /* master clock
- * Baud rate = ------------------
- * bgen * (bdiv + 1)
- *
- * Find acceptable values for baud generation.
- */
- for (bdiv = 4; bdiv < 255; bdiv++) {
- bgen = clock / (baud * (bdiv + 1));
- if (bgen < 2 || bgen > 65535)
- continue;
- calc_baud = clock / (bgen * (bdiv + 1));
- /*
- * Use first calculated baudrate with
- * an acceptable (<3%) error
- */
- if (baud > calc_baud)
- calc_bauderror = baud - calc_baud;
- else
- calc_bauderror = calc_baud - baud;
- if (((calc_bauderror * 100) / baud) < 3)
- break;
- }
- writel(bdiv, ®s->baud_rate_divider);
- writel(bgen, ®s->baud_rate_gen);
- }
- /* Initialize the UART, with...some settings. */
- static int uart_zynq_serial_init(const int port)
- {
- struct uart_zynq *regs = uart_zynq_ports[port];
- if (!regs)
- return -1;
- /* RX/TX enabled & reset */
- writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \
- ZYNQ_UART_CR_RXRST, ®s->control);
- writel(ZYNQ_UART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */
- uart_zynq_serial_setbrg(port);
- return 0;
- }
- static void uart_zynq_serial_putc(const char c, const int port)
- {
- struct uart_zynq *regs = uart_zynq_ports[port];
- while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
- WATCHDOG_RESET();
- if (c == '\n') {
- writel('\r', ®s->tx_rx_fifo);
- while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
- WATCHDOG_RESET();
- }
- writel(c, ®s->tx_rx_fifo);
- }
- static void uart_zynq_serial_puts(const char *s, const int port)
- {
- while (*s)
- uart_zynq_serial_putc(*s++, port);
- }
- static int uart_zynq_serial_tstc(const int port)
- {
- struct uart_zynq *regs = uart_zynq_ports[port];
- return (readl(®s->channel_sts) & ZYNQ_UART_SR_RXEMPTY) == 0;
- }
- static int uart_zynq_serial_getc(const int port)
- {
- struct uart_zynq *regs = uart_zynq_ports[port];
- while (!uart_zynq_serial_tstc(port))
- WATCHDOG_RESET();
- return readl(®s->tx_rx_fifo);
- }
- /* Multi serial device functions */
- #define DECLARE_PSSERIAL_FUNCTIONS(port) \
- static int uart_zynq##port##_init(void) \
- { return uart_zynq_serial_init(port); } \
- static void uart_zynq##port##_setbrg(void) \
- { return uart_zynq_serial_setbrg(port); } \
- static int uart_zynq##port##_getc(void) \
- { return uart_zynq_serial_getc(port); } \
- static int uart_zynq##port##_tstc(void) \
- { return uart_zynq_serial_tstc(port); } \
- static void uart_zynq##port##_putc(const char c) \
- { uart_zynq_serial_putc(c, port); } \
- static void uart_zynq##port##_puts(const char *s) \
- { uart_zynq_serial_puts(s, port); }
- /* Serial device descriptor */
- #define INIT_PSSERIAL_STRUCTURE(port, __name) { \
- .name = __name, \
- .start = uart_zynq##port##_init, \
- .stop = NULL, \
- .setbrg = uart_zynq##port##_setbrg, \
- .getc = uart_zynq##port##_getc, \
- .tstc = uart_zynq##port##_tstc, \
- .putc = uart_zynq##port##_putc, \
- .puts = uart_zynq##port##_puts, \
- }
- DECLARE_PSSERIAL_FUNCTIONS(0);
- static struct serial_device uart_zynq_serial0_device =
- INIT_PSSERIAL_STRUCTURE(0, "ttyPS0");
- DECLARE_PSSERIAL_FUNCTIONS(1);
- static struct serial_device uart_zynq_serial1_device =
- INIT_PSSERIAL_STRUCTURE(1, "ttyPS1");
- #ifdef CONFIG_OF_CONTROL
- __weak struct serial_device *default_serial_console(void)
- {
- const void *blob = gd->fdt_blob;
- int node;
- unsigned int base_addr;
- node = fdt_path_offset(blob, "serial0");
- if (node < 0)
- return NULL;
- base_addr = fdtdec_get_addr(blob, node, "reg");
- if (base_addr == FDT_ADDR_T_NONE)
- return NULL;
- if (base_addr == ZYNQ_SERIAL_BASEADDR0)
- return &uart_zynq_serial0_device;
- if (base_addr == ZYNQ_SERIAL_BASEADDR1)
- return &uart_zynq_serial1_device;
- return NULL;
- }
- #else
- __weak struct serial_device *default_serial_console(void)
- {
- #if defined(CONFIG_ZYNQ_SERIAL_UART0)
- if (uart_zynq_ports[0])
- return &uart_zynq_serial0_device;
- #endif
- #if defined(CONFIG_ZYNQ_SERIAL_UART1)
- if (uart_zynq_ports[1])
- return &uart_zynq_serial1_device;
- #endif
- return NULL;
- }
- #endif
- void zynq_serial_initalize(void)
- {
- serial_register(&uart_zynq_serial0_device);
- serial_register(&uart_zynq_serial1_device);
- }
|