diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c index e79e6b3..883bbc7 100644 --- a/arch/ppc/boot/common/misc-common.c +++ b/arch/ppc/boot/common/misc-common.c @@ -60,7 +60,8 @@ unsigned char *ISA_io = NULL; #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_UARTLITE_CONSOLE) extern unsigned long com_port; extern int serial_tstc(unsigned long com_port); @@ -83,7 +84,8 @@ int tstc(void) { #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_UARTLITE_CONSOLE) if(keyb_present) return (CRT_tstc() || serial_tstc(com_port)); else @@ -98,7 +100,8 @@ int getc(void) 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_UARTLITE_CONSOLE) if (serial_tstc(com_port)) return (serial_getc(com_port)); #endif /* serial console */ @@ -115,7 +118,8 @@ putc(const char c) #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_UARTLITE_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); @@ -164,7 +168,8 @@ void puts(const char *s) 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_UARTLITE_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */ diff --git a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile index 9533f8d..917f3fd 100644 --- a/arch/ppc/boot/simple/Makefile +++ b/arch/ppc/boot/simple/Makefile @@ -204,6 +204,7 @@ boot-$(CONFIG_8260) += m8260_tty.o endif boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE) += mpc52xx_tty.o boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o +boot-$(CONFIG_SERIAL_UARTLITE_CONSOLE) += uartlite_tty.o LIBS := $(common)/lib.a $(bootlib)/lib.a ifeq ($(CONFIG_PPC_PREP),y) diff --git a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c index 3865f3f..c0e7b54 100644 --- a/arch/ppc/boot/simple/misc-embedded.c +++ b/arch/ppc/boot/simple/misc-embedded.c @@ -90,7 +90,7 @@ load_kernel(unsigned long load_addr, int * initialize the serial console port. */ embed_config(&bp); -#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) com_port = serial_init(0, bp); #endif @@ -267,7 +267,7 @@ load_kernel(unsigned long load_addr, int rec = (struct bi_record *)((unsigned long)rec + rec->size); } puts("Now booting the kernel\n"); -#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_UARTLITE_CONSOLE) serial_close(com_port); #endif diff --git a/arch/ppc/boot/simple/misc.c b/arch/ppc/boot/simple/misc.c index f415d6c..26666c7 100644 --- a/arch/ppc/boot/simple/misc.c +++ b/arch/ppc/boot/simple/misc.c @@ -51,7 +51,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_UARTLITE_CONSOLE)) \ && !defined(CONFIG_GEMINI) #define INTERACTIVE_CONSOLE 1 #endif diff --git a/arch/ppc/boot/simple/uartlite_tty.c b/arch/ppc/boot/simple/uartlite_tty.c new file mode 100644 index 0000000..d08e0f8 --- /dev/null +++ b/arch/ppc/boot/simple/uartlite_tty.c @@ -0,0 +1,104 @@ +/* + * arch/ppc/boot/simple/uartlite_tty.c + * + * Bootloader version of the embedded Xilinx/UARTLITE driver. + * + * Author: David H. Lynch Jr. + * Copyright (C) 2005 DLA Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: uartlite.c,v 0.10 2005/12/14 10:03:27 dhlii Exp $ + * + * heavily based on the equivalent 8250 code. + * serial_getc is untested + * + */ + +#include +#include +#include +#include +#include "nonstdio.h" +#include "serial.h" + +#include + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static int shift; + +unsigned long +serial_init(int chan, void *ignored) +{ + unsigned long com_port; + + /* We need to find out which type io we're expecting. If it's + * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. + * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */ + switch (rs_table[chan].io_type) { + case SERIAL_IO_PORT: + com_port = rs_table[chan].port; + break; + case SERIAL_IO_MEM: + com_port = (unsigned long)rs_table[chan].iomem_base; + break; + default: + /* We can't deal with it. */ + return -1; + } + + /* How far apart the registers are. */ + shift = rs_table[chan].iomem_reg_shift; + return (com_port); +} + +void +serial_putc(unsigned long com_port, unsigned char c) +{ + unsigned int status ; + do { + status = serial_in32(com_port, UART_LSR); + } while ( status & UART_LSR_TXF); + serial_out32(com_port, UART_TX, c); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + unsigned int status ; + return 0; + do { + status = serial_in32(com_port, UART_LSR); + } while ((status & UART_LSR_DR) == 0) ; + return serial_in32(com_port, UART_RX); +} + +int +serial_tstc(unsigned long com_port) +{ + unsigned int status ; + return 0; + status = serial_in32(com_port, UART_LSR); + return (status & UART_LSR_DR); +} + +void +serial_close(unsigned long com_port) +{ +} diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 5b7f2b8..004b0ec 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -86,6 +86,9 @@ endif ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y) obj-$(CONFIG_SERIAL_TEXT_DEBUG) += mv64x60_dbg.o endif +ifeq ($(CONFIG_SERIAL_UARTLITE_CONSOLE),y) +obj-$(CONFIG_SERIAL_TEXT_DEBUG) += uartlite_dbg.o +endif obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_MPC10X_BRIDGE) += mpc10x_common.o ppc_sys.o obj-$(CONFIG_MPC10X_OPENPIC) += open_pic.o diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c index e83a83f..815bac3 100644 --- a/arch/ppc/syslib/ppc4xx_setup.c +++ b/arch/ppc/syslib/ppc4xx_setup.c @@ -41,7 +41,11 @@ #include #include +#if defined(CONFIG_SERIAL_8250) #include +#elif defined(CONFIG_SERIAL_UARTLITE) +#include +#endif /* Function Prototypes */ extern void abort(void); @@ -270,7 +274,11 @@ ppc4xx_init(unsigned long r3, unsigned l ppc_md.setup_io_mappings = ppc4xx_map_io; #ifdef CONFIG_SERIAL_TEXT_DEBUG +#if defined(CONFIG_SERIAL_8250) ppc_md.progress = gen550_progress; +#elif defined(CONFIG_SERIAL_UARTLITE) + ppc_md.progress = uartlite_progress; +#endif #endif #if defined(CONFIG_PCI) && defined(CONFIG_IDE) diff --git a/arch/ppc/syslib/uartlite.h b/arch/ppc/syslib/uartlite.h new file mode 100644 index 0000000..4c5f379 --- /dev/null +++ b/arch/ppc/syslib/uartlite.h @@ -0,0 +1,18 @@ +/* + * arch/ppc/syslib/uartlite.h + * + * uartlite prototypes + * + * Matt Porter + * + * 2004 (c) MontaVista Software, Inc. 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. + */ + +extern void uartlite_progress(char *, unsigned short); +extern void uartlite_puts(char *); +// extern void uartlite_putc(char); +extern void uartlite_init(int, struct uart_port *); +extern void uartlite_kgdb_map_scc(void); diff --git a/arch/ppc/syslib/uartlite_dbg.c b/arch/ppc/syslib/uartlite_dbg.c new file mode 100644 index 0000000..d666122 --- /dev/null +++ b/arch/ppc/syslib/uartlite_dbg.c @@ -0,0 +1,162 @@ +/* + * arch/ppc/syslib/uartlite_dbg.c + * + * Bootloader version of the embedded Xilinx/UARTLITE driver. + * + * Author: David H. Lynch Jr. + * Copyright (C) 2005 DLA Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: uartlite.c,v 0.10 2005/12/14 10:03:27 dhlii Exp $ + * + * Heavily based on equivalent 8250 code. + * uartlite_getc() is untested. + */ + +#include +#include +#include +#include /* For linux/serial_core.h */ +#include +#include +#include + +void uartlite_puts(char *s); + +/* SERIAL_PORT_DFNS is defined in */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +#include + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static int shift; + +unsigned long +serial_init(int chan, void *ignored) +{ + unsigned long com_port; + + /* We need to find out which type io we're expecting. If it's + * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. + * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */ + switch (rs_table[chan].io_type) { + case SERIAL_IO_PORT: + com_port = rs_table[chan].port; + break; + case SERIAL_IO_MEM: + com_port = (unsigned long)rs_table[chan].iomem_base; + break; + default: + /* We can't deal with it. */ + uartlite_puts("serial_init - oops"); + return -1; + } + + /* How far apart the registers are. */ + shift = rs_table[chan].iomem_reg_shift; + return (com_port); +} + +void +serial_putc(unsigned long com_port, unsigned char c) +{ + unsigned int status ; + do { + status = serial_in32(com_port, UART_LSR); + } while ( status & UART_LSR_TXF); + serial_out32(com_port, UART_TX, c); +} + +void +uartlite_puts(char *s) +{ + volatile unsigned int progress_debugport; + volatile char c; + + progress_debugport = XPAR_OPB_UARTLITE_0_BASEADDR; + while ((c = *s++) != 0) + serial_putc(progress_debugport, c); + + serial_putc(progress_debugport, '\n'); + serial_putc(progress_debugport, '\r'); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + unsigned int status ; + return 0; + do { + status = serial_in32(com_port, UART_LSR) ; + } while (!( status & UART_LSR_DR)) ; + return serial_in32(com_port, UART_RX); +} + +int +serial_tstc(unsigned long com_port) +{ + unsigned int status ; + return 0; + status = serial_in32(com_port, UART_LSR); + return (status & UART_LSR_DR); +} + +void +serial_close(unsigned long com_port) +{ +} +void +uartlite_init(int i, struct uart_port *serial_req) +{ + rs_table[i].io_type = serial_req->iotype; + rs_table[i].port = serial_req->iobase; + rs_table[i].iomem_base = serial_req->membase; + rs_table[i].iomem_reg_shift = serial_req->regshift; + rs_table[i].baud_base = BASE_BAUD; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +void +uartlite_progress(char *s, unsigned short hex) +{ + volatile unsigned int progress_debugport; + volatile char c; + + progress_debugport = serial_init(0, NULL); + + serial_putc(progress_debugport, '\r'); + + while ((c = *s++) != 0) + serial_putc(progress_debugport, c); + + serial_putc(progress_debugport, '\n'); + serial_putc(progress_debugport, '\r'); +} +void +ul_putc(char c) +{ + volatile unsigned int progress_debugport; + progress_debugport = serial_init(0, NULL); + + serial_putc(progress_debugport, c); +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 812bae6..eb8b60a 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -396,6 +396,28 @@ config SERIAL_MPSC_CONSOLE help Say Y here if you want to support a serial console on a Marvell MPSC. +config SERIAL_UARTLITE + bool "Xilinx UARTLITE serial port support" + depends on PPC32 + select SERIAL_CORE + help + Say Y here if you want to use the Xilinx UARTLITE serial controller. + +config SERIAL_UARTLITE_CONSOLE + bool "Support for console on Xilinx UARTLITE serial port" + depends on SERIAL_UARTLITE + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on a Xilinx UARTLITE. + +config SERIAL_UARTLITE_NR_UARTS + int "Maximum number of Xilinx UARTLITE serial ports" + depends on SERIAL_UARTLITE + default "1" + help + Set this to the number of serial ports you want the driver + to support. + config SERIAL_PXA bool "PXA serial port support" depends on ARM && ARCH_PXA diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index d7c7c71..54c5343 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -14,9 +14,11 @@ serial-8250-$(CONFIG_HP300) += 8250_hp30 obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) +obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +#obj-$(CONFIG_SERIAL_UARTLITE_CONSOLE) += uartlite_early.o obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c new file mode 100644 index 0000000..7efe233 --- /dev/null +++ b/drivers/serial/uartlite.c @@ -0,0 +1,1333 @@ +/* + * linux/drivers/serial/uartlite.c + * + * Driver for uartlite ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.c. + * Based on drivers/serial/m32r_sio.c. + * + * Author: David H. Lynch Jr. + * Copyright (C) 2005 DLA Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: uartlite.c,v 0.10 2005/12/14 10:03:27 dhlii Exp $ + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + * + * The Xilinx UartLite is a fairly simple fixed Baud Rate Uart with a 16 byte Fifo + * This driver borrows very heavily from drivers/serial/8250.c on the + * assumption that the 8250 driver is likely to be very heavily tested. + * As there is significant complexity to the 8250 driver that has no meaning for the + * Xilinx UartLite I have relied on drivers/serial/m32r_sio.c to work out simplifications. + * as much as possible functions, UART registers and bits where named to match the 8250. + * + */ + + +#define tx_enabled(port) ((port)->unused[0]) +#define rx_enabled(port) ((port)->unused[1]) + +#include + +#if defined(CONFIG_SERIAL_UARTLITE_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_INT 0 // enable interupt driven - untested +#define IS_TXI 0 // interupt driven sends + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define UARTLITE_ISR_PASS_LIMIT 256 +#define PORT_UARTLITE_BASE PORT_UARTLITE +#define PORT_INDEX(x) (x - PORT_UARTLITE_BASE + 1) + +/* + * We default to -1 (0 is actually used) for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) >= 0) + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +#define UART_NR CONFIG_SERIAL_UARTLITE_NR_UARTS + +struct uartlite_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short capabilities; /* port capabilities */ + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char lsr_break_flag; + unsigned int count; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uartlite_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .fifo_size = 1, + .flags = 0, + }, + [PORT_INDEX(PORT_UARTLITE)] = { + .name = "uartlite", + .fifo_size = 16, + .flags = UART_CAP_FIFO, + }, +}; + +static _INLINE_ unsigned int serial_in(struct uartlite_port *up, int offset) +{ + unsigned int value; + offset <<= up->port.regshift; + + switch (up->port.iotype) { + default: + value = (*(volatile unsigned int *) ( up->port.membase + offset)); + __asm__ __volatile__("eieio"); + return value; + } +} + +static _INLINE_ void +serial_out(struct uartlite_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { + default: + (*(volatile unsigned int *)( up->port.membase + offset) =value); + __asm__ __volatile__("eieio"); + break; + + } +} + +static struct uartlite_port uartlite_ports[UART_NR]; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void +uartlite_wait_for_xmitr_empty(struct uartlite_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while (status & UART_LSR_TXF); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) + udelay(1); + } +} + +void +uartlite_putc(struct uartlite_port *up, unsigned char c) +{ + serial_out(up, UART_TX, c); + if (c == '\r') + up->count = 0; + if (up->count >= 85) + uartlite_putc(up, '\r'); +} +void +uartlite_putchar(struct uartlite_port *up, unsigned char c) +{ + uartlite_wait_for_xmitr_empty(up); + uartlite_putc(up,c); +} + +unsigned char +uartlite_getc(struct uartlite_port *up) +{ + return serial_in(up, UART_RX); +} + +void +uartlite_set_ier(struct uartlite_port *up, unsigned int ier) +{ + serial_out(up, UART_IER, ier); +} + + +static void serial_reset( struct uartlite_port *up) +{ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); +} + +static void serial_init( struct uartlite_port *up) +{ + + up->capabilities = uart_config[up->port.type].flags; + up->mcr = 0; + up->ier = 0; + + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in set_termios()) + */ + up->ier &= ~(UART_LCR_RXF | UART_LCR_TXF); + uartlite_set_ier(up, up->ier); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + if (up->capabilities & UART_CAP_FIFO) { + // there is no FIFO enable/disable for the Uart Lite + } +} +/* + Stop transmitting characters. This might be due to the CTS + line becoming inactive or the tty layer indicating we want + to stop transmission due to an XOFF character. + + Locking: up->lock taken. + Interrupts: locally disabled. + This call must not sleep +*/ +static void uartlite_stop_tx(struct uart_port *port) +{ + // struct uartlite_port *up = (struct uartlite_port *)port; + + if (tx_enabled(port)) { + // disable_irq(IRQ_XXX); + tx_enabled(port) = 0; + } +} +static _INLINE_ void transmit_chars(struct uartlite_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + int count; + + if (up->port.x_char) { + uartlite_putc(up, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + uartlite_stop_tx(&up->port); + return; + } + + count = up->port.fifosize; + do { + uartlite_putc(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + // while (!serial_in(up, UART_LSR) & UART_LSR_THRE); + + } while (--count > 0); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + uartlite_stop_tx(&up->port); +} +/* + start transmitting characters. + + Locking: up->lock taken. + Interrupts: locally disabled. + This call must not sleep +*/ + +static void uartlite_start_tx(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; +#if IS_TXI + struct circ_buf *xmit = &up->port.info->xmit; +#endif + unsigned long flags; + + + if (!tx_enabled(port)) { + // enable_irq(IRQ_XXX); + tx_enabled(port) = 1; + } +#if IS_TXI + if (!(up->ier & UART_IER_THRI)) { // are TX interupts enabled ? + + up->ier |= UART_IER_THRI; // enable them + uartlite_set_ier(up, up->ier); + + uartlite_putc(up, xmit->buf[xmit->tail]); // send 1 character to kick things + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } + //while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); +#else + if (port) { + //disable_irq(up->irqs[SCIx_TXI_IRQ]); + spin_lock_irqsave(&up->port.lock, flags); + transmit_chars(up); + spin_unlock_irqrestore(&up->port.lock, flags); + //enable_irq(up->irqs[SCIx_TXI_IRQ]); + } +#endif +} + +static void uartlite_stop_rx(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + + up->ier &= ~UART_IER_RLSI; // disable receive interrupts + up->port.read_status_mask &= ~UART_LSR_DR; + uartlite_set_ier(up, up->ier); +} +/* + Enable the modem status interrupts. + + Locking: up->lock taken. + Interrupts: locally disabled. + This call must not sleep +*/ +static void uartlite_enable_ms(struct uart_port *port) +{ +} + +static _INLINE_ void +receive_chars(struct uartlite_port *up, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = up->port.info->tty; + unsigned char ch, lsr = *status; + int max_count = 256; + char flag; + + do { + /* The following is not allowed by the tty layer and + unsafe. It should be fixed ASAP */ + if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { + if (tty->low_latency) { + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + } + /* + * If this failed then we will throw away the + * bytes but must do so to clear interrupts + */ + } + ch = uartlite_getc(up); + flag = TTY_NORMAL; + up->port.icount.rx++; + +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + /* + * Recover the break flag from console xmit + */ + if (up->port.line == up->port.cons->index) { + lsr |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + + if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) + up->port.icount.parity++; + else if (lsr & UART_LSR_FE) + up->port.icount.frame++; + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + + if (lsr & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch, regs)) + goto ignore_char; + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); + + ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + *status = lsr; +} + + +/* + * This handles the interrupt from one port. + */ +static inline void +uartlite_handle_port(struct uartlite_port *up, struct pt_regs *regs) +{ + unsigned int status = serial_in(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + if (status & UART_LSR_DR) + receive_chars(up, &status, regs); +#if IS_TXI + if (!(status & UART_LSR_TXF)) + transmit_chars(up); +#endif +} + +#if IS_INT +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t uartlite_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0, handled = 0; + + DEBUG_INTR("uartlite_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uartlite_port *up; + unsigned int iir; + + up = list_entry(l, struct uartlite_port, list); + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + uartlite_handle_port(up, regs); + spin_unlock(&up->port.lock); + + handled = 1; + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > UARTLITE_ISR_PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk(KERN_ERR "uartlite: too much work for " + "irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_RETVAL(handled); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uartlite_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct uartlite_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, uartlite_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uartlite_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + serial_do_unlink(i, up); +} +#endif +/* + * This function is used to handle ports that do not have an interrupt. + */ +static void uartlite_timeout(unsigned long data) +{ + struct uartlite_port *up = (struct uartlite_port *)data; + unsigned int timeout; + unsigned int iir; + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + uartlite_handle_port(up, NULL); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} +/* + This function tests whether the transmitter fifo and shifter + for the port described by 'port' is empty. If it is empty, + this function should return TIOCSER_TEMT, otherwise return 0. + If the port does not support this operation, then it should + return TIOCSER_TEMT. + + Locking: none. + Interrupts: caller dependent. + This call must not sleep +*/ +static unsigned int uartlite_tx_empty(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + return ret; +} +/* + Returns the current state of modem control inputs. The state + of the outputs should not be returned, since the core keeps + track of their state. The state information should include: + - TIOCM_DCD state of DCD signal + - TIOCM_CTS state of CTS signal + - TIOCM_DSR state of DSR signal + - TIOCM_RI state of RI signal + The bit is set if the signal is currently driven active. If + the port does not support CTS, DCD or DSR, the driver should + indicate that the signal is permanently active. If RI is + not available, the signal should not be indicated as active. + + Locking: up->lock taken. + Interrupts: locally disabled. + This call must not sleep +*/ +static unsigned int uartlite_get_mctrl(struct uart_port *port) +{ + return 0; +} +/* + This function sets the modem control lines for port described + by 'port' to the state described by mctrl. The relevant bits + of mctrl are: + - TIOCM_RTS RTS signal. + - TIOCM_DTR DTR signal. + - TIOCM_OUT1 OUT1 signal. + - TIOCM_OUT2 OUT2 signal. + If the appropriate bit is set, the signal should be driven + active. If the bit is clear, the signal should be driven + inactive. + + Locking: up->lock taken. + Interrupts: locally disabled. + This call must not sleep +*/ +static void uartlite_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} +/* + Control the transmission of a break signal. If ctl is + nonzero, the break signal should be transmitted. The signal + should be terminated when another call is made with a zero + ctl. + + Locking: none. + Interrupts: caller dependent. + This call must not sleep +*/ +static void uartlite_break_ctl(struct uart_port *port, int break_state) +{ +} +/* + Grab any interrupt resources and initialise any low level driver + state. Enable the port for reception. It should not activate + RTS nor DTR; this will be done via a separate call to set_mctrl. + + Locking: port_sem taken. + Interrupts: globally disabled. +*/ +static int uartlite_startup(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + int retval = 0; + + // serial_init(up); + + tx_enabled(port) = 1; + rx_enabled(port) = 1; + +#if IS_INT + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } +#else + { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } +#endif + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + uartlite_set_ier(up, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + serial_reset(up); + return retval; +} +/* + Disable the port, disable any break condition that may be in + effect, and free any interrupt resources. It should not disable + RTS nor DTR; this will have already been done via a separate + call to set_mctrl. + + Locking: port_sem taken. + Interrupts: caller dependent. +*/ +static void uartlite_shutdown(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + uartlite_set_ier(up, up->ier); + + /* + * Disable break condition and FIFOs + */ + + serial_init(up); + +#if IS_INT + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +#else + del_timer_sync(&up->timer); +#endif +} +/* + Change the port parameters, including word length, parity, stop + bits. Update read_status_mask and ignore_status_mask to indicate + the types of events we are interested in receiving. Relevant + termios->c_cflag bits are: + CSIZE - word size + CSTOPB - 2 stop bits + PARENB - parity enable + PARODD - odd parity (when PARENB is in force) + CREAD - enable reception of characters (if not set, + still receive characters from the port, but + throw them away. + CRTSCTS - if set, enable CTS status change reporting + CLOCAL - if not set, enable modem status change + reporting. + Relevant termios->c_iflag bits are: + INPCK - enable frame and parity error events to be + passed to the TTY layer. + BRKINT + PARMRK - both of these enable break events to be + passed to the TTY layer. + + IGNPAR - ignore parity and framing errors + IGNBRK - ignore break errors, If IGNPAR is also + set, ignore overrun errors as well. + The interaction of the iflag bits is as follows (parity error + given as an example): + Parity error INPCK IGNPAR + None n/a n/a character received + Yes n/a 0 character discarded + Yes 0 1 character received, marked as + TTY_NORMAL + Yes 1 1 character received, marked as + TTY_PARITY + + Other flags may be used (eg, xon/xoff characters) if your + hardware supports hardware "soft" flow control. + + Locking: none. + Interrupts: caller dependent. + This call must not sleep +*/ +static void +uartlite_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + unsigned char cval ; + unsigned long flags; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + // uart_update_timeout(uport, termios->c_cflag, 57600); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + uartlite_set_ier(up, up->ier); + + uartlite_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} +/* + Perform any power management related activities on the specified + port. State indicates the new state (defined by ACPI D0-D3), + oldstate indicates the previous state. Essentially, D0 means + fully on, D3 means powered down. + + This function should not be used to grab any resources. + + This will be called when the port is initially opened and finally + closed, except when the port is also the system console. This + will occur even if CONFIG_PM is not set. + + Locking: none. + Interrupts: caller dependent. +*/ +static void +uartlite_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + + // 8250 + // uartlite_set_sleep(p, state != 0); + + if (up->pm) + up->pm(port, state, oldstate); +} + +/* + * Resource handling. + */ +static int +uartlite_request_std_resource(struct uartlite_port *up) +{ + unsigned int size = 8 << up->port.regshift; + int ret = 0; + + switch (up->port.iotype) { + case UPIO_MEM: + if (!up->port.mapbase) + break; + + if (!request_mem_region(up->port.mapbase, size, "serial")) { + ret = -EBUSY; + break; + } + + if (up->port.flags & UPF_IOREMAP) { + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) { + release_mem_region(up->port.mapbase, size); + ret = -ENOMEM; + } + } + break; + + case UPIO_PORT: + if (!request_region(up->port.iobase, size, "serial")) + ret = -EBUSY; + break; + } + return ret; +} +/* + Release any memory and IO region resources currently in use by + the port. + + Locking: none. + Interrupts: caller dependent. +*/ +static void uartlite_release_port(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + unsigned int size = 8 << up->port.regshift; + + switch (up->port.iotype) { + case UPIO_MEM: + if (!up->port.mapbase) + break; + + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + release_mem_region(up->port.mapbase, size); + break; + + case UPIO_PORT: + release_region(up->port.iobase, size); + break; + } +} +/* + Request any memory and IO region resources required by the port. + If any fail, no resources should be registered when this function + returns, and it should return -EBUSY on failure. + + Locking: none. + Interrupts: caller dependent. +*/ +static int uartlite_request_port(struct uart_port *port) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + return uartlite_request_std_resource(up); +} +/* + Perform any autoconfiguration steps required for the port. `type` + contains a bit mask of the required configuration. UART_CONFIG_TYPE + indicates that the port requires detection and identification. + up->type should be set to the type found, or PORT_UNKNOWN if + no port was detected. + + UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + which should be probed using standard kernel autoprobing techniques. + This is not necessary on platforms where ports have interrupts + internally hard wired (eg, system on a chip implementations). + + Locking: none. + Interrupts: caller dependent. +*/ +static void uartlite_config_port(struct uart_port *port, int flags) +{ + struct uartlite_port *up = (struct uartlite_port *)port; + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.type = (PORT_UARTLITE - PORT_UARTLITE_BASE + 1); + up->port.fifosize = uart_config[up->port.type].fifo_size; + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* 8250 + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + uartlite_request_std_resource(up); +} +/* + Verify the new serial port information contained within serinfo is + suitable for this port type. + + Locking: none. + Interrupts: caller dependent. +*/ +static int +uartlite_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type >= ARRAY_SIZE(uart_config)) + return -EINVAL; + return 0; +} +/* + Return a pointer to a string constant describing the specified + port, or return NULL, in which case the string 'unknown' is + substituted. + + Locking: none. + Interrupts: caller dependent. +*/ +static const char * +uartlite_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops uartlite_pops = { + .tx_empty = uartlite_tx_empty, + .set_mctrl = uartlite_set_mctrl, + .get_mctrl = uartlite_get_mctrl, + .stop_tx = uartlite_stop_tx, + .start_tx = uartlite_start_tx, + .stop_rx = uartlite_stop_rx, + .enable_ms = uartlite_enable_ms, + .break_ctl = uartlite_break_ctl, + .startup = uartlite_startup, + .shutdown = uartlite_shutdown, + .set_termios = uartlite_set_termios, + .pm = uartlite_pm, + .type = uartlite_type, + .release_port = uartlite_release_port, + .request_port = uartlite_request_port, + .config_port = uartlite_config_port, + .verify_port = uartlite_verify_port, +}; + + +static void __init uartlite_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < UART_NR; i++) { + if (old_serial_port[i].type == PORT_UARTLITE) { + struct uartlite_port *up = &uartlite_ports[i]; + up->port.line = i; + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base; + up->port.flags = old_serial_port[i].flags; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.type = old_serial_port[i].type; + up->port.ops = &uartlite_pops; + spin_lock_init(&up->port.lock); + init_timer(&up->timer); + up->timer.function = uartlite_timeout; + // dump_port(&up->port); + } + } +} + +static void __init +uartlite_register_ports(struct uart_driver *drv) +{ + int i; + + uartlite_init_ports(); + + for (i = 0; i < UART_NR; i++) { + struct uartlite_port *up = &uartlite_ports[i]; + +#if 1 // ~8250 + up->port.line = i; + up->port.ops = &uartlite_pops; + init_timer(&up->timer); + up->timer.function = uartlite_timeout; +#endif + uart_add_one_port(drv, &up->port); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +uartlite_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uartlite_port *up = &uartlite_ports[co->index]; + + uartlite_set_ier(up, 0); // disable interrupts + while (*s && count-- > 0) { + uartlite_putchar(up, *s); + if (*s == '\n') + uartlite_putchar(up, '\r'); + s++; + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + //uartlite_wait_for_xmitr_empty(up); + uartlite_set_ier(up, up->ier); // restore interrupts +} + +static int uartlite_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &uartlite_ports[co->index].port; + if (!port->iobase && !port->membase) + return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver uartlite_reg; +static struct console uartlite_console = { + .name = "ttyS", + .write = uartlite_console_write, + .device = uart_console_device, + .setup = uartlite_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &uartlite_reg, +}; + +static int __init uartlite_console_init(void) +{ + struct uartlite_port *port = &uartlite_ports[0]; + + serial_reset(port); + serial_init(port); + + uartlite_init_ports(); + register_console(&uartlite_console); + return 0; +} +console_initcall(uartlite_console_init); + +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE +#define SERIALUARTLITE_CONSOLE &uartlite_console +#else +#define SERIALUARTLITE_CONSOLE NULL +#endif + +static struct uart_driver uartlite_reg = { + .owner = THIS_MODULE, + .driver_name = "uartlite", + .devfs_name = "tts/", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, + .cons = SERIALUARTLITE_CONSOLE, +}; + +int __init early_serial_setup(struct uart_port *port) +{ + // dump_port(port); + if (port->line >= ARRAY_SIZE(uartlite_ports)) + return -ENODEV; + + uartlite_init_ports(); + uartlite_ports[port->line].port = *port; + uartlite_ports[port->line].port.ops = &uartlite_pops; + return 0; +} + +/** + * uartlite_suspend_port - suspend one serial port + * @line: serial line number + * @level: the level of port suspension, as per uart_suspend_port + * + * Suspend one serial port. + */ +void uartlite_suspend_port(int line) +{ + uart_suspend_port(&uartlite_reg, &uartlite_ports[line].port); +} + +/** + * uartlite_resume_port - resume one serial port + * @line: serial line number + * @level: the level of port resumption, as per uart_resume_port + * + * Resume one serial port. + */ +void uartlite_resume_port(int line) +{ + uart_resume_port(&uartlite_reg, &uartlite_ports[line].port); +} + +static int __init uartlite_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: uartlite driver $Revision: 0.10 $ " "%d ports\n", (int) UART_NR); + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&uartlite_reg); +#if 1 // ~8250 + if (ret >= 0) + uartlite_register_ports(&uartlite_reg); +#endif + + return ret; +} + +static void __exit uartlite_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&uartlite_reg, &uartlite_ports[i].port); + + uart_unregister_driver(&uartlite_reg); +} + +module_init(uartlite_init); +module_exit(uartlite_exit); + +EXPORT_SYMBOL(uartlite_suspend_port); +EXPORT_SYMBOL(uartlite_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic uartlite serial driver $Revision: 0.10 $"); +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); + + diff --git a/drivers/serial/uartlite_early.c b/drivers/serial/uartlite_early.c new file mode 100644 index 0000000..1e3696b --- /dev/null +++ b/drivers/serial/uartlite_early.c @@ -0,0 +1,233 @@ +/* + * Early serial console for Xilinx UartLite devices + * + * (c) Copyright 2005 DLA Systems + * David H. Lynch Jr. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on the 8250_early.c driver + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttyS0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * console=uart,io,0x3f8,9600n8 + * console=uart,mmio,0xff5e0000,115200n8 + * or platform code can call early_uart_console_init() to set + * the early UART device. + * + * After the normal serial driver starts, we try to locate the + * matching ttyS device and start a console there. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct early_uart_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; +}; + +static struct early_uart_device early_device __initdata; +static int early_uart_registered __initdata; + +static _INLINE_ unsigned int __init serial_in(struct uart_port *up, int offset) +{ + unsigned int value; + offset <<= up->port.regshift; + switch (up->port.iotype) { + default: + value = (*(volatile unsigned int *) ( up->port.membase + offset)); + __asm__ __volatile__("eieio"); + return value; + } +} + +static _INLINE_ void +__init serial_out(struct uart_port *port, int offset, int value) +{ + offset <<= up->port.regshift; + switch (up->port.iotype) { + default: + (*(volatile unsigned int *)( up->port.membase + offset) =value); + __asm__ __volatile__("eieio"); + break; + + } +} + +static void __init wait_for_xmitr(struct uart_port *port) +{ + unsigned int status; + + for (;;) { + status = serial_in(port, UART_LSR); + if ((status & UART_LSR_TXF) == UART_LSR_TXF) + return; + cpu_relax(); + } +} + +static void __init putc(struct uart_port *port, unsigned char c) +{ + wait_for_xmitr(port); + serial_out(port, UART_TX, c); +} + +static void __init early_uart_write(struct console *console, const char *s, unsigned int count) +{ + struct uart_port *port = &early_device.port; + while (*s && count-- > 0) { + putc(port, *s); + if (*s == '\n') + putc(port, '\r'); + s++; + } + + /* Wait for transmitter to become empty and restore the IER */ + wait_for_xmitr(port); +} + +static unsigned int __init probe_baud(struct uart_port *port) +{ + return 56700; +} + +static void __init init_port(struct early_uart_device *device) +{ +} + +static int __init parse_options(struct early_uart_device *device, char *options) +{ + struct uart_port *port = &device->port; + int mapsize = 64; + int mmio, length; + + if (!options) + return -ENODEV; + + port->uartclk = BASE_BAUD ; + if (!strncmp(options, "mmio,", 5)) { + port->iotype = UPIO_MEM; + port->mapbase = simple_strtoul(options + 5, &options, 0); + port->membase = ioremap(port->mapbase, mapsize); + if (!port->membase) { + printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n", + __FUNCTION__, port->mapbase); + return -ENOMEM; + } + mmio = 1; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + port->iobase = simple_strtoul(options + 3, &options, 0); + mmio = 0; + } else + return -EINVAL; + + if ((options = strchr(options, ','))) { + options++; + device->baud = simple_strtoul(options, 0, 0); + length = min(strcspn(options, " "), sizeof(device->options)); + strncpy(device->options, options, length); + } else { + device->baud = probe_baud(port); + snprintf(device->options, sizeof(device->options), "%u", + device->baud); + } + + printk(KERN_INFO "Early serial console at %s 0x%lx (options '%s')\n", + mmio ? "MMIO" : "I/O port", + mmio ? port->mapbase : (unsigned long) port->iobase, + device->options); + return 0; +} + +static int __init early_uart_setup(struct console *console, char *options) +{ + struct early_uart_device *device = &early_device; + int err; + + if (device->port.membase || device->port.iobase) + return 0; + + if ((err = parse_options(device, options)) < 0) + return err; + + init_port(device); + return 0; +} + +static struct console early_uart_console __initdata = { + .name = "uart", + .write = early_uart_write, + .setup = early_uart_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static int __init early_uart_console_init(void) +{ + if (!early_uart_registered) { + register_console(&early_uart_console); + early_uart_registered = 1; + } + return 0; +} +console_initcall(early_uart_console_init); + +int __init early_serial_console_init(char *cmdline) +{ + char *options; + int err; + + options = strstr(cmdline, "console=uart,"); + if (!options) + return -ENODEV; + + options = strchr(cmdline, ',') + 1; + if ((err = early_uart_setup(NULL, options)) < 0) + return err; + return early_uart_console_init(); +} + +static int __init early_uart_console_switch(void) +{ + struct early_uart_device *device = &early_device; + struct uart_port *port = &device->port; + int mmio, line = -1; + + if (!(early_uart_console.flags & CON_ENABLED)) + return 0; + + /* Try to start the normal driver on a matching line. */ + mmio = (port->iotype == UPIO_MEM); +#if 0 + line = uartlite_start_console(port, device->options); +#endif + if (line < 0) + printk("No ttyS device at %s 0x%lx for console\n", + mmio ? "MMIO" : "I/O port", + mmio ? port->mapbase : + (unsigned long) port->iobase); + + unregister_console(&early_uart_console); + if (mmio) + iounmap(port->membase); + + return 0; +} +late_initcall(early_uart_console_switch); + diff --git a/include/linux/serial.h b/include/linux/serial.h index 33fc8cb..99b8263 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -182,6 +182,7 @@ struct uart_port; /* forward declaration extern int early_serial_setup(struct uart_port *port); extern int early_serial_console_init(char *options); extern int serial8250_start_console(struct uart_port *port, char *options); +extern int uartlite_start_console(struct uart_port *port, char *options); #endif /* __KERNEL__ */ #endif /* _LINUX_SERIAL_H */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index e3710d7..053dba4 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -124,6 +124,9 @@ /* Hilscher netx */ #define PORT_NETX 71 +/*Xilinx UartLite */ +#define PORT_UARTLITE 72 + #ifdef __KERNEL__ #include diff --git a/include/linux/serial_uartlite.h b/include/linux/serial_uartlite.h new file mode 100644 index 0000000..28f92e6 --- /dev/null +++ b/include/linux/serial_uartlite.h @@ -0,0 +1,112 @@ +/* + * linux/include/linux/serial_uartlite.h + * + * Driver definitions for Xilinx uartlite serial ports + * + * Author: David H. Lynch Jr. + * Copyright (C) 2005 DLA Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * $Id: uartlite.h,v 0.8 2005/12/15 21:32:30 dhlii Exp $ + * + * Register names bit definititions etc are deliberately named + * the same as the 8250 + * + */ +#ifndef _LINUX_SERIAL_UARTLITE_H +#define _LINUX_SERIAL_UARTLITE_H + +#include +#include +#include + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char hub6; + unsigned char io_type; + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned int type; +}; + + +struct plat_serialuartlite_port { + unsigned long iobase; /* io base address */ + void __iomem *membase; /* ioremap cookie or NULL */ + unsigned long mapbase; /* resource base */ + unsigned int irq; /* interrupt number */ + unsigned int uartclk; /* UART clock rate */ + unsigned char regshift; /* register shift */ + unsigned char iotype; /* UPIO_* */ + unsigned char hub6; + unsigned int flags; /* UPF_* flags */ +}; + +/* + * This replaces serial_uart_config in include/linux/serial.h + */ +struct serial_uartlite_config { + const char *name; + unsigned short fifo_size; + unsigned short tx_loadsz; + unsigned int flags; +}; + +#define UART_RX 0 +#define UART_TX 1 +#define UART_LSR 2 +#define UART_LSR_DR (1 << (31-31)) +#define UART_LSR_RXF (1 << (31-30)) +#define UART_LSR_THRE (1 << (31-29)) +#define UART_LSR_TEMT (1 << (31-29)) +#define UART_LSR_TXF (1 << (31-28)) +#define UART_LSR_OE (1 << (31-26)) +#define UART_LSR_FE (1 << (31-25)) +#define UART_LSR_PE (1 << (31-24)) +#define UART_LSR_BI 0 // UartLite has no Break Indicator + +#define UART_IIR 2 +#define UART_IIR_NO_INT (1 << (31-27)) +#undef UART_IIR_NO_INT +#define UART_IIR_NO_INT 0 // Not present on UartLite +#define UART_MSR 2 + +#define UART_FCR 3 +#define UART_LCR 3 +#define UART_LCR_TXF (1 << (31-30)) +#define UART_LCR_RXF (1 << (31-30)) +#define UART_LCR_WLEN5 0 // UartLite serial parameters are fixed +#define UART_LCR_WLEN6 0 +#define UART_LCR_WLEN7 0 +#define UART_LCR_WLEN8 0 +#define UART_LCR_STOP 0 +#define UART_LCR_PARITY 0 +#define UART_LCR_EPAR 0 +#define UART_IER 3 +#define UART_IER_THRI (1 << (31-27)) +#define UART_IER_RLSI (1 << (31-27)) +#define UART_IER_MSI 0 // UartLite has no Modem Status +#define UART_IER_RDI (1 << (31-27)) + +#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +// #define XPAR_UARTLITE 0x40600000 + +#define serial_in32(port, offset) (*(volatile unsigned long *)(port + (offset << 2))); __asm__ __volatile__ ("eieio"); +#define serial_out32(port, offset, value) { (*(volatile unsigned long *)(port + (offset << 2)) = value); __asm__ __volatile__ ("eieio"); } + +#endif // _LINUX_SERIAL_UARTLITE_H +