From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx.dlasys.net (24.152.213.223.res-cmts.eph.ptd.net [24.152.213.223]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTP id 2D3EB67B54 for ; Thu, 12 Oct 2006 08:10:44 +1000 (EST) Message-ID: <452D6AFA.2010107@dlasys.net> Date: Wed, 11 Oct 2006 18:06:50 -0400 From: "David H. Lynch Jr." MIME-Version: 1.0 To: David Bolcsfoldi Subject: Re: [PATCH] Xilinx UART Lite 2.6.18 driver References: <609d5c8e0610101349w64cdd4ecjc5359ad8d1f5d635@mail.gmail.com> In-Reply-To: <609d5c8e0610101349w64cdd4ecjc5359ad8d1f5d635@mail.gmail.com> Content-Type: multipart/alternative; boundary="------------020800000002050708060202" Cc: linuxppc-embedded@ozlabs.org List-Id: Linux on Embedded PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , This is a multi-part message in MIME format. --------------020800000002050708060202 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit also: your driver is strictly interrupt driven. I need polled for the Pico E12 - which my driver an early version was posted in January, supports. Somebody else needs DCR support. David Bolcsfoldi wrote: > Hi, > > here's a set of patches that adds support for Xilinx UART lite > devices. It has been tested on an ML403-FX using xapp902 > (ml403_ppc_plb_temac) using a 2.6.18 kernel and a BusyBox userspace. > > This is my first patch for the Linux kernel, so please be gentle :-) > > David > ------------------------------------------------------------------------ > > diff -urN 2.6.18/arch/ppc/boot/simple/Makefile patched/arch/ppc/boot/simple/Makefile > --- 2.6.18/arch/ppc/boot/simple/Makefile 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/boot/simple/Makefile 2006-10-07 10:34:32.000000000 -0700 > @@ -205,6 +205,7 @@ > endif > boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE) += mpc52xx_tty.o > boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o > +boot-$(CONFIG_SERIAL_XUL_CONSOLE) += xuartlite_tty.o > > LIBS := $(common)/lib.a $(bootlib)/lib.a > ifeq ($(CONFIG_PPC_PREP),y) > diff -urN 2.6.18/arch/ppc/boot/simple/misc.c patched/arch/ppc/boot/simple/misc.c > --- 2.6.18/arch/ppc/boot/simple/misc.c 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/boot/simple/misc.c 2006-10-07 10:27:34.000000000 -0700 > @@ -48,7 +48,8 @@ > #if (defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_VGA_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE)) \ > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE)) \ > && !defined(CONFIG_GEMINI) > #define INTERACTIVE_CONSOLE 1 > #endif > diff -urN 2.6.18/arch/ppc/boot/simple/xuartlite_tty.c patched/arch/ppc/boot/simple/xuartlite_tty.c > --- 2.6.18/arch/ppc/boot/simple/xuartlite_tty.c 1969-12-31 16:00:00.000000000 -0800 > +++ patched/arch/ppc/boot/simple/xuartlite_tty.c 2006-10-07 10:29:30.000000000 -0700 > @@ -0,0 +1,74 @@ > +/* > + * Xilinx UART Lite support. > + * Right now it only works over UART0 and none other. > + * > + * Copyright (C) 2006 David Bolcsfoldi > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + */ > + > +#include > +#include > + > +#define XUL_STATUS_REG_OFFSET 8 /* status register, read only */ > +#define XUL_SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */ > +#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ > +#define XUL_RX_FIFO_OFFSET 0 /* receive FIFO, read only */ > +#define XUL_TX_FIFO_OFFSET 4 /* transmit FIFO, write only */ > + > +static inline int is_xmit_full(unsigned int address) > +{ > + return ((in_be32((volatile unsigned *) (address + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_FULL) == XUL_SR_TX_FIFO_FULL); > +} > + > +static inline int is_recv_empty(unsigned int address) > +{ > + return ((in_be32((volatile unsigned *) (address + XUL_STATUS_REG_OFFSET)) & XUL_SR_RX_FIFO_VALID_DATA) != XUL_SR_RX_FIFO_VALID_DATA); > +} > + > +unsigned long serial_init(int chan, void *ignored) > +{ > + switch (chan) { > + #ifdef XPAR_XUL_UART_0_BASEADDR > + case 0: > + return XPAR_XUL_UART_0_BASEADDR; > + #endif > + #ifdef XPAR_XUL_UART_1_BASEADDR > + case 1: > + return XPAR_XUL_UART_1_BASEADDR; > + #endif > + #ifdef XPAR_XUL_UART_2_BASEADDR > + case 2: > + return XPAR_XUL_UART_2_BASEADDR; > + #endif > + #ifdef XPAR_XUL_UART_3_BASEADDR > + case 3: > + return XPAR_XUL_UART_3_BASEADDR; > + #endif > + default: > + goto out; > + } > + > +out: > + return -1; > +} > + > +void serial_putc(unsigned long com_port, unsigned char c) > +{ > + while(is_xmit_full(XPAR_XUL_UART_0_BASEADDR)); > + out_be32((volatile unsigned *) (XPAR_XUL_UART_0_BASEADDR + XUL_TX_FIFO_OFFSET), c); > +} > + > +unsigned char serial_getc(unsigned long com_port) > +{ > + while(is_recv_empty(XPAR_XUL_UART_0_BASEADDR)); > + return in_be32((volatile unsigned *) (XPAR_XUL_UART_0_BASEADDR + XUL_RX_FIFO_OFFSET)); > +} > + > +int serial_tstc(unsigned long com_port) > +{ > + return !(is_recv_empty(XPAR_XUL_UART_0_BASEADDR)); > +} > + > > ------------------------------------------------------------------------ > > diff -urN 2.6.18/arch/ppc/boot/common/misc-common.c patched/arch/ppc/boot/common/misc-common.c > --- 2.6.18/arch/ppc/boot/common/misc-common.c 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/boot/common/misc-common.c 2006-10-07 10:33:28.000000000 -0700 > @@ -57,7 +57,8 @@ > > #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE) > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE) > extern unsigned long com_port; > > extern int serial_tstc(unsigned long com_port); > @@ -80,7 +81,8 @@ > { > #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE) > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE) > if(keyb_present) > return (CRT_tstc() || serial_tstc(com_port)); > else > @@ -95,7 +97,8 @@ > while (1) { > #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE) > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE) > if (serial_tstc(com_port)) > return (serial_getc(com_port)); > #endif /* serial console */ > @@ -112,7 +115,8 @@ > > #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE) > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE) > serial_putc(com_port, c); > if ( c == '\n' ) > serial_putc(com_port, '\r'); > @@ -161,7 +165,8 @@ > while ( ( c = *s++ ) != '\0' ) { > #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ > || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ > - || defined(CONFIG_SERIAL_MPSC_CONSOLE) > + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ > + || defined(CONFIG_SERIAL_XUL_CONSOLE) > serial_putc(com_port, c); > if ( c == '\n' ) serial_putc(com_port, '\r'); > #endif /* serial console */ > > ------------------------------------------------------------------------ > > --- 2.6.18/arch/ppc/platforms/4xx/virtex.c 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/platforms/4xx/virtex.c 2006-10-07 11:21:18.000000000 -0700 > @@ -52,5 +52,10 @@ > .id = 0, > .dev.platform_data = serial_platform_data, > }, > + > + [VIRTEX_XUL_UART] = { > + .name = "xul_uart", > + .id = 0, > + }, > }; > > > ------------------------------------------------------------------------ > > --- 2.6.18/arch/ppc/platforms/4xx/virtex.h 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/platforms/4xx/virtex.h 2006-10-07 10:35:31.000000000 -0700 > @@ -27,7 +27,9 @@ > /* Device type enumeration for platform bus definitions */ > #ifndef __ASSEMBLY__ > enum ppc_sys_devices { > - VIRTEX_UART, NUM_PPC_SYS_DEVS, > + VIRTEX_UART, > + VIRTEX_XUL_UART, > + NUM_PPC_SYS_DEVS, > }; > #endif > > > ------------------------------------------------------------------------ > > diff -urN 2.6.18/drivers/serial/Kconfig patched/drivers/serial/Kconfig > --- 2.6.18/drivers/serial/Kconfig 2006-10-04 14:31:18.000000000 -0700 > +++ patched/drivers/serial/Kconfig 2006-10-07 10:50:20.000000000 -0700 > @@ -959,4 +959,22 @@ > If you have enabled the serial port on the Motorola IMX > CPU you can make it the console by answering Y to this option. > > +config SERIAL_XUL > + tristate "Xilinx UART Lite serial support" > + depends on XILINX_ML403 > + select SERIAL_CORE > + help > + This driver supports the Xilinx UART Lite serial ports. If you would > + like to use them, you must answer Y or M to this option. Note that > + for use as console, it must be included in the kernel and not as a > + module. > + > +config SERIAL_XUL_CONSOLE > + bool "Console on a Xilinx UART Lite serial port" > + depends on SERIAL_XUL > + select SERIAL_CORE_CONSOLE > + help > + Select this option if you'd like to use the UART Lite serial port > + of the Xilinx ML403 board as a console. > + > endmenu > diff -urN 2.6.18/drivers/serial/Makefile patched/drivers/serial/Makefile > --- 2.6.18/drivers/serial/Makefile 2006-10-04 14:31:18.000000000 -0700 > +++ patched/drivers/serial/Makefile 2006-10-07 10:51:02.000000000 -0700 > @@ -56,3 +56,4 @@ > obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o > obj-$(CONFIG_SERIAL_AT91) += at91_serial.o > obj-$(CONFIG_SERIAL_NETX) += netx-serial.o > +obj-$(CONFIG_SERIAL_XUL) += xuartlite.o > diff -urN 2.6.18/drivers/serial/xuartlite.c patched/drivers/serial/xuartlite.c > --- 2.6.18/drivers/serial/xuartlite.c 1969-12-31 16:00:00.000000000 -0800 > +++ patched/drivers/serial/xuartlite.c 2006-10-10 11:08:08.000000000 -0700 > @@ -0,0 +1,723 @@ > +/* > + * drivers/serial/xuartlite.c > + * > + * Driver for Xilinx UART Lite device. > + * > + * This driver has only been tested with the Xilinx ML403-FX board using the plb_temac > + * reference design with on UART port. > + * > + * This driver is loosely based off the mpc52xx_uart driver. > + * > + * Copyright (C) 2006 David Bolcsfoldi > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +#if defined(CONFIG_SERIAL_XUL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) > +#define SUPPORT_SYSRQ > +#endif > + > +#include > + > +#define SERIAL_XUL_MAJOR 204 > +#define SERIAL_XUL_MINOR 187 > + > +/* > + * Keeps various tidbits about the serial port taken > + * from xparameters.h > + * */ > + > +struct xul_uart_data { > + int baud; > + int parity; > + int bits; > + int flow; > + int uartclk; > + int irq; > + int mapbase; > + int size; > +}; > + > +static struct xul_uart_data xul_data[XPAR_XUARTLITE_NUM_INSTANCES] = { > + { > + .baud = XPAR_XUL_UART_0_BAUDRATE, > +#if (XPAR_XUL_UART_0_USE_PARITY != 0) > + .parity = 'y', > +#else > + .parity = 'n', > +#endif /* XPAR_XUL_UART_0_USE_PARITY */ > + .bits = XPAR_XUL_UART_0_DATA_BITS, > + .flow = 'n', > + .uartclk = 100000000 / 16, /* PLB speed / 16 */ > + .irq = XPAR_INTC_0_XUL_UART_0_VEC_ID, > + .mapbase = XPAR_XUL_UART_0_BASEADDR, > + .size = (XPAR_XUL_UART_0_HIGHADDR - XPAR_XUL_UART_0_BASEADDR) + 1 > + } > + > + /* Add next uart here */ > +}; > + > +static const long ISR_PASS_LIMIT = 255; > +static struct uart_port xul_uart_ports[XPAR_XUARTLITE_NUM_INSTANCES]; > + > +#define XUL(port) ((unsigned int)((port)->membase)) > + > +#define XUL_RX_FIFO_OFFSET 0 /* receive FIFO, read only */ > +#define XUL_TX_FIFO_OFFSET 4 /* transmit FIFO, write only */ > +#define XUL_STATUS_REG_OFFSET 8 /* status register, read only */ > +#define XUL_CONTROL_REG_OFFSET 12 /* control register, write only */ > + > +#define XUL_CR_ENABLE_INTR 0x10 /* enable interrupt */ > +#define XUL_CR_FIFO_RX_RESET 0x02 /* reset receive FIFO */ > +#define XUL_CR_FIFO_TX_RESET 0x01 /* reset transmit FIFO */ > + > +#define XUL_SR_PARITY_ERROR 0x80 > +#define XUL_SR_FRAMING_ERROR 0x40 > +#define XUL_SR_OVERRUN_ERROR 0x20 > +#define XUL_SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */ > +#define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */ > +#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ > + > +/* Forward declaration of the interruption handling routine */ > +static irqreturn_t xul_uart_int(int irq,void *dev_id,struct pt_regs *regs); > + > +/* Simple macro to test if a port is console or not. This one is taken > + * for serial_core.c and maybe should be moved to serial_core.h ? */ > +#ifdef CONFIG_SERIAL_CORE_CONSOLE > +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) > +#else > +#define uart_console(port) (0) > +#endif > + > +/* ======================================================================== */ > +/* UART operations */ > +/* ======================================================================== */ > + > +static int > +xul_uart_int_tx_chars(struct uart_port *port); > + > +static inline int is_xmit_empty(struct uart_port *port) > +{ > + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_EMPTY) == XUL_SR_TX_FIFO_EMPTY); > +} > + > +static inline int is_recv_empty(struct uart_port *port) > +{ > + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_RX_FIFO_VALID_DATA) != XUL_SR_RX_FIFO_VALID_DATA); > +} > + > +static inline int is_xmit_full(struct uart_port *port) > +{ > + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_FULL) == XUL_SR_TX_FIFO_FULL); > +} > + > +static inline void xmit_char(struct uart_port *port, char c) > +{ > + while(is_xmit_full(port)); > + out_be32((volatile unsigned *) (XUL(port) + XUL_TX_FIFO_OFFSET), c); > +} > + > +static inline char recv_char(struct uart_port *port) > +{ > + while(is_recv_empty(port)); > + return in_be32((volatile unsigned *) (XUL(port) + XUL_RX_FIFO_OFFSET)); > +} > + > +static unsigned int > +xul_uart_tx_empty(struct uart_port *port) > +{ > + return ((is_xmit_empty(port)) ? TIOCSER_TEMT : 0); > +} > + > +static void > +xul_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) > +{ > + /* Not implemented */ > +} > + > +static unsigned int > +xul_uart_get_mctrl(struct uart_port *port) > +{ > + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; > +} > + > +static void > +xul_uart_stop_tx(struct uart_port *port) > +{ > + /* port->lock taken by caller */ > +} > + > +static void > +xul_uart_start_tx(struct uart_port *port) > +{ > + /* port->lock taken by caller */ > + xul_uart_int_tx_chars(port); > +} > + > +static void > +xul_uart_send_xchar(struct uart_port *port, char ch) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&port->lock, flags); > + > + port->x_char = ch; > + > + if (ch) { > + xmit_char(port, ch); > + } > + > + spin_unlock_irqrestore(&port->lock, flags); > +} > + > +static void > +xul_uart_stop_rx(struct uart_port *port) > +{ > + /* port->lock taken by caller */ > +} > + > +static void > +xul_uart_enable_ms(struct uart_port *port) > +{ > + /* Not implemented */ > +} > + > +static void > +xul_uart_break_ctl(struct uart_port *port, int ctl) > +{ > + /* Not implemented */ > +} > + > +static int > +xul_uart_startup(struct uart_port *port) > +{ > + int ret; > + > + /* Request IRQ */ > + ret = request_irq(port->irq, xul_uart_int, > + SA_INTERRUPT | SA_SAMPLE_RANDOM, "xul_uart", port); > + if (ret) > + return ret; > + > + /* Reset/activate the port, clear and enable interrupts */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_TX_RESET); /* Reset TX */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_RX_RESET); /* Reset RX */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_ENABLE_INTR); /* Enable interrupt */ > + > + return 0; > +} > + > +static void > +xul_uart_shutdown(struct uart_port *port) > +{ > + /* Shut down the port, interrupt and all */ > + > + /* Disable interrupt bu clearing control register */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); > + > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_TX_RESET); /* Reset TX */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_RX_RESET); /* Reset RX */ > + > + /* Release interrupt */ > + free_irq(port->irq, port); > +} > + > +static void > +xul_uart_set_termios(struct uart_port *port, struct termios *new, > + struct termios *old) > +{ > + /* Nothing can be set, fixed at IP block generation */ > +} > + > +static const char * > +xul_uart_type(struct uart_port *port) > +{ > + return port->type == PORT_XUL ? "ttyXUL" : NULL; > +} > + > +static void > +xul_uart_release_port(struct uart_port *port) > +{ > + if (port->flags & UPF_IOREMAP) { > + iounmap(port->membase); > + port->membase = NULL; > + } > + > + release_mem_region(port->mapbase, xul_data[port->line].size); > +} > + > +static int > +xul_uart_request_port(struct uart_port *port) > +{ > + int mem_region; > + > + if (port->flags & UPF_IOREMAP) { > + port->membase = ioremap(port->mapbase, xul_data[port->line].size); > + } > + > + if (!port->membase) { > + return -EINVAL; > + } > + > + mem_region = request_mem_region(port->mapbase, xul_data[port->line].size, "xul_uart") != NULL ? 0 : -EBUSY; > + return 0; > +} > + > +static void > +xul_uart_config_port(struct uart_port *port, int flags) > +{ > + if ( (flags & UART_CONFIG_TYPE) && > + (xul_uart_request_port(port) == 0) ) > + port->type = PORT_XUL; > +} > + > +static int > +xul_uart_verify_port(struct uart_port *port, struct serial_struct *ser) > +{ > + if ( ser->type != PORT_UNKNOWN && ser->type != PORT_XUL ) { > + printk(KERN_WARNING "xul_uart_verify_port(1)\n"); > + return -EINVAL; > + } > + > + if ( (ser->irq != port->irq) || > + (ser->io_type != SERIAL_IO_MEM) || > + (ser->baud_base != port->uartclk) || > + (ser->iomem_base != (void*)port->mapbase) || > + (ser->hub6 != 0 ) ) { > + printk(KERN_WARNING "xul_uart_verify_port(1)\n"); > + } > + return -EINVAL; > + > + return 0; > +} > + > + > +static struct uart_ops xul_uart_ops = { > + .tx_empty = xul_uart_tx_empty, > + .set_mctrl = xul_uart_set_mctrl, > + .get_mctrl = xul_uart_get_mctrl, > + .stop_tx = xul_uart_stop_tx, > + .start_tx = xul_uart_start_tx, > + .send_xchar = xul_uart_send_xchar, > + .stop_rx = xul_uart_stop_rx, > + .enable_ms = xul_uart_enable_ms, > + .break_ctl = xul_uart_break_ctl, > + .startup = xul_uart_startup, > + .shutdown = xul_uart_shutdown, > + .set_termios = xul_uart_set_termios, > +/* .pm = xul_uart_pm, Not supported yet */ > +/* .set_wake = xul_uart_set_wake, Not supported yet */ > + .type = xul_uart_type, > + .release_port = xul_uart_release_port, > + .request_port = xul_uart_request_port, > + .config_port = xul_uart_config_port, > + .verify_port = xul_uart_verify_port > +}; > + > + > +/* ======================================================================== */ > +/* Interrupt handling */ > +/* ======================================================================== */ > + > +static int > +xul_uart_int_rx_chars(struct uart_port *port, struct pt_regs *regs) > +{ > + struct tty_struct *tty = port->info->tty; > + unsigned char ch, flag; > + unsigned long status; > + > + status = in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)); > + > + /* While we can read, do so ! */ > + if ((status & XUL_SR_RX_FIFO_VALID_DATA) == XUL_SR_RX_FIFO_VALID_DATA) { > + > + /* Get the char */ > + ch = recv_char(port); > + > + /* Handle sysreq char */ > +#ifdef SUPPORT_SYSRQ > + if (uart_handle_sysrq_char(port, ch, regs)) { > + port->sysrq = 0; > + continue; > + } > +#endif > + /* Store it */ > + > + flag = TTY_NORMAL; > + port->icount.rx++; > + > + if (status & XUL_SR_PARITY_ERROR) > + flag = TTY_PARITY; > + else if (status & XUL_SR_FRAMING_ERROR) > + flag = TTY_FRAME; > + > + tty_insert_flip_char(tty, ch, flag); > + > + if (status & XUL_SR_OVERRUN_ERROR) { > + tty_insert_flip_char(tty, 0, TTY_OVERRUN); > + } > + } > + > + spin_unlock(&port->lock); > + tty_flip_buffer_push(tty); > + spin_lock(&port->lock); > + > + return 0; > +} > + > +static int > +xul_uart_int_tx_chars(struct uart_port *port) > +{ > + struct circ_buf *xmit = &port->info->xmit; > + > + /* Process out of band chars */ > + if (port->x_char) { > + xmit_char(port, port->x_char); > + port->icount.tx++; > + port->x_char = 0; > + return 1; > + } > + > + /* Nothing to do ? */ > + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { > + xul_uart_stop_tx(port); > + return 0; > + } > + > + /* Send chars */ > + while (is_xmit_full(port) == 0) { > + xmit_char(port, xmit->buf[xmit->tail]); > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + port->icount.tx++; > + > + if (uart_circ_empty(xmit)) > + break; > + } > + > + /* Wake up */ > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { > + uart_write_wakeup(port); > + } > + > + /* Maybe we're done after all */ > + if (uart_circ_empty(xmit)) { > + xul_uart_stop_tx(port); > + return 0; > + } > + > + return 1; > +} > + > +static irqreturn_t > +xul_uart_int(int irq, void *dev_id, struct pt_regs *regs) > +{ > + struct uart_port *port = (struct uart_port *) dev_id; > + unsigned long pass = ISR_PASS_LIMIT; > + unsigned int keepgoing; > + > + if ( irq != port->irq ) { > + printk( KERN_WARNING > + "xul_uart_int : " \ > + "Received wrong int %d. Waiting for %d\n", > + irq, port->irq); > + return IRQ_NONE; > + } > + > + spin_lock(&port->lock); > + > + /* While we have stuff to do, we continue */ > + do { > + /* If we don't find anything to do, we stop */ > + keepgoing = 0; > + > + /* Do we need to receive chars ? */ > + if (is_recv_empty(port) == 0) { > + keepgoing |= xul_uart_int_rx_chars(port, regs); > + } > + > + /* Do we need to send chars ? */ > + if (is_xmit_empty(port)) { > + keepgoing |= xul_uart_int_tx_chars(port); > + } > + > + /* Limit number of iteration */ > + if ( !(--pass) ) > + keepgoing = 0; > + > + } while (keepgoing); > + > + spin_unlock(&port->lock); > + > + return IRQ_HANDLED; > +} > + > + > +/* > + * Utility routines > + */ > + > +static void __init xul_uart_init_ports(void) > +{ > + int i; > + static int once = 1; > + > + /* Initialize ports only once */ > + if (!once) > + return; > + once = 0; > + > + for (i = 0; i < XPAR_XUARTLITE_NUM_INSTANCES; ++i) { > + struct uart_port *xup = &xul_uart_ports[i]; > + > + xup->line = i; > + spin_lock_init(&xup->lock); > + xup->uartclk = xul_data[i].uartclk / 16; > + xup->fifosize = 16; > + xup->iotype = UPIO_MEM; > + xup->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; > + xup->ops = &xul_uart_ops; > + xup->irq = xul_data[i].irq; > + xup->mapbase = xul_data[i].mapbase; > + } > +} > + > +static void __init xul_uart_register_ports(struct uart_driver *driver) > +{ > + int i, ret; > + > + for(i = 0; i < XPAR_XUARTLITE_NUM_INSTANCES; ++i) { > + struct uart_port *port = &xul_uart_ports[i]; > + /* Add the port to the uart sub-system */ > + ret = uart_add_one_port(driver, port); > + } > +} > + > +/* ======================================================================== */ > +/* Console ( if applicable ) */ > +/* ======================================================================== */ > + > +#ifdef CONFIG_SERIAL_XUL_CONSOLE > + > +static void > +xul_console_write(struct console *co, const char *s, unsigned int count) > +{ > + struct uart_port *port = &xul_uart_ports[co->index]; > + unsigned int i, j; > + > + /* Disable interrupts */ > + > + spin_lock(&port->lock); > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); > + spin_unlock(&port->lock); > + > + /* Wait the TX buffer to be empty */ > + j = 5000000; /* Maximum wait */ > + while (!(is_xmit_empty(port)) && > + --j) > + udelay(1); > + > + for (i = 0; i < count; i++, s++) { > + if (*s == '\n') > + xmit_char(port, '\r'); > + > + xmit_char(port, *s); > + } > + > + /* Restore interrupt state */ > + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); > +} > + > +static int __init > +xul_console_setup(struct console *co, char *options) > +{ > + struct uart_port *port; > + int baud; > + int bits; > + int parity; > + int flow; > + > + if (co->index < 0 || co->index >= XPAR_XUARTLITE_NUM_INSTANCES) > + return -EINVAL; > + > + port = &xul_uart_ports[co->index]; > + > + /* We ioremap ourself */ > + port->membase = ioremap(port->mapbase, xul_data[co->index].size); > + > + if (port->membase == NULL) { > + return -EINVAL; > + } > + > + port->flags &= ~UPF_IOREMAP; > + > + baud = xul_data[co->index].baud; > + parity = xul_data[co->index].parity; > + bits = xul_data[co->index].bits; > + flow = xul_data[co->index].flow; > + > + return uart_set_options(port, co, baud, parity, bits, flow); > +} > + > +static struct uart_driver xul_uart_driver; > + > +static struct console xul_console = { > + .name = "ttyXUL", > + .write = xul_console_write, > + .device = uart_console_device, > + .setup = xul_console_setup, > + .flags = CON_PRINTBUFFER, > + .index = -1, /* Specified on the cmdline (e.g. console=ttyXUL0 ) */ > + .data = &xul_uart_driver, > +}; > + > + > +static int __init > +xul_console_init(void) > +{ > + xul_uart_init_ports(); > + register_console(&xul_console); > + return 0; > +} > + > +console_initcall(xul_console_init); > + > +#define XUL_CONSOLE &xul_console > +#else > +#define XUL_CONSOLE NULL > +#endif > + > + > +/* ======================================================================== */ > +/* UART Driver */ > +/* ======================================================================== */ > + > +static struct uart_driver xul_uart_driver = { > + .owner = THIS_MODULE, > + .driver_name = "xul_uart", > + .dev_name = "ttyXUL", > + .major = SERIAL_XUL_MAJOR, > + .minor = SERIAL_XUL_MINOR, > + .nr = XPAR_XUARTLITE_NUM_INSTANCES, > + .cons = XUL_CONSOLE, > +}; > + > + > +/* ======================================================================== */ > +/* Platform Driver */ > +/* ======================================================================== */ > + > +static int __devinit > +xul_uart_probe(struct platform_device *dev) > +{ > + /* Probe does nothing */ > + return 0; > +} > + > +static int > +xul_uart_remove(struct platform_device *dev) > +{ > + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); > + > + platform_set_drvdata(dev, NULL); > + > + if (port) > + uart_remove_one_port(&xul_uart_driver, port); > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int > +xul_uart_suspend(struct platform_device *dev, pm_message_t state) > +{ > + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); > + > + if (port) > + uart_suspend_port(&xul_uart_driver, port); > + > + return 0; > +} > + > +static int > +xul_uart_resume(struct platform_device *dev) > +{ > + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); > + > + if (port) > + uart_resume_port(&xul_uart_driver, port); > + > + return 0; > +} > +#endif > + > +static struct platform_driver xul_uart_platform_driver = { > + .probe = xul_uart_probe, > + .remove = xul_uart_remove, > +#ifdef CONFIG_PM > + .suspend = xul_uart_suspend, > + .resume = xul_uart_resume, > +#endif > + .driver = { > + .name = "xul_uart" > + }, > +}; > + > + > +/* ======================================================================== */ > +/* Module */ > +/* ======================================================================== */ > + > +static int __init > +xul_uart_init(void) > +{ > + int ret; > + > + printk(KERN_INFO "Serial: Xilinx UART Lite driver\n"); > + > + xul_uart_init_ports(); > + > + ret = uart_register_driver(&xul_uart_driver); > + > + if (ret == 0) { > + xul_uart_register_ports(&xul_uart_driver); > + > + ret = platform_driver_register(&xul_uart_platform_driver); > + > + if (ret) { > + printk(KERN_WARNING "platform_driver_register failed! :%i\n", ret); > + uart_unregister_driver(&xul_uart_driver); > + } > + } > + > + return ret; > +} > + > +static void __exit > +xul_uart_exit(void) > +{ > + platform_driver_unregister(&xul_uart_platform_driver); > + uart_unregister_driver(&xul_uart_driver); > +} > + > + > +module_init(xul_uart_init); > +module_exit(xul_uart_exit); > + > +MODULE_AUTHOR("David Bolcsfoldi "); > +MODULE_DESCRIPTION("Xilinx UART Lite"); > +MODULE_LICENSE("GPL"); > + > > ------------------------------------------------------------------------ > > --- 2.6.18/arch/ppc/platforms/4xx/xilinx_ml403.c 2006-10-04 14:31:15.000000000 -0700 > +++ patched/arch/ppc/platforms/4xx/xilinx_ml403.c 2006-10-07 10:41:50.000000000 -0700 > @@ -69,6 +69,7 @@ > .device_list = (enum ppc_sys_devices[]) > { > VIRTEX_UART, > + VIRTEX_XUL_UART, > }, > }, > }; > > ------------------------------------------------------------------------ > > --- 2.6.18/include/linux/serial_core.h 2006-10-04 14:31:19.000000000 -0700 > +++ patched/include/linux/serial_core.h 2006-10-07 10:52:24.000000000 -0700 > @@ -132,6 +132,8 @@ > > #define PORT_S3C2412 73 > > +/* Xilinx UART Lite */ > +#define PORT_XUL 74 > > #ifdef __KERNEL__ > > > ------------------------------------------------------------------------ > > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded -- Dave Lynch DLA Systems Software Development: Embedded Linux 717.627.3770 dhlii@dlasys.net http://www.dlasys.net fax: 1.253.369.9244 Cell: 1.717.587.7774 Over 25 years' experience in platforms, languages, and technologies too numerous to list. "Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction." Albert Einstein --------------020800000002050708060202 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit
    also:

         your driver is strictly interrupt driven.
          I need polled for the Pico E12 - which my driver an early version was posted in January, supports.
          Somebody else needs DCR support.


David Bolcsfoldi wrote:
Hi,

here's a set of patches that adds support for Xilinx UART lite
devices. It has been tested on an ML403-FX using xapp902
(ml403_ppc_plb_temac) using a 2.6.18 kernel and a BusyBox userspace.

This is my first patch for the Linux kernel, so please be gentle :-)

David

diff -urN 2.6.18/arch/ppc/boot/simple/Makefile patched/arch/ppc/boot/simple/Makefile --- 2.6.18/arch/ppc/boot/simple/Makefile 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/boot/simple/Makefile 2006-10-07 10:34:32.000000000 -0700 @@ -205,6 +205,7 @@ endif boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE) += mpc52xx_tty.o boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o +boot-$(CONFIG_SERIAL_XUL_CONSOLE) += xuartlite_tty.o LIBS := $(common)/lib.a $(bootlib)/lib.a ifeq ($(CONFIG_PPC_PREP),y) diff -urN 2.6.18/arch/ppc/boot/simple/misc.c patched/arch/ppc/boot/simple/misc.c --- 2.6.18/arch/ppc/boot/simple/misc.c 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/boot/simple/misc.c 2006-10-07 10:27:34.000000000 -0700 @@ -48,7 +48,8 @@ #if (defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_VGA_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE)) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE)) \ && !defined(CONFIG_GEMINI) #define INTERACTIVE_CONSOLE 1 #endif diff -urN 2.6.18/arch/ppc/boot/simple/xuartlite_tty.c patched/arch/ppc/boot/simple/xuartlite_tty.c --- 2.6.18/arch/ppc/boot/simple/xuartlite_tty.c 1969-12-31 16:00:00.000000000 -0800 +++ patched/arch/ppc/boot/simple/xuartlite_tty.c 2006-10-07 10:29:30.000000000 -0700 @@ -0,0 +1,74 @@ +/* + * Xilinx UART Lite support. + * Right now it only works over UART0 and none other. + * + * Copyright (C) 2006 David Bolcsfoldi <dbolcsfoldi@gmail.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <asm/io.h> +#include <platforms/4xx/xparameters/xparameters.h> + +#define XUL_STATUS_REG_OFFSET 8 /* status register, read only */ +#define XUL_SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */ +#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ +#define XUL_RX_FIFO_OFFSET 0 /* receive FIFO, read only */ +#define XUL_TX_FIFO_OFFSET 4 /* transmit FIFO, write only */ + +static inline int is_xmit_full(unsigned int address) +{ + return ((in_be32((volatile unsigned *) (address + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_FULL) == XUL_SR_TX_FIFO_FULL); +} + +static inline int is_recv_empty(unsigned int address) +{ + return ((in_be32((volatile unsigned *) (address + XUL_STATUS_REG_OFFSET)) & XUL_SR_RX_FIFO_VALID_DATA) != XUL_SR_RX_FIFO_VALID_DATA); +} + +unsigned long serial_init(int chan, void *ignored) +{ + switch (chan) { + #ifdef XPAR_XUL_UART_0_BASEADDR + case 0: + return XPAR_XUL_UART_0_BASEADDR; + #endif + #ifdef XPAR_XUL_UART_1_BASEADDR + case 1: + return XPAR_XUL_UART_1_BASEADDR; + #endif + #ifdef XPAR_XUL_UART_2_BASEADDR + case 2: + return XPAR_XUL_UART_2_BASEADDR; + #endif + #ifdef XPAR_XUL_UART_3_BASEADDR + case 3: + return XPAR_XUL_UART_3_BASEADDR; + #endif + default: + goto out; + } + +out: + return -1; +} + +void serial_putc(unsigned long com_port, unsigned char c) +{ + while(is_xmit_full(XPAR_XUL_UART_0_BASEADDR)); + out_be32((volatile unsigned *) (XPAR_XUL_UART_0_BASEADDR + XUL_TX_FIFO_OFFSET), c); +} + +unsigned char serial_getc(unsigned long com_port) +{ + while(is_recv_empty(XPAR_XUL_UART_0_BASEADDR)); + return in_be32((volatile unsigned *) (XPAR_XUL_UART_0_BASEADDR + XUL_RX_FIFO_OFFSET)); +} + +int serial_tstc(unsigned long com_port) +{ + return !(is_recv_empty(XPAR_XUL_UART_0_BASEADDR)); +} +

diff -urN 2.6.18/arch/ppc/boot/common/misc-common.c patched/arch/ppc/boot/common/misc-common.c --- 2.6.18/arch/ppc/boot/common/misc-common.c 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/boot/common/misc-common.c 2006-10-07 10:33:28.000000000 -0700 @@ -57,7 +57,8 @@ #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE) extern unsigned long com_port; extern int serial_tstc(unsigned long com_port); @@ -80,7 +81,8 @@ { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE) if(keyb_present) return (CRT_tstc() || serial_tstc(com_port)); else @@ -95,7 +97,8 @@ while (1) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE) if (serial_tstc(com_port)) return (serial_getc(com_port)); #endif /* serial console */ @@ -112,7 +115,8 @@ #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); @@ -161,7 +165,8 @@ while ( ( c = *s++ ) != '\0' ) { #if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ - || defined(CONFIG_SERIAL_MPSC_CONSOLE) + || defined(CONFIG_SERIAL_MPSC_CONSOLE) \ + || defined(CONFIG_SERIAL_XUL_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */

--- 2.6.18/arch/ppc/platforms/4xx/virtex.c 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/platforms/4xx/virtex.c 2006-10-07 11:21:18.000000000 -0700 @@ -52,5 +52,10 @@ .id = 0, .dev.platform_data = serial_platform_data, }, + + [VIRTEX_XUL_UART] = { + .name = "xul_uart", + .id = 0, + }, };

