serial: Add Zynq serial driver
PATCH v4 1/4 serial: Add Zynq serial driver
The driver is used on Xilinx Zynq platform.
---
v2: Use Zynq name instead of Dragonfire and XPSS/XDFUART
Rename driver name
Remove driver description
v3: SERIAL_MULTI support
Rename xdfuart to uart_zynq
v4: Properly spell out the structure member names of serial_driver
--- common/serial.c | 8 ++ drivers/serial/Makefile | 1 + drivers/serial/serial_zynq.c | 247 ++++++++++++++++++++++++++++++++++++++++++ include/serial.h | 5 + 4 files changed, 261 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_zynq.c
common/serial.c +8 -0
@@ -122,6 +122,14 @@ void serial_initialize(void)serial_register (&uartlite_serial3_device);# endif /* XILINX_UARTLITE_BASEADDR3 */ #endif /* CONFIG_XILINX_UARTLITE */ +#if defined(CONFIG_ZYNQ_SERIAL ) +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0 +serial_register (&uart_zynq_serial0_device); +# endif +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1 +serial_register (&uart_zynq_serial1_device); +# endif +#endif serial_assign (default_serial_console ()->name); }
drivers/serial/Makefile +1 -0
@@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o
drivers/serial/serial_zynq.c new file +247 -0
247 lines not on this page · raw message
include/serial.h +5 -0
@@ -90,6 +90,11 @@ extern struct serial_device bfin_serial2_device;extern struct serial_device bfin_serial3_device;#endif +#if defined(CONFIG_ZYNQ_SERIAL ) +extern struct serial_device uart_zynq_serial0_device; +extern struct serial_device uart_zynq_serial1_device; +#endif +extern void serial_register (struct serial_device *);extern void serial_initialize (void );extern void serial_stdio_init (void );
--
1.7.0.4
PATCH v4 1/4 serial: Add Zynq serial driver
Dear Michal Simek,
Quoted 5 lines
The driver is used on Xilinx Zynq platform.
Signed-off-by Michal Simek <[email protected]>CC Joe Hershberger <[email protected]>CC Marek Vasut <[email protected]>
Quoted 322 lines
---
v2: Use Zynq name instead of Dragonfire and XPSS/XDFUART
Rename driver name
Remove driver descriptionv3:
SERIAL_MULTIsupport
Rename xdfuart touart_zynqv4: Properly spell out the structure member names of
serial_driver--- common/serial.c | 8 ++ drivers/serial/Makefile | 1 + drivers/serial/serial_zynq.c | 247 ++++++++++++++++++++++++++++++++++++++++++ include/serial.h | 5 + 4 files changed, 261 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_zynq.ccommon/serial.c +8
-0@@ -122,6 +122,14 @@ void serial_initialize(void)serial_register (&uartlite_serial3_device);# endif /* XILINX_UARTLITE_BASEADDR3 */ #endif /* CONFIG_XILINX_UARTLITE */ +#if defined(CONFIG_ZYNQ_SERIAL ) +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0 +serial_register (&uart_zynq_serial0_device); +# endif +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1 +serial_register (&uart_zynq_serial1_device); +# endif +#endif serial_assign (default_serial_console ()->name); }drivers/serial/Makefile +1
-0@@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.odrivers/serial/serial_zynq.c new file +70
-0@@ -0,0 +1,247 @@ +/* +* Copyright (C) 2012 Michal Simek <[email protected]> +* Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. +* +* See file CREDITS for list of people who contributed to this +* project. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 USA +*/ + +#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <linux/compiler.h> +#include <serial.h> + +#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 */ + +/* Some clock/baud constants */ +#define ZYNQ_UART_BDIV 15/* Default/reset BDIV value */ +#define ZYNQ_UART_BASECLK 3125000L/* master / (bdiv + 1) */ + +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 ] = { +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0 + [0 ] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR0 , +#endif +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1 + [1 ] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR1 , +#endif +}; + +struct uart_zynq_params { +u32 baudrate; +u32 clock; +}; + +static struct uart_zynq_params uart_zynq_ports_param[2] = { +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0) &&
defined(CONFIG_ZYNQ_SERIAL_CLOCK0)+ [0].baudrate =CONFIG_ZYNQ_SERIAL_BAUDRATE0,drivers/serial/serial_zynq.c new file +3
-0+ [0].clock = CONFIG_ZYNQ_SERIAL_CLOCK0, +#endif +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1) &&
defined(CONFIG_ZYNQ_SERIAL_CLOCK1)+ [1].baudrate =CONFIG_ZYNQ_SERIAL_BAUDRATE1,drivers/serial/serial_zynq.c new file +172
-0+ [1].clock = CONFIG_ZYNQ_SERIAL_CLOCK1, +#endif +}; + +/* 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 = uart_zynq_ports_param[port].clock; +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); +} + +#if !defined(CONFIG_SERIAL_MULTI ) +int serial_init (void ) +{ +return uart_zynq_serial_init (0 ); +} + +void serial_setbrg (void ) +{ +uart_zynq_serial_setbrg (0 ); +} + +void serial_putc (const char c) +{ +uart_zynq_serial_putc (c,0 ); +} + +void serial_puts (const char *s) +{ +uart_zynq_serial_puts (s,0 ); +} + +int serial_getc (void ) +{ +return uart_zynq_serial_getc (0 ); +} + +int serial_tstc (void ) +{ +return uart_zynq_serial_tstc (0 ); +} +#else +/* Multi serial device functions */ +#define DECLARE_PSSERIAL_FUNCTIONS (port) \ + int uart_zynq##port##_init(void) \ + { return uart_zynq_serial_init(port); } \ + void uart_zynq##port##_setbrg(void) \ + { return uart_zynq_serial_setbrg(port); } \ + int uart_zynq##port##_getc(void) \ + { return uart_zynq_serial_getc(port); } \ + int uart_zynq##port##_tstc(void) \ + { return uart_zynq_serial_tstc(port); } \ + void uart_zynq##port##_putc(const char c) \ + { uart_zynq_serial_putc(c, port); } \ + 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, \ + .init = uart_zynq##port##_init, \ + .uninit = 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 ); +struct serial_device uart_zynq_serial0_device = +INIT_PSSERIAL_STRUCTURE (0 ,"ttyPS0" ); +DECLARE_PSSERIAL_FUNCTIONS (1 ); +struct serial_device uart_zynq_serial1_device = +INIT_PSSERIAL_STRUCTURE (1 ,"ttyPS1" ); + +__weak struct serial_device *default_serial_console (void ) +{ +if (uart_zynq_ports[0 ]) +return &uart_zynq_serial0_device; +if (uart_zynq_ports[1 ]) +return &uart_zynq_serial1_device; + +return NULL ; +} +#endif include/serial.h +5
-1@@ -90,6 +90,11 @@ extern struct serial_device bfin_serial2_device;extern struct serial_device bfin_serial3_device;#endif +#if defined(CONFIG_ZYNQ_SERIAL ) +extern struct serial_device uart_zynq_serial0_device; +extern struct serial_device uart_zynq_serial1_device; +#endif +extern void serial_register (struct serial_device *);extern void serial_initialize (void );extern void serial_stdio_init (void ); --1.7.0.4