--- /dev/null +++ b/drivers/serial/it8712.c @@ -0,0 +1,858 @@ +/* + * linux/drivers/char/serial_uart00.c + * + * Driver for UART00 serial ports + * + * Based on drivers/char/serial_amba.c, by ARM Limited & + * Deep Blue Solutions Ltd. + * Copyright 2001 Altera Corporation + * + * 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: it8712.c,v 1.2 2006/06/06 06:36:04 middle Exp $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_IT8712_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include "it8712.h" + +//#define DEBUG 1 +#define UART_NR 1 + +#define SERIAL_IT8712_NAME "ttySI" +#define SERIAL_IT8712_MAJOR 204 +#define SERIAL_IT8712_MINOR 41 /* Temporary - will change in future */ +#define SERIAL_IT8712_NR UART_NR +#define UART_PORT_SIZE 0x50 +#define LPC_HOST_CONTINUE_MODE 0x00000040 + +#define IT8712_NO_PORTS UART_NR +#define IT8712_ISR_PASS_LIMIT 256 + +#define LPC_BUS_CTRL *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 4)) +#define LPC_BUS_STATUS *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 4)) +#define LPC_SERIAL_IRQ_CTRL *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 8)) +#define LPC_SERIAL_IRQ_STATUS *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 0x0c)) +#define LPC_SERIAL_IRQ_TRITYPE *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 0x10)) +#define LPC_SERIAL_IRQ_POLARITY *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 0x14)) +#define LPC_SERIAL_IRQ_ENABLE *(unsigned int*)(IO_ADDRESS(SL2312_LPC_HOST_BASE + 0x18)) + + + + +/* + * Access macros for the SL2312 UARTs + */ +#define UART_GET_INT_STATUS(p) (inb(((p)->membase+UART_IIR)) & 0x0F) // interrupt identification +#define UART_PUT_IER(p, c) outb(c,((p)->membase+UART_IER)) // interrupt enable +#define UART_GET_IER(p) inb(((p)->membase+UART_IER)) +#define UART_PUT_CHAR(p, c) outb(c,((p)->membase+UART_TX)) // transmitter holding +#define UART_GET_CHAR(p) inb(((p)->membase+UART_RX)) // receive buffer +#define UART_GET_LSR(p) inb(((p)->membase+UART_LSR)) // line status +#define UART_GET_MSR(p) inb(((p)->membase+UART_MSR)) // modem status +#define UART_GET_MCR(p) inb(((p)->membase+UART_MCR)) // modem control +#define UART_PUT_MCR(p, c) outb(c,((p)->membase+UART_MCR)) +#define UART_GET_LCR(p) inb(((p)->membase+UART_LCR)) // mode control +#define UART_PUT_LCR(p, c) outb(c,((p)->membase+UART_LCR)) +#define UART_PUT_FCR(p, c) outb(c,((p)->membase+UART_FCR)) // fifo control +#define UART_GET_DIV_HI(p) inb(((p)->membase+UART_DLM)) +#define UART_PUT_DIV_HI(p, c) outb(c,((p)->membase+UART_DLM)) +#define UART_GET_DIV_LO(p) inb(((p)->membase+UART_DLL)) +#define UART_PUT_DIV_LO(p, c) outb(c,((p)->membase+UART_DLL)) +#define UART_PUT_MDR(p, c) outb(c,UART_MDR((p)->membase)) +#define UART_RX_DATA(s) ((s) & UART_LSR_DR) +#define UART_TX_READY(s) ((s) & UART_LSR_THRE) + +static void it8712_stop_tx(struct uart_port *port, u_int from_tty) +{ + unsigned int reg; + + //printk("it8712 stop tx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_THRI); + UART_PUT_IER(port, reg); +} + +static void it8712_stop_rx(struct uart_port *port) +{ + unsigned int reg; + + //printk("it8712 stop rx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_RDI); + UART_PUT_IER(port, reg); + +} + +static void it8712_enable_ms(struct uart_port *port) +{ + unsigned int reg; + + //printk("it8712 enable ms : \n"); + + reg = UART_GET_IER(port); + reg |= (UART_IER_MSI); + UART_PUT_IER(port, reg); + +} + +static void it8712_rx_chars(struct uart_port *port, struct pt_regs *regs) +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, mask, ch, flg, ignored = 0; + + // printk("it8712_rx_chars : \n"); + status = UART_GET_LSR(port); + while (UART_RX_DATA(status)) { + + /* + * We need to read rds before reading the + * character from the fifo + */ + ch = UART_GET_CHAR(port); + port->icount.rx++; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + + if (status & (UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI|UART_LSR_DE)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UART_GET_LSR(port); + } // end of while +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (status & UART_LSR_BI) { + status &= ~(UART_LSR_FE); + port->icount.brk++; + +#ifdef SUPPORT_SYSRQ + if (uart_handle_break(port)) + goto ignore_char; +#endif + } else if (status & UART_LSR_PE) + port->icount.parity++; + else if (status & UART_LSR_FE) + port->icount.frame++; + + if (status & UART_LSR_OE) + port->icount.overrun++; + + if (status & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + mask = status & port->read_status_mask; + + if (mask & UART_LSR_BI) + flg = TTY_BREAK; + else if (mask & UART_LSR_PE) + flg = TTY_PARITY; + else if (mask & UART_LSR_FE) + flg = TTY_FRAME; + + if (status & UART_LSR_OE) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void it8712_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + it8712_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + it8712_stop_tx(port, 0); +} + +static void it8712_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned int reg; + + //printk("it8712 start tx : \n"); + reg = UART_GET_IER(port); + reg |= (UART_IER_THRI); + UART_PUT_IER(port, reg); + it8712_tx_chars(port); +} + +static void it8712_modem_status(struct uart_port *port) +{ + unsigned int status; + +// printk("it8712 modem status : \n"); + + status = UART_GET_MSR(port); + + if (!(status & (UART_MSR_DCTS | UART_MSR_DDSR | + UART_MSR_TERI | UART_MSR_DDCD))) + return; + + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(port, status & UART_MSR_DCD); + + if (status & UART_MSR_DDSR) + port->icount.dsr++; + + if (status & UART_MSR_DCTS) + uart_handle_cts_change(port, status & UART_MSR_CTS); + + wake_up_interruptible(&port->info->delta_msr_wait); + +} + +static irqreturn_t it8712_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = 0, data; + + + data = LPC_SERIAL_IRQ_STATUS; + if((data&0x10)==0x10) + { + status = UART_GET_INT_STATUS(port); + do { +// printk("it8712_int: status %x \n", status); + switch(status) + { + case UART_IIR_RDI: + case UART_IIR_RLSI: + case UART_IIR_RCTO: + it8712_rx_chars(port, regs); + break; + case UART_IIR_THRI: + it8712_tx_chars(port); + break; + case UART_IIR_MSI: + it8712_modem_status(port); + break; + default: + break; + } + if (pass_counter++ > IT8712_ISR_PASS_LIMIT) + break; + + status = UART_GET_INT_STATUS(port); + } while (status); + } + + status = 0; + status |= (IRQ_LPC_MASK); + *((volatile unsigned int *)IRQ_CLEAR(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = status; + + //cnt=0; + //do{ + // data = LPC_SERIAL_IRQ_STATUS; + LPC_SERIAL_IRQ_STATUS = data; + // cnt++; + //}while(data); + //if(cnt>2) + // printf("it8712_uart_Isr clear LPC_SERIAL_IRQ_STATUS %x \n", cnt); + return IRQ_HANDLED; +} + +static u_int it8712_tx_empty(struct uart_port *port) +{ +// printk("it8712 tx empty : \n"); + + return ((UART_GET_LSR(port) & UART_LSR_THRE)? TIOCSER_TEMT : 0); +} + +static u_int it8712_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + +// printk("it8712 get mctrl : \n"); + + status = UART_GET_MSR(port); + if (status & UART_MSR_DCD) + result |= TIOCM_CAR; + if (status & UART_MSR_DSR) + result |= TIOCM_DSR; + if (status & UART_MSR_CTS) + result |= TIOCM_CTS; + if (status & UART_MSR_RI) + result |= TIOCM_RI; + + return result; +} + +static void it8712_set_mctrl_null(struct uart_port *port, u_int mctrl) +{ +} + +static void it8712_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int lcr; + +// printk("it8712 break ctl : \n"); + + lcr = UART_GET_LCR(port); + if (break_state == -1) + lcr |= UART_LCR_SBC; + else + lcr &= ~UART_LCR_SBC; + UART_PUT_LCR(port, lcr); +} + +static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + quot = (port->uartclk/(16 * baud)) ; + + return quot; +} +static void it8712_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + unsigned int uart_mc, old_ier, baud, quot; + unsigned long flags; + + termios->c_cflag |= CREAD; + termios->c_cflag |= CLOCAL; +#ifdef DEBUG + printk("it8712_set_cflag(0x%x) called\n", cflag); +#endif + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + /* byte size and parity */ + switch (termios->c_cflag & CSIZE) { + case CS5: + uart_mc = UART_LCR_WLEN5; + break; + case CS6: + uart_mc = UART_LCR_WLEN6; + break; + case CS7: + uart_mc = UART_LCR_WLEN7; + break; + default: // CS8 + uart_mc = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + uart_mc|= UART_LCR_STOP; + if (termios->c_cflag & PARENB) { + uart_mc |= UART_LCR_EVEN; + if (!(termios->c_cflag & PARODD)) + uart_mc |= UART_LCR_ODD; + } + + spin_lock_irqsave(&port->lock, flags); + /* + * Update the per-port timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + port->read_status_mask = UART_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + old_ier = UART_GET_IER(port); + + if(UART_ENABLE_MS(port, termios->c_cflag)) + old_ier |= UART_IER_MSI; + + /* Set baud rate */ + quot = quot / 13; + UART_PUT_LCR(port, UART_LCR_DLAB); + UART_PUT_DIV_LO(port, (quot & 0xff)); + UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); + + UART_PUT_LCR(port, uart_mc); +// UART_PUT_LCR(port, 0x07); // ???? it is wired + UART_PUT_MCR(port, 0x08); + UART_PUT_FCR(port, 0x01); + UART_PUT_IER(port, 0x07); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int it8712_startup(struct uart_port *port) +{ + int retval, i; + unsigned int regs; + + //printk("it8712 startup : \n"); + + /* + * Use iobase to store a pointer to info. We need this to start a + * transmission as the tranmittr interrupt is only generated on + * the transition to the idle state + */ + + // regs = 0; + // regs |= (IRQ_LPC_MASK); + // *((volatile unsigned int *)IRQ_CLEAR(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, it8712_int, SA_INTERRUPT, "it8712", port); + if (retval) + return retval; + + //printk("Init LPC int...........\n"); + /* setup interrupt controller */ + regs = *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs &= ~(IRQ_LPC_MASK); + *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + regs = *((volatile unsigned int *)IRQ_TLEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs &= ~(IRQ_LPC_MASK); + *((volatile unsigned int *)IRQ_TLEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + *((volatile unsigned int *)IRQ_MASK(IO_ADDRESS(SL2312_INTERRUPT_BASE))) |= (unsigned int)(IRQ_LPC_MASK); + + LPC_SERIAL_IRQ_POLARITY = 0x10; //0x10; //0x02; + LPC_SERIAL_IRQ_TRITYPE = 0x10; //0x10;// + LPC_SERIAL_IRQ_ENABLE = 0x10; + + LPC_BUS_CTRL = 0xc0; + LPC_SERIAL_IRQ_CTRL = 0xc0; + for(i=0;i<1000;i++) ; + LPC_SERIAL_IRQ_CTRL = 0x80; + /* + * Finally, enable interrupts. Use the TII interrupt to minimise + * the number of interrupts generated. If higher performance is + * needed, consider using the TI interrupt with a suitable FIFO + * threshold + */ + //UART_PUT_IER(port, (UART_IER_RDI|UART_IER_THRI)); + UART_PUT_IER(port, (UART_IER_RDI|UART_IER_THRI|UART_IER_RLSI));//middle + + return 0; +} + +static void it8712_shutdown(struct uart_port *port) +{ + //printk("it8712 shutdown : \n"); + + /* + * disable all interrupts, disable the port + */ + UART_PUT_IER(port, 0x0); + + /* disable break condition and fifos */ +// UART_PUT_MCR(port, (UART_GET_MCR(port)&UART_MCR_MASK)); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); +} + +static const char *it8712_type(struct uart_port *port) +{ + return port->type == PORT_IT8712 ? "IT8712" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void it8712_release_port(struct uart_port *port) +{ +// printk("it8712 release port : \n"); + + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int it8712_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_it8712") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void it8712_config_port(struct uart_port *port, int flags) +{ + + if (flags & UART_CONFIG_TYPE) { + if (it8712_request_port(port) == 0) + port->type = PORT_IT8712; + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int it8712_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops it8712_pops = { + .tx_empty = it8712_tx_empty, + .set_mctrl = it8712_set_mctrl_null, + .get_mctrl = it8712_get_mctrl, + .stop_tx = it8712_stop_tx, + .start_tx = it8712_start_tx, + .stop_rx = it8712_stop_rx, + .enable_ms = it8712_enable_ms, + .break_ctl = it8712_break_ctl, + .startup = it8712_startup, + .shutdown = it8712_shutdown, + .set_termios = it8712_set_termios, + .type = it8712_type, + .release_port = it8712_release_port, + .request_port = it8712_request_port, + .config_port = it8712_config_port, + .verify_port = it8712_verify_port, +}; + +#ifdef CONFIG_ARCH_SL2312 + +static struct uart_port it8712_ports[UART_NR] = { + { + membase: (void *)0, + mapbase: 0, + iotype: SERIAL_IO_MEM, + irq: 0, + uartclk: UART_CLK/2, + fifosize: 16, + ops: &it8712_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#endif + +#ifdef CONFIG_SERIAL_IT8712_CONSOLE +#ifdef used_and_not_const_char_pointer +static int it8712_console_read(struct uart_port *port, char *s, u_int count) +{ + unsigned int status; + int c; +#ifdef DEBUG + printk("it8712_console_read() called\n"); +#endif + + c = 0; + while (c < count) { + status = UART_GET_LSR(port); + if (UART_RX_DATA(status)) { + *s++ = UART_GET_CHAR(port); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif +static void it8712_console_write(struct console *co, const char *s, unsigned count) +{ +#ifdef CONFIG_ARCH_SL2312 + struct uart_port *port = it8712_ports + co->index; + unsigned int status, old_ies; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_ies = UART_GET_IER(port); + //if(old_ies!=7) + //{ + // + // printk("old_ies = %x\n",old_ies); + // old_ies = 7; + //} + UART_PUT_IER(port,0x0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IES + */ + do { + status = UART_GET_LSR(port); + } while (!(status&UART_LSR_THRE)); + UART_PUT_IER(port, old_ies); +#endif +} + +static void /*__init*/ it8712_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + //printk("it8712 console get options : \n"); + + u_int uart_mc, quot; + uart_mc= UART_GET_MCR(port); + + *parity = 'n'; + if (uart_mc & UART_LCR_PARITY) { + if (uart_mc & UART_LCR_EVEN) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (uart_mc & UART_LCR_MSK){ + + case UART_LCR_WLEN5: + *bits = 5; + break; + case UART_LCR_WLEN6: + *bits = 6; + break; + case UART_LCR_WLEN7: + *bits = 7; + break; + case UART_LCR_WLEN8: + *bits = 8; + break; + } + UART_PUT_MCR(port,UART_LCR_DLAB); + quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); + UART_PUT_MCR(port,uart_mc); + *baud = (port->uartclk / (16 *quot)); +} + +static int __init it8712_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow= 'n'; + int base;//, irq; + int i ; + + printk("it8712 console setup : \n"); + + LPCSetConfig(0, 0x02, 0x01); + LPCSetConfig(LDN_SERIAL1, 0x30, 0x1); + LPCSetConfig(LDN_SERIAL1, 0x23, 0x0); + base = IT8712_IO_BASE; + base += ((LPCGetConfig(LDN_SERIAL1, 0x60) << 8) + LPCGetConfig(LDN_SERIAL1, 0x61)); + it8712_ports[0].mapbase = base; + it8712_ports[0].membase = (void *)IO_ADDRESS(base); + it8712_ports[0].irq = IRQ_LPC_OFFSET; + // irq = LPCGetConfig(LDN_SERIAL1, 0x70); + //it8712_ports[0].irq += irq; + + //printk("it8712 irq is %x \n", it8712_ports[0].irq); + + // setup LPC Host 'quiet mode' + //*((volatile unsigned int *)IO_ADDRESS((SL2312_LPC_HOST_BASE+0x04))) |= LPC_HOST_CONTINUE_MODE ; + //for(i=0;i<1000;i++) ; // delay + //*((volatile unsigned int *)IO_ADDRESS((SL2312_LPC_HOST_BASE+0x04))) &= ~(LPC_HOST_CONTINUE_MODE) ; + LPC_BUS_CTRL = 0xc0; + LPC_SERIAL_IRQ_CTRL = 0xc0; + for(i=0;i<1000;i++) ; + LPC_SERIAL_IRQ_CTRL = 0x80; + +#ifdef CONFIG_ARCH_SL2312 + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(it8712_ports,IT8712_NO_PORTS,co); +#else + return -ENODEV; +#endif + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + it8712_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +extern struct uart_driver it8712_reg; +static struct console it8712_console = { + .name = SERIAL_IT8712_NAME, + .write = it8712_console_write, + .device = uart_console_device, + .setup = it8712_console_setup, + .flags = CON_PRINTBUFFER, + .index = 0, + .data = &it8712_reg, +}; + +static int __init it8712_console_init(void) +{ + register_console(&it8712_console); + return 0; +} + +console_initcall(it8712_console_init); + +#define IT8712_CONSOLE &it8712_console +#else +#define IT8712_CONSOLE NULL +#endif + +static struct uart_driver it8712_reg = { + .owner = NULL, + .driver_name = SERIAL_IT8712_NAME, + .dev_name = SERIAL_IT8712_NAME, + .major = SERIAL_IT8712_MAJOR, + .minor = SERIAL_IT8712_MINOR, + .nr = UART_NR, + .cons = IT8712_CONSOLE, +}; + +static int __init it8712_init(void) +{ + int result; + //printk("serial_it8712: it871212_init \n"); + + + result = uart_register_driver(&it8712_reg); + if(result) + return result; + result = uart_add_one_port(&it8712_reg, &it8712_ports[0]); + + return result; + +} + + +__initcall(it8712_init); --- /dev/null +++ b/drivers/serial/it8712.h @@ -0,0 +1,135 @@ +#define UART_RX 0 /* In: Receive buffer (DLAB=0) */ +#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_FCTR 1 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_EFR 2 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_SCR 7 /* I/O: Scratch Register */ +#define UART_EMSR 7 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +/* + * These are the definitions for the FIFO Control Register + * (16650 only) + */ +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ +#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ +#define UART_FCR_TRIGGER_1 0x00 /* Mask for trigger set at 1 */ +#define UART_FCR_TRIGGER_4 0x40 /* Mask for trigger set at 4 */ +#define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */ +#define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */ +/* 16650 redefinitions */ +#define UART_FCR6_R_TRIGGER_8 0x00 /* Mask for receive trigger set at 1 */ +#define UART_FCR6_R_TRIGGER_16 0x40 /* Mask for receive trigger set at 4 */ +#define UART_FCR6_R_TRIGGER_24 0x80 /* Mask for receive trigger set at 8 */ +#define UART_FCR6_R_TRIGGER_28 0xC0 /* Mask for receive trigger set at 14 */ +#define UART_FCR6_T_TRIGGER_16 0x00 /* Mask for transmit trigger set at 16 */ +#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */ +#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */ +#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */ +/* TI 16750 definitions */ +#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode */ + +/* + * These are the definitions for the Line Control Register + * + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ +#define UART_LCR_EVEN 0x18 /* Even parity */ +#define UART_LCR_ODD 0x08 /* Odd parity */ +#define UART_LCR_MSK 0x03 +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_DE 0x80 /* FIFO Data Error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ + +/* + * These are the definitions for the Interrupt Identification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ + +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ +#define UART_IIR_RCTO 0x0c /* Receiver character timeout interrupt */ +/* + * These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ +/* + * Sleep mode for ST16650 and TI16750. + * Note that for 16650, EFR-bit 4 must be selected as well. + */ +#define UART_IERX_SLEEP 0x10 /* Enable sleep mode */ + +/* + * These are the definitions for the Modem Control Register + */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +/* + * These are the definitions for the Modem Status Register + */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ + +#define UART_PARITY_NONE 0x00 +#define UART_PARITY_ODD 0x01 +#define UART_PARITY_EVEN 0x02 + + + --- /dev/null +++ b/drivers/serial/serial_it8712.c @@ -0,0 +1,876 @@ +/* + * linux/drivers/char/serial_uart00.c + * + * Driver for UART00 serial ports + * + * Based on drivers/char/serial_amba.c, by ARM Limited & + * Deep Blue Solutions Ltd. + * Copyright 2001 Altera Corporation + * + * 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: serial_it8712.c,v 1.1.1.1 2006/04/03 08:41:00 amos_lee Exp $ + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_IT8712_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include "serial_it8712.h" + +//#define DEBUG 1 +#define UART_NR 1 + +#define SERIAL_IT8712_NAME "ttySI" +#define SERIAL_IT8712_MAJOR 204 +#define SERIAL_IT8712_MINOR 41 /* Temporary - will change in future */ +#define SERIAL_IT8712_NR UART_NR +#define UART_PORT_SIZE 0x50 + +#define CALLOUT_IT8712_NAME "cuaslI" +#define CALLOUT_IT8712_MAJOR 205 +#define CALLOUT_IT8712_MINOR 41 /* Temporary - will change in future */ +#define CALLOUT_IT8712_NR UART_NR +#define LPC_HOST_CONTINUE_MODE 0x00000040 + +#define IT8712_NO_PORTS UART_NR + +static struct tty_driver normal, callout; +static struct tty_struct *it8712_table[UART_NR]; +static struct termios *it8712_termios[UART_NR], *it8712_termios_locked[UART_NR]; +static struct console it8712_console; + +#define IT8712_ISR_PASS_LIMIT 256 + +/* + * Access macros for the SL2312 UARTs + */ +#define UART_GET_INT_STATUS(p) (inb(((p)->membase+UART_IIR)) & 0x0F) // interrupt identification +#define UART_PUT_IER(p, c) outb(c,((p)->membase+UART_IER)) // interrupt enable +#define UART_GET_IER(p) inb(((p)->membase+UART_IER)) +#define UART_PUT_CHAR(p, c) outb(c,((p)->membase+UART_TX)) // transmitter holding +#define UART_GET_CHAR(p) inb(((p)->membase+UART_RX)) // receive buffer +#define UART_GET_LSR(p) inb(((p)->membase+UART_LSR)) // line status +#define UART_GET_MSR(p) inb(((p)->membase+UART_MSR)) // modem status +#define UART_GET_MCR(p) inb(((p)->membase+UART_MCR)) // modem control +#define UART_PUT_MCR(p, c) outb(c,((p)->membase+UART_MCR)) +#define UART_GET_LCR(p) inb(((p)->membase+UART_LCR)) // mode control +#define UART_PUT_LCR(p, c) outb(c,((p)->membase+UART_LCR)) +#define UART_PUT_FCR(p, c) outb(c,((p)->membase+UART_FCR)) // fifo control +#define UART_GET_DIV_HI(p) inb(((p)->membase+UART_DLM)) +#define UART_PUT_DIV_HI(p, c) outb(c,((p)->membase+UART_DLM)) +#define UART_GET_DIV_LO(p) inb(((p)->membase+UART_DLL)) +#define UART_PUT_DIV_LO(p, c) outb(c,((p)->membase+UART_DLL)) +#define UART_PUT_MDR(p, c) outb(c,UART_MDR((p)->membase)) +#define UART_RX_DATA(s) ((s) & UART_LSR_DR) +#define UART_TX_READY(s) ((s) & UART_LSR_THRE) + +static void it8712_stop_tx(struct uart_port *port, u_int from_tty) +{ + unsigned int reg; + +// printk("it8712 stop tx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_THRI); + UART_PUT_IER(port, reg); +} + +static void it8712_stop_rx(struct uart_port *port) +{ + unsigned int reg; + +// printk("it8712 stop rx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_RDI); + UART_PUT_IER(port, reg); + +} + +static void it8712_enable_ms(struct uart_port *port) +{ + unsigned int reg; + +// printk("it8712 enable ms : \n"); + + reg = UART_GET_IER(port); + reg |= (UART_IER_MSI); + UART_PUT_IER(port, reg); + +} + +static void +it8712_rx_chars(struct uart_info *info, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned int status, mask, ch, flg, ignored = 0; + struct uart_port *port = info->port; + + // printk("it8712_rx_chars : \n"); + status = UART_GET_LSR(port); + while (UART_RX_DATA(status)) { + + /* + * We need to read rds before reading the + * character from the fifo + */ + ch = UART_GET_CHAR(port); + port->icount.rx++; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + + if (status & (UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI|UART_LSR_DE)) + goto handle_error; + if (uart_handle_sysrq_char(info, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UART_GET_LSR(port); + } // end of while +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (status & UART_LSR_BI) { + status &= ~(UART_LSR_FE); + port->icount.brk++; + +#ifdef SUPPORT_SYSRQ + if (uart_handle_break(info, &it8712_console)) + goto ignore_char; +#endif + } else if (status & UART_LSR_PE) + port->icount.parity++; + else if (status & UART_LSR_FE) + port->icount.frame++; + + if (status & UART_LSR_OE) + port->icount.overrun++; + + if (status & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + mask = status & port->read_status_mask; + + if (mask & UART_LSR_BI) + flg = TTY_BREAK; + else if (mask & UART_LSR_PE) + flg = TTY_PARITY; + else if (mask & UART_LSR_FE) + flg = TTY_FRAME; + + if (status & UART_LSR_OE) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + info->sysrq = 0; +#endif + goto error_return; +} + +static void it8712_tx_chars(struct uart_info *info) +{ + int count; + struct uart_port *port=info->port; + + if (port->x_char) { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + it8712_stop_tx(info->port, 0); + return; + } + + count = port->fifosize >> 1; + do { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + UART_XMIT_SIZE) < WAKEUP_CHARS) + uart_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) + it8712_stop_tx(info->port, 0); +} + +static void it8712_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + unsigned int reg; + struct uart_info *info=(struct uart_info*)(port->iobase); + +// printk("it8712 start tx : \n"); + reg = UART_GET_IER(port); + reg |= (UART_IER_THRI); + UART_PUT_IER(port, reg); + it8712_tx_chars(info); +} + +static void it8712_modem_status(struct uart_info *info) +{ + unsigned int status; + struct uart_icount *icount = &info->port->icount; + +// printk("it8712 modem status : \n"); + + status = UART_GET_MSR(info->port); + + if (!(status & (UART_MSR_DCTS | UART_MSR_DDSR | + UART_MSR_TERI | UART_MSR_DDCD))) + return; + + if (status & UART_MSR_DCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD_MSK)) + hardpps(); +#endif + if (info->flags & ASYNC_CHECK_CD) { + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { + if (info->tty) + tty_hangup(info->tty); + } + } + } + + if (status & UART_MSR_DDSR) + icount->dsr++; + + if (status & UART_MSR_DCTS) { + icount->cts++; + + if (info->flags & ASYNC_CTS_FLOW) { + status &= UART_MSR_CTS; + + if (info->tty->hw_stopped) { + if (status) { + info->tty->hw_stopped = 0; + info->ops->start_tx(info->port, 1, 0); + uart_event(info, EVT_WRITE_WAKEUP); + } + } else { + if (!status) { + info->tty->hw_stopped = 1; + info->ops->stop_tx(info->port, 0); + } + } + } + } + wake_up_interruptible(&info->delta_msr_wait); + +} + +static void it8712_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + unsigned int status, pass_counter = 0; + + status = UART_GET_INT_STATUS(info->port); + do { +// printk("it8712_int: status %x \n", status); + switch(status) + { + case UART_IIR_RDI: + case UART_IIR_RLSI: + case UART_IIR_RCTO: + it8712_rx_chars(info, regs); + break; + case UART_IIR_THRI: + it8712_tx_chars(info); + break; + case UART_IIR_MSI: + it8712_modem_status(info); + break; + default: + break; + } + if (pass_counter++ > IT8712_ISR_PASS_LIMIT) + break; + + status = UART_GET_INT_STATUS(info->port); + } while (status); +} + +static u_int it8712_tx_empty(struct uart_port *port) +{ +// printk("it8712 tx empty : \n"); + + return ((UART_GET_LSR(port) & UART_LSR_THRE)? TIOCSER_TEMT : 0); +} + +static u_int it8712_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + +// printk("it8712 get mctrl : \n"); + + status = UART_GET_MSR(port); + if (status & UART_MSR_DCD) + result |= TIOCM_CAR; + if (status & UART_MSR_DSR) + result |= TIOCM_DSR; + if (status & UART_MSR_CTS) + result |= TIOCM_CTS; + if (status & UART_MSR_RI) + result |= TIOCM_RI; + + return result; +} + +static void it8712_set_mctrl_null(struct uart_port *port, u_int mctrl) +{ +} + +static void it8712_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int lcr; + +// printk("it8712 break ctl : \n"); + + lcr = UART_GET_LCR(port); + if (break_state == -1) + lcr |= UART_LCR_SBC; + else + lcr &= ~UART_LCR_SBC; + UART_PUT_LCR(port, lcr); +} + +static inline u_int uart_calculate_quot(struct uart_info *info, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + quot = (info->port->uartclk/(16 * baud)) ; + + return quot; +} +static void it8712_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + u_int uart_mc=0, old_ier; + unsigned long flags; + +#ifdef DEBUG + printk("it8712_set_cflag(0x%x) called\n", cflag); +#endif + + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: uart_mc = UART_LCR_WLEN5; break; + case CS6: uart_mc = UART_LCR_WLEN6; break; + case CS7: uart_mc = UART_LCR_WLEN7; break; + default: uart_mc = UART_LCR_WLEN8; break; // CS8 + } + if (cflag & CSTOPB) + uart_mc|= UART_LCR_STOP; + if (cflag & PARENB) { + uart_mc |= UART_LCR_EVEN; + if (!(cflag & PARODD)) + uart_mc |= UART_LCR_ODD; + } + + port->read_status_mask = UART_LSR_OE; + if (iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + /* first, disable everything */ + save_flags(flags); cli(); + old_ier = UART_GET_IER(port); + + if ((port->flags & ASYNC_HARDPPS_CD) || + (cflag & CRTSCTS) || !(cflag & CLOCAL)) + old_ier |= UART_IER_MSI; + + /* Set baud rate */ + quot = quot / 13; + UART_PUT_LCR(port, UART_LCR_DLAB); + UART_PUT_DIV_LO(port, (quot & 0xff)); + UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); + + UART_PUT_LCR(port, uart_mc); +// UART_PUT_LCR(port, 0x07); // ???? it is wired + UART_PUT_MCR(port, 0x08); + UART_PUT_FCR(port, 0x01); + UART_PUT_IER(port, 0x05); + + restore_flags(flags); +} + +static int it8712_startup(struct uart_port *port, struct uart_info *info) +{ + int retval; + unsigned int regs; + +// printk("it8712 startup : \n"); + + /* + * Use iobase to store a pointer to info. We need this to start a + * transmission as the tranmittr interrupt is only generated on + * the transition to the idle state + */ + + port->iobase=(u_int)info; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, it8712_int, SA_INTERRUPT, "it8712", info); + if (retval) + return retval; + + /* setup interrupt controller */ + regs = *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs |= (IRQ_SERIRQ0_MASK); + *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + regs = *((volatile unsigned int *)IRQ_LEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs &= ~(IRQ_SERIRQ0_MASK); + *((volatile unsigned int *)IRQ_LEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + *((volatile unsigned int *)IRQ_MASK(IO_ADDRESS(SL2312_INTERRUPT_BASE))) |= (unsigned int)(IRQ_SERIRQ0_MASK); + + /* + * Finally, enable interrupts. Use the TII interrupt to minimise + * the number of interrupts generated. If higher performance is + * needed, consider using the TI interrupt with a suitable FIFO + * threshold + */ + UART_PUT_IER(port, (UART_IER_RDI|UART_IER_THRI)); + + return 0; +} + +static void it8712_shutdown(struct uart_port *port, struct uart_info *info) +{ +// printk("it8712 shutdown : \n"); + + /* + * disable all interrupts, disable the port + */ + UART_PUT_IER(port, 0x0); + + /* disable break condition and fifos */ +// UART_PUT_MCR(port, (UART_GET_MCR(port)&UART_MCR_MASK)); + + /* + * Free the interrupt + */ + free_irq(port->irq, info); +} + +static const char *it8712_type(struct uart_port *port) +{ + return port->type == PORT_IT8712 ? "IT8712" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void it8712_release_port(struct uart_port *port) +{ +// printk("it8712 release port : \n"); + + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int it8712_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_it8712") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void it8712_config_port(struct uart_port *port, int flags) +{ + + if (flags & UART_CONFIG_TYPE) { + if (it8712_request_port(port) == 0) + port->type = PORT_IT8712; + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int it8712_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops it8712_pops = { + tx_empty: it8712_tx_empty, + set_mctrl: it8712_set_mctrl_null, + get_mctrl: it8712_get_mctrl, + stop_tx: it8712_stop_tx, + start_tx: it8712_start_tx, + stop_rx: it8712_stop_rx, + enable_ms: it8712_enable_ms, + break_ctl: it8712_break_ctl, + startup: it8712_startup, + shutdown: it8712_shutdown, + change_speed: it8712_change_speed, + type: it8712_type, + release_port: it8712_release_port, + request_port: it8712_request_port, + config_port: it8712_config_port, + verify_port: it8712_verify_port, +}; + +#ifdef CONFIG_ARCH_SL2312 + +static struct uart_port it8712_ports[UART_NR] = { + { + membase: (void *)0, + mapbase: 0, + iotype: SERIAL_IO_MEM, + irq: 0, + uartclk: UART_CLK/2, + fifosize: 16, + ops: &it8712_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#endif + +#ifdef CONFIG_SERIAL_IT8712_CONSOLE +#ifdef used_and_not_const_char_pointer +static int it8712_console_read(struct uart_port *port, char *s, u_int count) +{ + unsigned int status; + int c; +#ifdef DEBUG + printk("it8712_console_read() called\n"); +#endif + + c = 0; + while (c < count) { + status = UART_GET_LSR(port); + if (UART_RX_DATA(status)) { + *s++ = UART_GET_CHAR(port); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif +static void it8712_console_write(struct console *co, const char *s, unsigned count) +{ +#ifdef CONFIG_ARCH_SL2312 + struct uart_port *port = it8712_ports + co->index; + unsigned int status, old_ies; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_ies = UART_GET_IER(port); + UART_PUT_IER(port,0x0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IES + */ + do { + status = UART_GET_LSR(port); + } while (!(status&UART_LSR_THRE)); + UART_PUT_IER(port, old_ies); +#endif +} + +static kdev_t it8712_console_device(struct console *co) +{ + return MKDEV(SERIAL_IT8712_MAJOR, SERIAL_IT8712_MINOR + co->index); +} + +static int it8712_console_wait_key(struct console *co) +{ +#ifdef CONFIG_ARCH_SL2312 + struct uart_port *port = (it8712_ports + co->index); + unsigned int status; + + do { + status = UART_GET_LSR(port); + } while (!UART_RX_DATA(status)); + return UART_GET_CHAR(port); +#else + return 0; +#endif +} + +static void /*__init*/ it8712_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + printk("it8712 console get options : \n"); + + u_int uart_mc, quot; + uart_mc= UART_GET_MCR(port); + + *parity = 'n'; + if (uart_mc & UART_LCR_PARITY) { + if (uart_mc & UART_LCR_EVEN) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (uart_mc & UART_LCR_MSK){ + + case UART_LCR_WLEN5: + *bits = 5; + break; + case UART_LCR_WLEN6: + *bits = 6; + break; + case UART_LCR_WLEN7: + *bits = 7; + break; + case UART_LCR_WLEN8: + *bits = 8; + break; + } + UART_PUT_MCR(port,UART_LCR_DLAB); + quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); + UART_PUT_MCR(port,uart_mc); + *baud = (port->uartclk / (16 *quot)); +} + +static int __init it8712_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow= 'n'; + int base, irq; + int i ; + +// printk("it8712 console setup : \n"); + + LPCSetConfig(0, 0x02, 0x01); + LPCSetConfig(LDN_SERIAL1, 0x30, 0x1); + LPCSetConfig(LDN_SERIAL1, 0x23, 0x0); + base = IT8712_IO_BASE; + base += ((LPCGetConfig(LDN_SERIAL1, 0x60) << 8) + LPCGetConfig(LDN_SERIAL1, 0x61)); + it8712_ports[0].mapbase = base; + it8712_ports[0].membase = IO_ADDRESS(base); + it8712_ports[0].irq = IRQ_SERIRQ0_OFFSET; + irq = LPCGetConfig(LDN_SERIAL1, 0x70); + it8712_ports[0].irq += irq; + + printk("it8712 irq is %x %x \n", it8712_ports[0].irq, irq); + + // setup LPC Host 'quiet mode' + *((volatile unsigned int *)IO_ADDRESS((SL2312_LPC_HOST_BASE+0x04))) |= LPC_HOST_CONTINUE_MODE ; + for(i=0;i<1000;i++) ; // delay + *((volatile unsigned int *)IO_ADDRESS((SL2312_LPC_HOST_BASE+0x04))) &= ~(LPC_HOST_CONTINUE_MODE) ; + +#ifdef CONFIG_ARCH_SL2312 + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(it8712_ports,IT8712_NO_PORTS,co); +#else + return -ENODEV; +#endif + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + it8712_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console it8712_console = { + name: SERIAL_IT8712_NAME, + write: it8712_console_write, +#ifdef used_and_not_const_char_pointer + read: it8712_console_read, +#endif + device: it8712_console_device, +// wait_key: it8712_console_wait_key, + setup: it8712_console_setup, + flags: (CON_PRINTBUFFER|CON_ENABLED), + index: -1, +}; + +void __init it8712_console_init(void) +{ + register_console(&it8712_console); +} + +#define IT8712_CONSOLE &it8712_console +#else +#define IT8712_CONSOLE NULL +#endif + +static struct uart_driver it8712_reg = { + owner: NULL, + normal_major: SERIAL_IT8712_MAJOR, + normal_name: SERIAL_IT8712_NAME, + normal_driver: &normal, + callout_major: CALLOUT_IT8712_MAJOR, + callout_name: CALLOUT_IT8712_NAME, + callout_driver: &callout, + table: it8712_table, + termios: it8712_termios, + termios_locked: it8712_termios_locked, + minor: SERIAL_IT8712_MINOR, + nr: UART_NR, +#ifdef CONFIG_ARCH_SL2312 + port: it8712_ports, +#endif + state: NULL, + cons: IT8712_CONSOLE, +}; + +static int __init it8712_init(void) +{ +// printk("serial_it8712: it871212_init \n"); + + return uart_register_driver(&it8712_reg); +} + + +__initcall(it8712_init); --- /dev/null +++ b/drivers/serial/serial_sl2312.c @@ -0,0 +1,827 @@ +/* + * linux/drivers/char/serial_uart00.c + * + * Driver for UART00 serial ports + * + * Based on drivers/char/serial_amba.c, by ARM Limited & + * Deep Blue Solutions Ltd. + * Copyright 2001 Altera Corporation + * + * 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: serial_sl2312.c,v 1.1.1.1 2006/04/03 08:41:00 amos_lee Exp $ + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(CONFIG_SERIAL_SL2312_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#define UART_TYPE (volatile unsigned int*) +#include +#include + +// #define DEBUG 1 +#define UART_NR 1 + + +#define SERIAL_SL2312_NAME "ttyS" +#define SERIAL_SL2312_MAJOR 204 +#define SERIAL_SL2312_MINOR 40 /* Temporary - will change in future */ +#define SERIAL_SL2312_NR UART_NR +#define UART_PORT_SIZE 0x50 + +#define SL2312_NO_PORTS UART_NR +#define SL2312_ISR_PASS_LIMIT 256 + +/* + * Access macros for the SL2312 UARTs + */ +#define UART_GET_INT_STATUS(p) (inl(UART_IIR((p)->membase)) & 0x0F) // interrupt identification +#define UART_PUT_IER(p, c) outl(c,UART_IER((p)->membase)) // interrupt enable +#define UART_GET_IER(p) inl(UART_IER((p)->membase)) +#define UART_PUT_CHAR(p, c) outl(c,UART_THR((p)->membase)) // transmitter holding +#define UART_GET_CHAR(p) inl(UART_RBR((p)->membase)) // receive buffer +#define UART_GET_LSR(p) inl(UART_LSR((p)->membase)) // line status +#define UART_GET_MSR(p) inl(UART_MSR((p)->membase)) // modem status +#define UART_GET_MCR(p) inl(UART_MCR((p)->membase)) // modem control +#define UART_PUT_MCR(p, c) outl(c,UART_MCR((p)->membase)) +#define UART_GET_LCR(p) inl(UART_LCR((p)->membase)) // mode control +#define UART_PUT_LCR(p, c) outl(c,UART_LCR((p)->membase)) +#define UART_GET_DIV_HI(p) inl(UART_DIV_HI((p)->membase)) +#define UART_PUT_DIV_HI(p, c) outl(c,UART_DIV_HI((p)->membase)) +#define UART_GET_DIV_LO(p) inl(UART_DIV_LO((p)->membase)) +#define UART_PUT_DIV_LO(p, c) outl(c,UART_DIV_LO((p)->membase)) +#define UART_PUT_MDR(p, c) outl(c,UART_MDR((p)->membase)) +#define UART_RX_DATA(s) ((s) & UART_LSR_DR) +#define UART_TX_READY(s) ((s) & UART_LSR_THRE) + + +static void sl2312_stop_tx(struct uart_port *port) +{ + unsigned int reg; + +// printk("sl2312 stop tx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_TE); + UART_PUT_IER(port, reg); +} + +static void sl2312_stop_rx(struct uart_port *port) +{ + unsigned int reg; + +// printk("sl2312 stop rx : \n"); + reg = UART_GET_IER(port); + reg &= ~(UART_IER_DR); + UART_PUT_IER(port, reg); + +} + +static void sl2312_enable_ms(struct uart_port *port) +{ + unsigned int reg; + +// printk("sl2312 enable ms : \n"); + + reg = UART_GET_IER(port); + reg |= (UART_IER_MS); + UART_PUT_IER(port, reg); + +} + +static void +sl2312_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, mask, ch, flg, ignored = 0; + + + // printk("sl2312_rx_chars : \n"); + status = UART_GET_LSR(port); + while (UART_RX_DATA(status)) { + + /* + * We need to read rds before reading the + * character from the fifo + */ + ch = UART_GET_CHAR(port); + port->icount.rx++; + + //if (tty->flip.count >= TTY_FLIPBUF_SIZE) + if (tty && !tty_buffer_request_room(tty, 1)) + goto ignore_char; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + + if (status & (UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI|UART_LSR_DE)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + error_return: + //*tty->flip.flag_buf_ptr++ = flg; + //*tty->flip.char_buf_ptr++ = ch; + //tty->flip.count++; + tty_insert_flip_char(tty, ch, flg); + ignore_char: + status = UART_GET_LSR(port); + } // end of while +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (status & UART_LSR_BI) { + status &= ~(UART_LSR_FE); + port->icount.brk++; + +#ifdef SUPPORT_SYSRQ + if (uart_handle_break(port)) + goto ignore_char; +#endif + } else if (status & UART_LSR_PE) + port->icount.parity++; + else if (status & UART_LSR_FE) + port->icount.frame++; + + if (status & UART_LSR_OE) + port->icount.overrun++; + + if (status & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + mask = status & port->read_status_mask; + + if (mask & UART_LSR_BI) + flg = TTY_BREAK; + else if (mask & UART_LSR_PE) + flg = TTY_PARITY; + else if (mask & UART_LSR_FE) + flg = TTY_FRAME; + + if (status & UART_LSR_OE) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + //*tty->flip.flag_buf_ptr++ = flg; + //*tty->flip.char_buf_ptr++ = ch; + //tty->flip.count++; + + tty_insert_flip_char(tty, 0, TTY_BREAK); + + // if (tty->flip.count >= TTY_FLIPBUF_SIZE) + if (tty_buffer_request_room(tty, 1)) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void sl2312_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + + if (port->x_char) { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sl2312_stop_tx(port); + + return; + } + + count = port->fifosize >> 1; + do { + while(!(UART_GET_LSR(port)&UART_LSR_THRE)); + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sl2312_stop_tx(port); + +} + +static void sl2312_start_tx(struct uart_port *port) +{ + unsigned int reg; + +// printk("sl2312 start tx : \n"); + reg = UART_GET_IER(port); + reg |= (UART_IER_TE); + UART_PUT_IER(port, reg); + + sl2312_tx_chars(port); +} + +static void sl2312_modem_status(struct uart_port *port) +{ + unsigned int status; + +// printk("it8712 modem status : \n"); + + status = UART_GET_MSR(port); + + if (!(status & (UART_MSR_DCTS | UART_MSR_DDSR | + UART_MSR_TERI | UART_MSR_DDCD))) + return; + + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(port, status & UART_MSR_DCD); + + if (status & UART_MSR_DDSR) + port->icount.dsr++; + + if (status & UART_MSR_DCTS) + uart_handle_cts_change(port, status & UART_MSR_CTS); + + wake_up_interruptible(&port->info->delta_msr_wait); + +} + +static irqreturn_t sl2312_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = 0; + + status = UART_GET_INT_STATUS(port); + do { + switch(status) + { + case UART_IIR_DR: + case UART_IIR_RLS: + sl2312_rx_chars(port); + break; + case UART_IIR_TE: + sl2312_tx_chars(port); + break; + case UART_IIR_MODEM: + sl2312_modem_status(port); + break; + default: + break; + } + if (pass_counter++ > SL2312_ISR_PASS_LIMIT) + break; + + status = UART_GET_INT_STATUS(port); + } while (status); + + return IRQ_HANDLED; +} + +static u_int sl2312_tx_empty(struct uart_port *port) +{ +// printk("sl2312 tx empty : \n"); + + return ((UART_GET_LSR(port) & UART_LSR_TE)? TIOCSER_TEMT : 0); +} + +static u_int sl2312_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + +// printk("sl2312 get mctrl : \n"); + + status = UART_GET_MSR(port); + if (status & UART_MSR_DCD) + result |= TIOCM_CAR; + if (status & UART_MSR_DSR) + result |= TIOCM_DSR; + if (status & UART_MSR_CTS) + result |= TIOCM_CTS; + if (status & UART_MSR_RI) + result |= TIOCM_RI; + + return result; +} + +static void sl2312_set_mctrl_null(struct uart_port *port, u_int mctrl) +{ +} + +static void sl2312_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int lcr; + +// printk("sl2312 break ctl : \n"); + + lcr = UART_GET_LCR(port); + if (break_state == -1) + lcr |= UART_LCR_SETBREAK; + else + lcr &= ~UART_LCR_SETBREAK; + UART_PUT_LCR(port, lcr); +} + +static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + quot = (port->uartclk / (16 * baud)-1) ; + + return quot; +} + +static void sl2312_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int uart_mc, old_ier, baud, quot; + unsigned long flags; + + termios->c_cflag |= CREAD; +#ifdef DEBUG + printk("it8712_set_cflag(0x%x) called\n", cflag); +#endif + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = (port->uartclk / (16 * baud)) ; + //uart_get_divisor(port, baud); + + /* byte size and parity */ + switch (termios->c_cflag & CSIZE) { + case CS5: + uart_mc = UART_LCR_LEN5; + break; + case CS6: + uart_mc = UART_LCR_LEN6; + break; + case CS7: + uart_mc = UART_LCR_LEN7; + break; + default: // CS8 + uart_mc = UART_LCR_LEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + uart_mc|= UART_LCR_STOP; + if (termios->c_cflag & PARENB) { + uart_mc |= UART_LCR_EVEN; + if (!(termios->c_cflag & PARODD)) + uart_mc |= UART_LCR_ODD; + } + + spin_lock_irqsave(&port->lock, flags); + /* + * Update the per-port timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + port->read_status_mask = UART_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + //save_flags(flags); cli(); + old_ier = UART_GET_IER(port); + + if(UART_ENABLE_MS(port, termios->c_cflag)) + old_ier |= UART_IER_MS; + + /* Set baud rate */ + UART_PUT_LCR(port, UART_LCR_DLAB); + UART_PUT_DIV_LO(port, (quot & 0xff)); + UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); + + UART_PUT_LCR(port, uart_mc); + UART_PUT_IER(port, old_ier); + + //restore_flags(flags); + spin_unlock_irqrestore(&port->lock, flags); +} + + + +static int sl2312_startup(struct uart_port *port) +{ + int retval; + unsigned int regs; + +// printk("sl2312 startup : \n"); + + /* + * Use iobase to store a pointer to info. We need this to start a + * transmission as the tranmittr interrupt is only generated on + * the transition to the idle state + */ + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, sl2312_int, IRQF_DISABLED, "sl2312", port); + if (retval) + return retval; + + /* setup interrupt controller */ + regs = *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs &= ~(IRQ_UART_MASK); + *((volatile unsigned int *)IRQ_TMODE(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + regs = *((volatile unsigned int *)IRQ_TLEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))); + regs &= ~(IRQ_UART_MASK); + *((volatile unsigned int *)IRQ_TLEVEL(IO_ADDRESS(SL2312_INTERRUPT_BASE))) = regs; + *((volatile unsigned int *)IRQ_MASK(IO_ADDRESS(SL2312_INTERRUPT_BASE))) |= (unsigned int)(IRQ_UART_MASK); + + /* + * Finally, enable interrupts. Use the TII interrupt to minimise + * the number of interrupts generated. If higher performance is + * needed, consider using the TI interrupt with a suitable FIFO + * threshold + */ + UART_PUT_IER(port, (UART_IER_DR|UART_IER_TE)); + + return 0; +} + +static void sl2312_shutdown(struct uart_port *port) +{ +// printk("sl2312 shutdown : \n"); + + /* + * disable all interrupts, disable the port + */ + UART_PUT_IER(port, 0x0); + + /* disable break condition and fifos */ +// UART_PUT_MCR(port, (UART_GET_MCR(port)&UART_MCR_MASK)); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); +} + +static const char *sl2312_type(struct uart_port *port) +{ + return port->type == PORT_SL2312 ? "SL2312" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void sl2312_release_port(struct uart_port *port) +{ +// printk("sl2312 release port : \n"); + + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int sl2312_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_sl2312") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void sl2312_config_port(struct uart_port *port, int flags) +{ + + if (flags & UART_CONFIG_TYPE) { + if (sl2312_request_port(port) == 0) + port->type = PORT_SL2312; + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int sl2312_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops sl2312_pops = { + .tx_empty =sl2312_tx_empty, + .set_mctrl =sl2312_set_mctrl_null, + .get_mctrl =sl2312_get_mctrl, + .stop_tx =sl2312_stop_tx, + .start_tx =sl2312_start_tx, + .stop_rx =sl2312_stop_rx, + .enable_ms =sl2312_enable_ms, + .break_ctl =sl2312_break_ctl, + .startup =sl2312_startup, + .shutdown =sl2312_shutdown, + .set_termios =sl2312_set_termios, + .type =sl2312_type, + .release_port =sl2312_release_port, + .request_port =sl2312_request_port, + .config_port =sl2312_config_port, + .verify_port =sl2312_verify_port, +}; + +#ifdef CONFIG_ARCH_SL2312 + +static struct uart_port sl2312_ports[UART_NR] = { + { + membase: (void *)IO_ADDRESS(SL2312_UART_BASE), + mapbase: SL2312_UART_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UART, + uartclk: UART_CLK, + fifosize: 16, + ops: &sl2312_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#endif + +#ifdef CONFIG_SERIAL_SL2312_CONSOLE +#ifdef used_and_not_const_char_pointer +static int sl2312_console_read(struct uart_port *port, char *s, u_int count) +{ + unsigned int status; + int c; +#ifdef DEBUG + printk("sl2312_console_read() called\n"); +#endif + + c = 0; + while (c < count) { + status = UART_GET_LSR(port); + if (UART_RX_DATA(status)) { + *s++ = UART_GET_CHAR(port); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif +static void sl2312_console_write(struct console *co, const char *s, unsigned count) +{ +#ifdef CONFIG_ARCH_SL2312 + struct uart_port *port = sl2312_ports + co->index; + unsigned int status, old_ies; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_ies = UART_GET_IER(port); + UART_PUT_IER(port,0x0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_LSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IES + */ + do { + status = UART_GET_LSR(port); + } while (!(status&UART_LSR_TE)); + UART_PUT_IER(port, old_ies); +#endif +} + +#if 0 +static void sl2312_console_device(struct console *co,int *index) +{ + + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; + +} +#endif + +static void /*__init*/ sl2312_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ +// printk("sl2312 console get options : \n"); + + u_int uart_mc, quot; + uart_mc= UART_GET_MCR(port); + + *parity = 'n'; + if (uart_mc & UART_LCR_PE) { + if (uart_mc & UART_LCR_EVEN) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (uart_mc & UART_LCR_MSK){ + + case UART_LCR_LEN5: + *bits = 5; + break; + case UART_LCR_LEN6: + *bits = 6; + break; + case UART_LCR_LEN7: + *bits = 7; + break; + case UART_LCR_LEN8: + *bits = 8; + break; + } + UART_PUT_MCR(port,UART_LCR_DLAB); + quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); + UART_PUT_MCR(port,uart_mc); + *baud = port->uartclk / (16 *quot ); +} + +static int __init sl2312_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 19200; + int bits = 8; + int parity = 'n'; + int flow= 'n'; + + printk("sl2312 console setup : \n"); + +#ifdef CONFIG_ARCH_SL2312 + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(sl2312_ports,SL2312_NO_PORTS,co); +#else + return -ENODEV; +#endif + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + sl2312_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +extern struct uart_driver sl2312_reg; +static struct console sl2312_console = { + .name = SERIAL_SL2312_NAME, + .write = sl2312_console_write, + .device = uart_console_device, +// .device = sl2312_console_device, + .setup = sl2312_console_setup, +// .flags = (CON_PRINTBUFFER|CON_ENABLED), + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sl2312_reg, +}; + +static int __init sl2312_console_init(void) +{ + register_console(&sl2312_console); + return 0; + +} + +console_initcall(sl2312_console_init); + +#define SL2312_CONSOLE &sl2312_console +#else +#define SL2312_CONSOLE NULL +#endif + +// static +struct uart_driver sl2312_reg = { + .owner = NULL, + .driver_name = SERIAL_SL2312_NAME, + .dev_name = SERIAL_SL2312_NAME, + .major = SERIAL_SL2312_MAJOR, + .minor = SERIAL_SL2312_MINOR, + .nr = UART_NR, + .cons = SL2312_CONSOLE, +}; + +static int __init sl2312_init(void) +{ + int result; + //printk("serial_it8712: it871212_init \n"); + + result = uart_register_driver(&sl2312_reg); + if(result) + return result; + result = uart_add_one_port(&sl2312_reg, &sl2312_ports[0]); + + return result; +} + + +__initcall(sl2312_init); --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -147,6 +147,10 @@ #define PORT_SB1250_DUART 77 +/* Storlink Soc */ +#define PORT_SL2312 72 +#define PORT_IT8712 73 + #ifdef __KERNEL__ #include --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -70,6 +70,16 @@ obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o + +### for Storlink SoC ### +obj-$(CONFIG_SL2312_RTC) += sl2312_rtc.o +obj-$(CONFIG_IT8712_GPIO) += it8712_gpio.o +obj-$(CONFIG_GEMINI_GPIO) += gemini_gpio.o +obj-$(CONFIG_GEMINI_PWC) += gemini_pwr.o +obj-$(CONFIG_GEMINI_CIR) += gemini_cir.o +obj-$(CONFIG_GEMINI_I2S) += gemini_i2s.o +obj-$(CONFIG_SL2312_WATCHDOG) += sl2312_wd.o + obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -280,6 +280,56 @@ comment "Non-8250 serial port support" +config SERIAL_SL2312 + bool "SL2312 serial port (sl2312) support" + depends on ARCH_SL2312 + select SERIAL_CORE + select SERIAL_SL2312_CONSOLE + help + Say Y here if you want to use the hard logic uart on SWORD. This + driver also supports soft logic implentations of this uart core. + +config SERIAL_SL2312_CONSOLE + bool "Support for console on SL2312 serial port" + depends on SERIAL_SL2312 + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on an SWORD + hard logic uart or uart00 IP core. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + + +config SERIAL_IT8712 + bool "Sl2312 serial port(IT8712) support" + depends on ARM && ARCH_SL2312 && SL2312_LPC + select SERIAL_CORE + select SERIAL_IT8712_CONSOLE + help + Say Y here if you want to use the hard logic uart on Excalibur. This + driver also supports soft logic implentations of this uart core. + +config SERIAL_IT8712_CONSOLE + bool "Support for console on Sword serial port(IT8712)" + depends on SERIAL_IT8712 + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on an Excalibur + hard logic uart or uart00 IP core. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySI0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + + config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE) --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -62,5 +62,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o +obj-$(CONFIG_SERIAL_IT8712) += it8712.o +obj-$(CONFIG_SERIAL_SL2312) += serial_sl2312.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o