mirror of https://github.com/hak5/openwrt.git
2810 lines
74 KiB
Diff
2810 lines
74 KiB
Diff
--- /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 <linux/module.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/serial.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/sysrq.h>
|
|
+#include <asm/hardware.h>
|
|
+#include <asm/system.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/irq.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include <asm/bitops.h>
|
|
+#include <asm/sizes.h>
|
|
+
|
|
+#if defined(CONFIG_SERIAL_IT8712_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
|
+#define SUPPORT_SYSRQ
|
|
+#endif
|
|
+
|
|
+#include <linux/serial_core.h>
|
|
+#include <asm/arch/sl2312.h>
|
|
+#include <asm/arch/int_ctrl.h>
|
|
+#include <asm/arch/it8712.h>
|
|
+#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 <linux/module.h>
|
|
+
|
|
+#include <linux/errno.h>
|
|
+#include <linux/signal.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/tty_flip.h>
|
|
+#include <linux/major.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/fcntl.h>
|
|
+#include <linux/ptrace.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/circ_buf.h>
|
|
+#include <linux/serial.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/sysrq.h>
|
|
+
|
|
+#include <asm/system.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/irq.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include <asm/bitops.h>
|
|
+#include <asm/sizes.h>
|
|
+
|
|
+#if defined(CONFIG_SERIAL_IT8712_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
|
+#define SUPPORT_SYSRQ
|
|
+#endif
|
|
+
|
|
+#include <linux/serial_core.h>
|
|
+#include <asm/arch/sl2312.h>
|
|
+#include <asm/arch/int_ctrl.h>
|
|
+#include <asm/arch/it8712.h>
|
|
+#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 <linux/module.h>
|
|
+
|
|
+#include <linux/errno.h>
|
|
+#include <linux/signal.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/tty_flip.h>
|
|
+#include <linux/major.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/fcntl.h>
|
|
+#include <linux/ptrace.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/circ_buf.h>
|
|
+#include <linux/serial.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/sysrq.h>
|
|
+#include <linux/serial_core.h>
|
|
+
|
|
+#include <asm/system.h>
|
|
+#include <asm/hardware.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/irq.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include <asm/bitops.h>
|
|
+#include <asm/sizes.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/irq.h>
|
|
+
|
|
+
|
|
+#if defined(CONFIG_SERIAL_SL2312_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
|
+#define SUPPORT_SYSRQ
|
|
+#endif
|
|
+
|
|
+#include <asm/arch/sl2312.h>
|
|
+#define UART_TYPE (volatile unsigned int*)
|
|
+#include <asm/arch/uart.h>
|
|
+#include <asm/arch/int_ctrl.h>
|
|
+
|
|
+// #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 <linux/compiler.h>
|
|
--- 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
|