--- 2.6.18/arch/ppc/platforms/4xx/virtex.h 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/platforms/4xx/virtex.h 2006-10-07 10:35:31.000000000 -0700 @@ -27,7 +27,9 @@ /* Device type enumeration for platform bus definitions */ #ifndef __ASSEMBLY__ enum ppc_sys_devices { - VIRTEX_UART, NUM_PPC_SYS_DEVS, + VIRTEX_UART, + VIRTEX_XUL_UART, + NUM_PPC_SYS_DEVS, }; #endif

diff -urN 2.6.18/drivers/serial/Kconfig patched/drivers/serial/Kconfig --- 2.6.18/drivers/serial/Kconfig 2006-10-04 14:31:18.000000000 -0700 +++ patched/drivers/serial/Kconfig 2006-10-07 10:50:20.000000000 -0700 @@ -959,4 +959,22 @@ If you have enabled the serial port on the Motorola IMX CPU you can make it the console by answering Y to this option. +config SERIAL_XUL + tristate "Xilinx UART Lite serial support" + depends on XILINX_ML403 + select SERIAL_CORE + help + This driver supports the Xilinx UART Lite serial ports. If you would + like to use them, you must answer Y or M to this option. Note that + for use as console, it must be included in the kernel and not as a + module. + +config SERIAL_XUL_CONSOLE + bool "Console on a Xilinx UART Lite serial port" + depends on SERIAL_XUL + select SERIAL_CORE_CONSOLE + help + Select this option if you'd like to use the UART Lite serial port + of the Xilinx ML403 board as a console. + endmenu diff -urN 2.6.18/drivers/serial/Makefile patched/drivers/serial/Makefile --- 2.6.18/drivers/serial/Makefile 2006-10-04 14:31:18.000000000 -0700 +++ patched/drivers/serial/Makefile 2006-10-07 10:51:02.000000000 -0700 @@ -56,3 +56,4 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_AT91) += at91_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o +obj-$(CONFIG_SERIAL_XUL) += xuartlite.o diff -urN 2.6.18/drivers/serial/xuartlite.c patched/drivers/serial/xuartlite.c --- 2.6.18/drivers/serial/xuartlite.c 1969-12-31 16:00:00.000000000 -0800 +++ patched/drivers/serial/xuartlite.c 2006-10-10 11:08:08.000000000 -0700 @@ -0,0 +1,723 @@ +/* + * drivers/serial/xuartlite.c + * + * Driver for Xilinx UART Lite device. + * + * This driver has only been tested with the Xilinx ML403-FX board using the plb_temac + * reference design with on UART port. + * + * This driver is loosely based off the mpc52xx_uart driver. + * + * Copyright (C) 2006 David Bolcsfoldi <dbolcsfoldi@gmail.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/config.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/sysrq.h> +#include <linux/console.h> + +#include <asm/delay.h> +#include <asm/io.h> +#include <platforms/4xx/xparameters/xparameters.h> + +#if defined(CONFIG_SERIAL_XUL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/serial_core.h> + +#define SERIAL_XUL_MAJOR 204 +#define SERIAL_XUL_MINOR 187 + +/* + * Keeps various tidbits about the serial port taken + * from xparameters.h + * */ + +struct xul_uart_data { + int baud; + int parity; + int bits; + int flow; + int uartclk; + int irq; + int mapbase; + int size; +}; + +static struct xul_uart_data xul_data[XPAR_XUARTLITE_NUM_INSTANCES] = { + { + .baud = XPAR_XUL_UART_0_BAUDRATE, +#if (XPAR_XUL_UART_0_USE_PARITY != 0) + .parity = 'y', +#else + .parity = 'n', +#endif /* XPAR_XUL_UART_0_USE_PARITY */ + .bits = XPAR_XUL_UART_0_DATA_BITS, + .flow = 'n', + .uartclk = 100000000 / 16, /* PLB speed / 16 */ + .irq = XPAR_INTC_0_XUL_UART_0_VEC_ID, + .mapbase = XPAR_XUL_UART_0_BASEADDR, + .size = (XPAR_XUL_UART_0_HIGHADDR - XPAR_XUL_UART_0_BASEADDR) + 1 + } + + /* Add next uart here */ +}; + +static const long ISR_PASS_LIMIT = 255; +static struct uart_port xul_uart_ports[XPAR_XUARTLITE_NUM_INSTANCES]; + +#define XUL(port) ((unsigned int)((port)->membase)) + +#define XUL_RX_FIFO_OFFSET 0 /* receive FIFO, read only */ +#define XUL_TX_FIFO_OFFSET 4 /* transmit FIFO, write only */ +#define XUL_STATUS_REG_OFFSET 8 /* status register, read only */ +#define XUL_CONTROL_REG_OFFSET 12 /* control register, write only */ + +#define XUL_CR_ENABLE_INTR 0x10 /* enable interrupt */ +#define XUL_CR_FIFO_RX_RESET 0x02 /* reset receive FIFO */ +#define XUL_CR_FIFO_TX_RESET 0x01 /* reset transmit FIFO */ + +#define XUL_SR_PARITY_ERROR 0x80 +#define XUL_SR_FRAMING_ERROR 0x40 +#define XUL_SR_OVERRUN_ERROR 0x20 +#define XUL_SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */ +#define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */ +#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ + +/* Forward declaration of the interruption handling routine */ +static irqreturn_t xul_uart_int(int irq,void *dev_id,struct pt_regs *regs); + +/* Simple macro to test if a port is console or not. This one is taken + * for serial_core.c and maybe should be moved to serial_core.h ? */ +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +/* ======================================================================== */ +/* UART operations */ +/* ======================================================================== */ + +static int +xul_uart_int_tx_chars(struct uart_port *port); + +static inline int is_xmit_empty(struct uart_port *port) +{ + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_EMPTY) == XUL_SR_TX_FIFO_EMPTY); +} + +static inline int is_recv_empty(struct uart_port *port) +{ + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_RX_FIFO_VALID_DATA) != XUL_SR_RX_FIFO_VALID_DATA); +} + +static inline int is_xmit_full(struct uart_port *port) +{ + return ((in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)) & XUL_SR_TX_FIFO_FULL) == XUL_SR_TX_FIFO_FULL); +} + +static inline void xmit_char(struct uart_port *port, char c) +{ + while(is_xmit_full(port)); + out_be32((volatile unsigned *) (XUL(port) + XUL_TX_FIFO_OFFSET), c); +} + +static inline char recv_char(struct uart_port *port) +{ + while(is_recv_empty(port)); + return in_be32((volatile unsigned *) (XUL(port) + XUL_RX_FIFO_OFFSET)); +} + +static unsigned int +xul_uart_tx_empty(struct uart_port *port) +{ + return ((is_xmit_empty(port)) ? TIOCSER_TEMT : 0); +} + +static void +xul_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* Not implemented */ +} + +static unsigned int +xul_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void +xul_uart_stop_tx(struct uart_port *port) +{ + /* port->lock taken by caller */ +} + +static void +xul_uart_start_tx(struct uart_port *port) +{ + /* port->lock taken by caller */ + xul_uart_int_tx_chars(port); +} + +static void +xul_uart_send_xchar(struct uart_port *port, char ch) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + port->x_char = ch; + + if (ch) { + xmit_char(port, ch); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +xul_uart_stop_rx(struct uart_port *port) +{ + /* port->lock taken by caller */ +} + +static void +xul_uart_enable_ms(struct uart_port *port) +{ + /* Not implemented */ +} + +static void +xul_uart_break_ctl(struct uart_port *port, int ctl) +{ + /* Not implemented */ +} + +static int +xul_uart_startup(struct uart_port *port) +{ + int ret; + + /* Request IRQ */ + ret = request_irq(port->irq, xul_uart_int, + SA_INTERRUPT | SA_SAMPLE_RANDOM, "xul_uart", port); + if (ret) + return ret; + + /* Reset/activate the port, clear and enable interrupts */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_TX_RESET); /* Reset TX */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_RX_RESET); /* Reset RX */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_ENABLE_INTR); /* Enable interrupt */ + + return 0; +} + +static void +xul_uart_shutdown(struct uart_port *port) +{ + /* Shut down the port, interrupt and all */ + + /* Disable interrupt bu clearing control register */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); + + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_TX_RESET); /* Reset TX */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), XUL_CR_FIFO_RX_RESET); /* Reset RX */ + + /* Release interrupt */ + free_irq(port->irq, port); +} + +static void +xul_uart_set_termios(struct uart_port *port, struct termios *new, + struct termios *old) +{ + /* Nothing can be set, fixed at IP block generation */ +} + +static const char * +xul_uart_type(struct uart_port *port) +{ + return port->type == PORT_XUL ? "ttyXUL" : NULL; +} + +static void +xul_uart_release_port(struct uart_port *port) +{ + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, xul_data[port->line].size); +} + +static int +xul_uart_request_port(struct uart_port *port) +{ + int mem_region; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, xul_data[port->line].size); + } + + if (!port->membase) { + return -EINVAL; + } + + mem_region = request_mem_region(port->mapbase, xul_data[port->line].size, "xul_uart") != NULL ? 0 : -EBUSY; + return 0; +} + +static void +xul_uart_config_port(struct uart_port *port, int flags) +{ + if ( (flags & UART_CONFIG_TYPE) && + (xul_uart_request_port(port) == 0) ) + port->type = PORT_XUL; +} + +static int +xul_uart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if ( ser->type != PORT_UNKNOWN && ser->type != PORT_XUL ) { + printk(KERN_WARNING "xul_uart_verify_port(1)\n"); + return -EINVAL; + } + + if ( (ser->irq != port->irq) || + (ser->io_type != SERIAL_IO_MEM) || + (ser->baud_base != port->uartclk) || + (ser->iomem_base != (void*)port->mapbase) || + (ser->hub6 != 0 ) ) { + printk(KERN_WARNING "xul_uart_verify_port(1)\n"); + } + return -EINVAL; + + return 0; +} + + +static struct uart_ops xul_uart_ops = { + .tx_empty = xul_uart_tx_empty, + .set_mctrl = xul_uart_set_mctrl, + .get_mctrl = xul_uart_get_mctrl, + .stop_tx = xul_uart_stop_tx, + .start_tx = xul_uart_start_tx, + .send_xchar = xul_uart_send_xchar, + .stop_rx = xul_uart_stop_rx, + .enable_ms = xul_uart_enable_ms, + .break_ctl = xul_uart_break_ctl, + .startup = xul_uart_startup, + .shutdown = xul_uart_shutdown, + .set_termios = xul_uart_set_termios, +/* .pm = xul_uart_pm, Not supported yet */ +/* .set_wake = xul_uart_set_wake, Not supported yet */ + .type = xul_uart_type, + .release_port = xul_uart_release_port, + .request_port = xul_uart_request_port, + .config_port = xul_uart_config_port, + .verify_port = xul_uart_verify_port +}; + + +/* ======================================================================== */ +/* Interrupt handling */ +/* ======================================================================== */ + +static int +xul_uart_int_rx_chars(struct uart_port *port, struct pt_regs *regs) +{ + struct tty_struct *tty = port->info->tty; + unsigned char ch, flag; + unsigned long status; + + status = in_be32((volatile unsigned *) (XUL(port) + XUL_STATUS_REG_OFFSET)); + + /* While we can read, do so ! */ + if ((status & XUL_SR_RX_FIFO_VALID_DATA) == XUL_SR_RX_FIFO_VALID_DATA) { + + /* Get the char */ + ch = recv_char(port); + + /* Handle sysreq char */ +#ifdef SUPPORT_SYSRQ + if (uart_handle_sysrq_char(port, ch, regs)) { + port->sysrq = 0; + continue; + } +#endif + /* Store it */ + + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & XUL_SR_PARITY_ERROR) + flag = TTY_PARITY; + else if (status & XUL_SR_FRAMING_ERROR) + flag = TTY_FRAME; + + tty_insert_flip_char(tty, ch, flag); + + if (status & XUL_SR_OVERRUN_ERROR) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(tty); + spin_lock(&port->lock); + + return 0; +} + +static int +xul_uart_int_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + /* Process out of band chars */ + if (port->x_char) { + xmit_char(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return 1; + } + + /* Nothing to do ? */ + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + xul_uart_stop_tx(port); + return 0; + } + + /* Send chars */ + while (is_xmit_full(port) == 0) { + xmit_char(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + + if (uart_circ_empty(xmit)) + break; + } + + /* Wake up */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { + uart_write_wakeup(port); + } + + /* Maybe we're done after all */ + if (uart_circ_empty(xmit)) { + xul_uart_stop_tx(port); + return 0; + } + + return 1; +} + +static irqreturn_t +xul_uart_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = (struct uart_port *) dev_id; + unsigned long pass = ISR_PASS_LIMIT; + unsigned int keepgoing; + + if ( irq != port->irq ) { + printk( KERN_WARNING + "xul_uart_int : " \ + "Received wrong int %d. Waiting for %d\n", + irq, port->irq); + return IRQ_NONE; + } + + spin_lock(&port->lock); + + /* While we have stuff to do, we continue */ + do { + /* If we don't find anything to do, we stop */ + keepgoing = 0; + + /* Do we need to receive chars ? */ + if (is_recv_empty(port) == 0) { + keepgoing |= xul_uart_int_rx_chars(port, regs); + } + + /* Do we need to send chars ? */ + if (is_xmit_empty(port)) { + keepgoing |= xul_uart_int_tx_chars(port); + } + + /* Limit number of iteration */ + if ( !(--pass) ) + keepgoing = 0; + + } while (keepgoing); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + + +/* + * Utility routines + */ + +static void __init xul_uart_init_ports(void) +{ + int i; + static int once = 1; + + /* Initialize ports only once */ + if (!once) + return; + once = 0; + + for (i = 0; i < XPAR_XUARTLITE_NUM_INSTANCES; ++i) { + struct uart_port *xup = &xul_uart_ports[i]; + + xup->line = i; + spin_lock_init(&xup->lock); + xup->uartclk = xul_data[i].uartclk / 16; + xup->fifosize = 16; + xup->iotype = UPIO_MEM; + xup->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + xup->ops = &xul_uart_ops; + xup->irq = xul_data[i].irq; + xup->mapbase = xul_data[i].mapbase; + } +} + +static void __init xul_uart_register_ports(struct uart_driver *driver) +{ + int i, ret; + + for(i = 0; i < XPAR_XUARTLITE_NUM_INSTANCES; ++i) { + struct uart_port *port = &xul_uart_ports[i]; + /* Add the port to the uart sub-system */ + ret = uart_add_one_port(driver, port); + } +} + +/* ======================================================================== */ +/* Console ( if applicable ) */ +/* ======================================================================== */ + +#ifdef CONFIG_SERIAL_XUL_CONSOLE + +static void +xul_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &xul_uart_ports[co->index]; + unsigned int i, j; + + /* Disable interrupts */ + + spin_lock(&port->lock); + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); + spin_unlock(&port->lock); + + /* Wait the TX buffer to be empty */ + j = 5000000; /* Maximum wait */ + while (!(is_xmit_empty(port)) && + --j) + udelay(1); + + for (i = 0; i < count; i++, s++) { + if (*s == '\n') + xmit_char(port, '\r'); + + xmit_char(port, *s); + } + + /* Restore interrupt state */ + out_be32((volatile unsigned *) (XUL(port) + XUL_CONTROL_REG_OFFSET), 0); +} + +static int __init +xul_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud; + int bits; + int parity; + int flow; + + if (co->index < 0 || co->index >= XPAR_XUARTLITE_NUM_INSTANCES) + return -EINVAL; + + port = &xul_uart_ports[co->index]; + + /* We ioremap ourself */ + port->membase = ioremap(port->mapbase, xul_data[co->index].size); + + if (port->membase == NULL) { + return -EINVAL; + } + + port->flags &= ~UPF_IOREMAP; + + baud = xul_data[co->index].baud; + parity = xul_data[co->index].parity; + bits = xul_data[co->index].bits; + flow = xul_data[co->index].flow; + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver xul_uart_driver; + +static struct console xul_console = { + .name = "ttyXUL", + .write = xul_console_write, + .device = uart_console_device, + .setup = xul_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, /* Specified on the cmdline (e.g. console=ttyXUL0 ) */ + .data = &xul_uart_driver, +}; + + +static int __init +xul_console_init(void) +{ + xul_uart_init_ports(); + register_console(&xul_console); + return 0; +} + +console_initcall(xul_console_init); + +#define XUL_CONSOLE &xul_console +#else +#define XUL_CONSOLE NULL +#endif + + +/* ======================================================================== */ +/* UART Driver */ +/* ======================================================================== */ + +static struct uart_driver xul_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "xul_uart", + .dev_name = "ttyXUL", + .major = SERIAL_XUL_MAJOR, + .minor = SERIAL_XUL_MINOR, + .nr = XPAR_XUARTLITE_NUM_INSTANCES, + .cons = XUL_CONSOLE, +}; + + +/* ======================================================================== */ +/* Platform Driver */ +/* ======================================================================== */ + +static int __devinit +xul_uart_probe(struct platform_device *dev) +{ + /* Probe does nothing */ + return 0; +} + +static int +xul_uart_remove(struct platform_device *dev) +{ + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + if (port) + uart_remove_one_port(&xul_uart_driver, port); + + return 0; +} + +#ifdef CONFIG_PM +static int +xul_uart_suspend(struct platform_device *dev, pm_message_t state) +{ + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); + + if (port) + uart_suspend_port(&xul_uart_driver, port); + + return 0; +} + +static int +xul_uart_resume(struct platform_device *dev) +{ + struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); + + if (port) + uart_resume_port(&xul_uart_driver, port); + + return 0; +} +#endif + +static struct platform_driver xul_uart_platform_driver = { + .probe = xul_uart_probe, + .remove = xul_uart_remove, +#ifdef CONFIG_PM + .suspend = xul_uart_suspend, + .resume = xul_uart_resume, +#endif + .driver = { + .name = "xul_uart" + }, +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +xul_uart_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: Xilinx UART Lite driver\n"); + + xul_uart_init_ports(); + + ret = uart_register_driver(&xul_uart_driver); + + if (ret == 0) { + xul_uart_register_ports(&xul_uart_driver); + + ret = platform_driver_register(&xul_uart_platform_driver); + + if (ret) { + printk(KERN_WARNING "platform_driver_register failed! :%i\n", ret); + uart_unregister_driver(&xul_uart_driver); + } + } + + return ret; +} + +static void __exit +xul_uart_exit(void) +{ + platform_driver_unregister(&xul_uart_platform_driver); + uart_unregister_driver(&xul_uart_driver); +} + + +module_init(xul_uart_init); +module_exit(xul_uart_exit); + +MODULE_AUTHOR("David Bolcsfoldi <dbolcsfoldi@gmail.com>"); +MODULE_DESCRIPTION("Xilinx UART Lite"); +MODULE_LICENSE("GPL"); +

