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"); +