Subject: Re: [PATCH] Xilinx UART Lite 2.6.18 driver

From: "David H. Lynch Jr." <[email-masked]>
Date: Wed, 11 Oct 2006 18:06:50 -0400

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 <[email-masked]>
> + * 
> + * 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 <[email-masked]>
> + * 
> + * 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 <[email-masked]>");
> +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
> [email-masked]
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded

-- 
Dave Lynch 					  	    DLA Systems
Software Development:  				         Embedded Linux
717.627.3770 	       [email-masked] 	  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