--- 2.6.18/arch/ppc/platforms/4xx/xilinx_ml403.c 2006-10-04 14:31:15.000000000 -0700 +++ patched/arch/ppc/platforms/4xx/xilinx_ml403.c 2006-10-07 10:41:50.000000000 -0700 @@ -69,6 +69,7 @@ .device_list = (enum ppc_sys_devices[]) { VIRTEX_UART, + VIRTEX_XUL_UART, }, }, };

--- 2.6.18/include/linux/serial_core.h 2006-10-04 14:31:19.000000000 -0700 +++ patched/include/linux/serial_core.h 2006-10-07 10:52:24.000000000 -0700 @@ -132,6 +132,8 @@ #define PORT_S3C2412 73 +/* Xilinx UART Lite */ +#define PORT_XUL 74 #ifdef __KERNEL__

_______________________________________________ Linuxppc-embedded mailing list Linuxppc-embedded@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-embedded


-- 
Dave Lynch 					  	    DLA Systems
Software Development:  				         Embedded Linux
717.627.3770 	       dhlii@dlasys.net 	  http://www.dlasys.net
fax: 1.253.369.9244 			           Cell: 1.717.587.7774
Over 25 years' experience in platforms, languages, and technologies too numerous to list.

"Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction."
Albert Einstein
--------------020800000002050708060202--