aboutsummaryrefslogtreecommitdiff
path: root/arch/ppc/4xx_io
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/4xx_io
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/ppc/4xx_io')
-rw-r--r--arch/ppc/4xx_io/Makefile6
-rw-r--r--arch/ppc/4xx_io/serial_sicc.c2012
2 files changed, 2018 insertions, 0 deletions
diff --git a/arch/ppc/4xx_io/Makefile b/arch/ppc/4xx_io/Makefile
new file mode 100644
index 00000000000..6a8cd575f38
--- /dev/null
+++ b/arch/ppc/4xx_io/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the linux MPC4xx ppc-specific parts
+#
+
+
+obj-$(CONFIG_SERIAL_SICC) += serial_sicc.o
diff --git a/arch/ppc/4xx_io/serial_sicc.c b/arch/ppc/4xx_io/serial_sicc.c
new file mode 100644
index 00000000000..e95c48d5757
--- /dev/null
+++ b/arch/ppc/4xx_io/serial_sicc.c
@@ -0,0 +1,2012 @@
+/*
+ * arch/ppc/4xx_io/serial_sicc.c
+ *
+ * Driver for IBM STB3xxx SICC serial port
+ *
+ * Based on drivers/char/serial_amba.c, by ARM Ltd.
+ *
+ * Copyright 2001 IBM Crop.
+ * Author: IBM China Research Lab
+ * Yudong Yang <yangyud@cn.ibm.com>
+ * Yi Ge <geyi@cn.ibm.com>
+ *
+ * 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
+ *
+ *
+ * This is a driver for SICC serial port on IBM Redwood 4 evaluation board.
+ * The driver support both as a console device and normal serial device and
+ * is compatible with normal ttyS* devices.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.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/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/serial.h>
+
+
+#include <linux/serialP.h>
+
+
+/* -----------------------------------------------------------------------------
+ * From STB03xxx SICC UART Specification
+ * -----------------------------------------------------------------------------
+ * UART Register Offsets.
+ */
+
+#define BL_SICC_LSR 0x0000000 /* line status register read/clear */
+#define BL_SICC_LSRS 0x0000001 /* set line status register read/set */
+#define BL_SICC_HSR 0x0000002 /* handshake status register r/clear */
+#define BL_SICC_HSRS 0x0000003 /* set handshake status register r/set */
+#define BL_SICC_BRDH 0x0000004 /* baudrate divisor high reg r/w */
+#define BL_SICC_BRDL 0x0000005 /* baudrate divisor low reg r/w */
+#define BL_SICC_LCR 0x0000006 /* control register r/w */
+#define BL_SICC_RCR 0x0000007 /* receiver command register r/w */
+#define BL_SICC_TxCR 0x0000008 /* transmitter command register r/w */
+#define BL_SICC_RBR 0x0000009 /* receive buffer r */
+#define BL_SICC_TBR 0x0000009 /* transmit buffer w */
+#define BL_SICC_CTL2 0x000000A /* added for Vesta */
+#define BL_SICC_IrCR 0x000000B /* added for Vesta IR */
+
+/* masks and definitions for serial port control register */
+
+#define _LCR_LM_MASK 0xc0 /* loop back modes */
+#define _LCR_DTR_MASK 0x20 /* data terminal ready 0-inactive */
+#define _LCR_RTS_MASK 0x10 /* request to send 0-inactive */
+#define _LCR_DB_MASK 0x08 /* data bits mask */
+#define _LCR_PE_MASK 0x04 /* parity enable */
+#define _LCR_PTY_MASK 0x02 /* parity */
+#define _LCR_SB_MASK 0x01 /* stop bit mask */
+
+#define _LCR_LM_NORM 0x00 /* normal operation */
+#define _LCR_LM_LOOP 0x40 /* internal loopback mode */
+#define _LCR_LM_ECHO 0x80 /* automatic echo mode */
+#define _LCR_LM_RES 0xc0 /* reserved */
+
+#define _LCR_DTR_ACTIVE _LCR_DTR_MASK /* DTR is active */
+#define _LCR_RTS_ACTIVE _LCR_RTS_MASK /* RTS is active */
+#define _LCR_DB_8_BITS _LCR_DB_MASK /* 8 data bits */
+#define _LCR_DB_7_BITS 0x00 /* 7 data bits */
+#define _LCR_PE_ENABLE _LCR_PE_MASK /* parity enabled */
+#define _LCR_PE_DISABLE 0x00 /* parity disabled */
+#define _LCR_PTY_EVEN 0x00 /* even parity */
+#define _LCR_PTY_ODD _LCR_PTY_MASK /* odd parity */
+#define _LCR_SB_1_BIT 0x00 /* one stop bit */
+#define _LCR_SB_2_BIT _LCR_SB_MASK /* two stop bit */
+
+/* serial port handshake register */
+
+#define _HSR_DIS_MASK 0x80 /* DSR input inactive error mask */
+#define _HSR_CS_MASK 0x40 /* CTS input inactive error mask */
+#define _HSR_DIS_ACT 0x00 /* dsr input is active */
+#define _HSR_DIS_INACT _HSR_DIS_MASK /* dsr input is inactive */
+#define _HSR_CS_ACT 0x00 /* cts input is active */
+#define _HSR_CS_INACT _HSR_CS_MASK /* cts input is active */
+
+/* serial port line status register */
+
+#define _LSR_RBR_MASK 0x80 /* receive buffer ready mask */
+#define _LSR_FE_MASK 0x40 /* framing error */
+#define _LSR_OE_MASK 0x20 /* overrun error */
+#define _LSR_PE_MASK 0x10 /* parity error */
+#define _LSR_LB_MASK 0x08 /* line break */
+#define _LSR_TBR_MASK 0x04 /* transmit buffer ready */
+#define _LSR_TSR_MASK 0x02 /* transmit shift register ready */
+
+#define _LSR_RBR_FULL _LSR_RBR_MASK /* receive buffer is full */
+#define _LSR_FE_ERROR _LSR_FE_MASK /* framing error detected */
+#define _LSR_OE_ERROR _LSR_OE_MASK /* overrun error detected */
+#define _LSR_PE_ERROR _LSR_PE_MASK /* parity error detected */
+#define _LSR_LB_BREAK _LSR_LB_MASK /* line break detected */
+#define _LSR_TBR_EMPTY _LSR_TBR_MASK /* transmit buffer is ready */
+#define _LSR_TSR_EMPTY _LSR_TSR_MASK /* transmit shift register is empty */
+#define _LSR_TX_ALL 0x06 /* all physical transmit is done */
+
+#define _LSR_RX_ERR (_LSR_LB_BREAK | _LSR_FE_MASK | _LSR_OE_MASK | \
+ _LSR_PE_MASK )
+
+/* serial port receiver command register */
+
+#define _RCR_ER_MASK 0x80 /* enable receiver mask */
+#define _RCR_DME_MASK 0x60 /* dma mode */
+#define _RCR_EIE_MASK 0x10 /* error interrupt enable mask */
+#define _RCR_PME_MASK 0x08 /* pause mode mask */
+
+#define _RCR_ER_ENABLE _RCR_ER_MASK /* receiver enabled */
+#define _RCR_DME_DISABLE 0x00 /* dma disabled */
+#define _RCR_DME_RXRDY 0x20 /* dma disabled, RxRDY interrupt enabled*/
+#define _RCR_DME_ENABLE2 0x40 /* dma enabled,receiver src channel 2 */
+#define _RCR_DME_ENABLE3 0x60 /* dma enabled,receiver src channel 3 */
+#define _RCR_PME_HARD _RCR_PME_MASK /* RTS controlled by hardware */
+#define _RCR_PME_SOFT 0x00 /* RTS controlled by software */
+
+/* serial port transmit command register */
+
+#define _TxCR_ET_MASK 0x80 /* transmiter enable mask */
+#define _TxCR_DME_MASK 0x60 /* dma mode mask */
+#define _TxCR_TIE_MASK 0x10 /* empty interrupt enable mask */
+#define _TxCR_EIE_MASK 0x08 /* error interrupt enable mask */
+#define _TxCR_SPE_MASK 0x04 /* stop/pause mask */
+#define _TxCR_TB_MASK 0x02 /* transmit break mask */
+
+#define _TxCR_ET_ENABLE _TxCR_ET_MASK /* transmiter enabled */
+#define _TxCR_DME_DISABLE 0x00 /* transmiter disabled, TBR intr disabled */
+#define _TxCR_DME_TBR 0x20 /* transmiter disabled, TBR intr enabled */
+#define _TxCR_DME_CHAN_2 0x40 /* dma enabled, destination chann 2 */
+#define _TxCR_DME_CHAN_3 0x60 /* dma enabled, destination chann 3 */
+
+/* serial ctl reg 2 - added for Vesta */
+
+#define _CTL2_EXTERN 0x80 /* */
+#define _CTL2_USEFIFO 0x40 /* */
+#define _CTL2_RESETRF 0x08 /* */
+#define _CTL2_RESETTF 0x04 /* */
+
+
+
+#define SERIAL_SICC_NAME "ttySICC"
+#define SERIAL_SICC_MAJOR 150
+#define SERIAL_SICC_MINOR 1
+#define SERIAL_SICC_NR 1
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * Things needed by tty driver
+ */
+static struct tty_driver *siccnormal_driver;
+
+#if defined(CONFIG_SERIAL_SICC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+/*
+ * Things needed internally to this driver
+ */
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static u_char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+#define SICC_ISR_PASS_LIMIT 256
+
+#define EVT_WRITE_WAKEUP 0
+
+struct SICC_icount {
+ __u32 cts;
+ __u32 dsr;
+ __u32 rng;
+ __u32 dcd;
+ __u32 rx;
+ __u32 tx;
+ __u32 frame;
+ __u32 overrun;
+ __u32 parity;
+ __u32 brk;
+ __u32 buf_overrun;
+};
+
+/*
+ * Static information about the port
+ */
+struct SICC_port {
+ unsigned int uart_base;
+ unsigned int uart_base_phys;
+ unsigned int irqrx;
+ unsigned int irqtx;
+ unsigned int uartclk;
+ unsigned int fifosize;
+ unsigned int tiocm_support;
+ void (*set_mctrl)(struct SICC_port *, u_int mctrl);
+};
+
+/*
+ * This is the state information which is persistent across opens
+ */
+struct SICC_state {
+ struct SICC_icount icount;
+ unsigned int line;
+ unsigned int close_delay;
+ unsigned int closing_wait;
+ unsigned int custom_divisor;
+ unsigned int flags;
+ int count;
+ struct SICC_info *info;
+ spinlock_t sicc_lock;
+};
+
+#define SICC_XMIT_SIZE 1024
+/*
+ * This is the state information which is only valid when the port is open.
+ */
+struct SICC_info {
+ struct SICC_port *port;
+ struct SICC_state *state;
+ struct tty_struct *tty;
+ unsigned char x_char;
+ unsigned char old_status;
+ unsigned char read_status_mask;
+ unsigned char ignore_status_mask;
+ struct circ_buf xmit;
+ unsigned int flags;
+#ifdef SUPPORT_SYSRQ
+ unsigned long sysrq;
+#endif
+
+ unsigned int event;
+ unsigned int timeout;
+ unsigned int lcr_h;
+ unsigned int mctrl;
+ int blocked_open;
+
+ struct tasklet_struct tlet;
+
+ wait_queue_head_t open_wait;
+ wait_queue_head_t close_wait;
+ wait_queue_head_t delta_msr_wait;
+};
+
+#ifdef CONFIG_SERIAL_SICC_CONSOLE
+static struct console siccuart_cons;
+#endif
+static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios);
+static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout);
+
+
+
+static void powerpcMtcic_cr(unsigned long value)
+{
+ mtdcr(DCRN_CICCR, value);
+}
+
+static unsigned long powerpcMfcic_cr(void)
+{
+ return mfdcr(DCRN_CICCR);
+}
+
+static unsigned long powerpcMfclkgpcr(void)
+{
+ return mfdcr(DCRN_SCCR);
+}
+
+static void sicc_set_mctrl_null(struct SICC_port *port, u_int mctrl)
+{
+}
+
+static struct SICC_port sicc_ports[SERIAL_SICC_NR] = {
+ {
+ .uart_base = 0,
+ .uart_base_phys = SICC0_IO_BASE,
+ .irqrx = SICC0_INTRX,
+ .irqtx = SICC0_INTTX,
+// .uartclk = 0,
+ .fifosize = 1,
+ .set_mctrl = sicc_set_mctrl_null,
+ }
+};
+
+static struct SICC_state sicc_state[SERIAL_SICC_NR];
+
+static void siccuart_enable_rx_interrupt(struct SICC_info *info)
+{
+ unsigned char cr;
+
+ cr = readb(info->port->uart_base+BL_SICC_RCR);
+ cr &= ~_RCR_DME_MASK;
+ cr |= _RCR_DME_RXRDY;
+ writeb(cr, info->port->uart_base+BL_SICC_RCR);
+}
+
+static void siccuart_disable_rx_interrupt(struct SICC_info *info)
+{
+ unsigned char cr;
+
+ cr = readb(info->port->uart_base+BL_SICC_RCR);
+ cr &= ~_RCR_DME_MASK;
+ cr |= _RCR_DME_DISABLE;
+ writeb(cr, info->port->uart_base+BL_SICC_RCR);
+}
+
+
+static void siccuart_enable_tx_interrupt(struct SICC_info *info)
+{
+ unsigned char cr;
+
+ cr = readb(info->port->uart_base+BL_SICC_TxCR);
+ cr &= ~_TxCR_DME_MASK;
+ cr |= _TxCR_DME_TBR;
+ writeb(cr, info->port->uart_base+BL_SICC_TxCR);
+}
+
+static void siccuart_disable_tx_interrupt(struct SICC_info *info)
+{
+ unsigned char cr;
+
+ cr = readb(info->port->uart_base+BL_SICC_TxCR);
+ cr &= ~_TxCR_DME_MASK;
+ cr |= _TxCR_DME_DISABLE;
+ writeb(cr, info->port->uart_base+BL_SICC_TxCR);
+}
+
+
+static void siccuart_stop(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ /* disable interrupts while stopping serial port interrupts */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ siccuart_disable_tx_interrupt(info);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+}
+
+static void siccuart_start(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ /* disable interrupts while starting serial port interrupts */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ if (info->xmit.head != info->xmit.tail
+ && info->xmit.buf)
+ siccuart_enable_tx_interrupt(info);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+}
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static void siccuart_event(struct SICC_info *info, int event)
+{
+ info->event |= 1 << event;
+ tasklet_schedule(&info->tlet);
+}
+
+static void
+siccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned int status, ch, rsr, flg, ignored = 0;
+ struct SICC_icount *icount = &info->state->icount;
+ struct SICC_port *port = info->port;
+
+ status = readb(port->uart_base+BL_SICC_LSR );
+ while (status & _LSR_RBR_FULL) {
+ ch = readb(port->uart_base+BL_SICC_RBR);
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ goto ignore_char;
+ icount->rx++;
+
+ flg = TTY_NORMAL;
+
+ /*
+ * Note that the error handling code is
+ * out of the main execution path
+ */
+ rsr = readb(port->uart_base+BL_SICC_LSR);
+ if (rsr & _LSR_RX_ERR)
+ goto handle_error;
+#ifdef SUPPORT_SYSRQ
+ if (info->sysrq) {
+ if (ch && time_before(jiffies, info->sysrq)) {
+ handle_sysrq(ch, regs, NULL);
+ info->sysrq = 0;
+ goto ignore_char;
+ }
+ info->sysrq = 0;
+ }
+#endif
+ error_return:
+ *tty->flip.flag_buf_ptr++ = flg;
+ *tty->flip.char_buf_ptr++ = ch;
+ tty->flip.count++;
+ ignore_char:
+ status = readb(port->uart_base+BL_SICC_LSR );
+ }
+out:
+ tty_flip_buffer_push(tty);
+ return;
+
+handle_error:
+ if (rsr & _LSR_LB_BREAK) {
+ rsr &= ~(_LSR_FE_MASK | _LSR_PE_MASK);
+ icount->brk++;
+
+#ifdef SUPPORT_SYSRQ
+ if (info->state->line == siccuart_cons.index) {
+ if (!info->sysrq) {
+ info->sysrq = jiffies + HZ*5;
+ goto ignore_char;
+ }
+ }
+#endif
+ } else if (rsr & _LSR_PE_MASK)
+ icount->parity++;
+ else if (rsr & _LSR_FE_MASK)
+ icount->frame++;
+ if (rsr & _LSR_OE_MASK)
+ icount->overrun++;
+
+ if (rsr & info->ignore_status_mask) {
+ if (++ignored > 100)
+ goto out;
+ goto ignore_char;
+ }
+ rsr &= info->read_status_mask;
+
+ if (rsr & _LSR_LB_BREAK)
+ flg = TTY_BREAK;
+ else if (rsr & _LSR_PE_MASK)
+ flg = TTY_PARITY;
+ else if (rsr & _LSR_FE_MASK)
+ flg = TTY_FRAME;
+
+ if (rsr & _LSR_OE_MASK) {
+ /*
+ * 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 siccuart_tx_chars(struct SICC_info *info)
+{
+ struct SICC_port *port = info->port;
+ int count;
+ unsigned char status;
+
+
+ if (info->x_char) {
+ writeb(info->x_char, port->uart_base+ BL_SICC_TBR);
+ info->state->icount.tx++;
+ info->x_char = 0;
+ return;
+ }
+ if (info->xmit.head == info->xmit.tail
+ || info->tty->stopped
+ || info->tty->hw_stopped) {
+ siccuart_disable_tx_interrupt(info);
+ writeb(status&(~_LSR_RBR_MASK),port->uart_base+BL_SICC_LSR);
+ return;
+ }
+
+ count = port->fifosize;
+ do {
+ writeb(info->xmit.buf[info->xmit.tail], port->uart_base+ BL_SICC_TBR);
+ info->xmit.tail = (info->xmit.tail + 1) & (SICC_XMIT_SIZE - 1);
+ info->state->icount.tx++;
+ if (info->xmit.head == info->xmit.tail)
+ break;
+ } while (--count > 0);
+
+ if (CIRC_CNT(info->xmit.head,
+ info->xmit.tail,
+ SICC_XMIT_SIZE) < WAKEUP_CHARS)
+ siccuart_event(info, EVT_WRITE_WAKEUP);
+
+ if (info->xmit.head == info->xmit.tail) {
+ siccuart_disable_tx_interrupt(info);
+ }
+}
+
+
+static irqreturn_t siccuart_int_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct SICC_info *info = dev_id;
+ siccuart_rx_chars(info, regs);
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t siccuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct SICC_info *info = dev_id;
+ siccuart_tx_chars(info);
+ return IRQ_HANDLED;
+}
+
+static void siccuart_tasklet_action(unsigned long data)
+{
+ struct SICC_info *info = (struct SICC_info *)data;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
+ return;
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+}
+
+static int siccuart_startup(struct SICC_info *info)
+{
+ unsigned long flags;
+ unsigned long page;
+ int retval = 0;
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ return 0;
+ }
+
+ page = get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (info->port->uart_base == 0)
+ info->port->uart_base = (int)ioremap(info->port->uart_base_phys, PAGE_SIZE);
+ if (info->port->uart_base == 0) {
+ free_page(page);
+ return -ENOMEM;
+ }
+
+ /* lock access to info while doing setup */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+
+ if (info->xmit.buf)
+ free_page(page);
+ else
+ info->xmit.buf = (unsigned char *) page;
+
+
+ info->mctrl = 0;
+ if (info->tty->termios->c_cflag & CBAUD)
+ info->mctrl = TIOCM_RTS | TIOCM_DTR;
+ info->port->set_mctrl(info->port, info->mctrl);
+
+ /*
+ * initialise the old status of the modem signals
+ */
+ info->old_status = 0; // UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;
+
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit.head = info->xmit.tail = 0;
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if (info->tty) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
+ }
+
+
+ writeb( 0x00, info->port->uart_base + BL_SICC_IrCR ); // disable IrDA
+
+
+ /*
+ * and set the speed of the serial port
+ */
+ siccuart_change_speed(info, 0);
+
+ // enable rx/tx ports
+ writeb(_RCR_ER_ENABLE /*| _RCR_PME_HARD*/, info->port->uart_base + BL_SICC_RCR);
+ writeb(_TxCR_ET_ENABLE , info->port->uart_base + BL_SICC_TxCR);
+
+ readb(info->port->uart_base + BL_SICC_RBR); // clear rx port
+
+ writeb(0xf8, info->port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */
+
+ /*
+ * Finally, enable interrupts
+ */
+
+ /*
+ * Allocate the IRQ
+ */
+ retval = request_irq(info->port->irqrx, siccuart_int_rx, 0, "SICC rx", info);
+ if (retval) {
+ if (capable(CAP_SYS_ADMIN)) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ retval = 0;
+ }
+ goto errout;
+ }
+ retval = request_irq(info->port->irqtx, siccuart_int_tx, 0, "SICC tx", info);
+ if (retval) {
+ if (capable(CAP_SYS_ADMIN)) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ retval = 0;
+ }
+ free_irq(info->port->irqrx, info);
+ goto errout;
+ }
+
+ siccuart_enable_rx_interrupt(info);
+
+ info->flags |= ASYNC_INITIALIZED;
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ return 0;
+
+
+errout:
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void siccuart_shutdown(struct SICC_info *info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+ /* lock while shutting down port */
+ spin_lock_irqsave(&info->state->sicc_lock,flags); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be woken up
+ */
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ /*
+ * disable all interrupts, disable the port
+ */
+ siccuart_disable_rx_interrupt(info);
+ siccuart_disable_tx_interrupt(info);
+
+ /*
+ * Free the IRQ
+ */
+ free_irq(info->port->irqtx, info);
+ free_irq(info->port->irqrx, info);
+
+ if (info->xmit.buf) {
+ unsigned long pg = (unsigned long) info->xmit.buf;
+ info->xmit.buf = NULL;
+ free_page(pg);
+ }
+
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+ info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS);
+ info->port->set_mctrl(info->port, info->mctrl);
+
+ /* kill off our tasklet */
+ tasklet_kill(&info->tlet);
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+}
+
+
+static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios)
+{
+ unsigned int lcr_h, baud, quot, cflag, old_rcr, old_tcr, bits;
+ unsigned long flags;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+
+ cflag = info->tty->termios->c_cflag;
+
+ pr_debug("siccuart_set_cflag(0x%x) called\n", cflag);
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS7: lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; bits = 9; break;
+ default: lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; bits = 10; break; // CS8
+ }
+ if (cflag & CSTOPB) {
+ lcr_h |= _LCR_SB_2_BIT;
+ bits ++;
+ }
+ if (cflag & PARENB) {
+ lcr_h |= _LCR_PE_ENABLE;
+ bits++;
+ if (!(cflag & PARODD))
+ lcr_h |= _LCR_PTY_ODD;
+ else
+ lcr_h |= _LCR_PTY_EVEN;
+ }
+
+ do {
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+
+
+ {
+ // here is ppc403SetBaud(com_port, baud);
+ unsigned long divisor, clockSource, temp;
+
+ /* Ensure CICCR[7] is 0 to select Internal Baud Clock */
+ powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF));
+
+ /* Determine Internal Baud Clock Frequency */
+ /* powerpcMfclkgpcr() reads DCR 0x120 - the*/
+ /* SCCR (Serial Clock Control Register) on Vesta */
+ temp = powerpcMfclkgpcr();
+
+ if(temp & 0x00000080) {
+ clockSource = 324000000;
+ }
+ else {
+ clockSource = 216000000;
+ }
+ clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18);
+ divisor = clockSource/(16*baud) - 1;
+ /* divisor has only 12 bits of resolution */
+ if(divisor>0x00000FFF){
+ divisor=0x00000FFF;
+ }
+
+ quot = divisor;
+ }
+
+ if (baud == 38400 &&
+ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+ quot = info->state->custom_divisor;
+
+ if (!quot && old_termios) {
+ info->tty->termios->c_cflag &= ~CBAUD;
+ info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+ old_termios = NULL;
+ }
+ } while (quot == 0 && old_termios);
+
+ /* As a last resort, if the quotient is zero, default to 9600 bps */
+ if (!quot)
+ quot = (info->port->uartclk / (16 * 9600)) - 1;
+
+ info->timeout = info->port->fifosize * HZ * bits / baud;
+ info->timeout += HZ/50; /* Add .02 seconds of slop */
+
+ if (cflag & CRTSCTS)
+ info->flags |= ASYNC_CTS_FLOW;
+ else
+ info->flags &= ~ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else
+ info->flags |= ASYNC_CHECK_CD;
+
+ /*
+ * Set up parity check flag
+ */
+#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ info->read_status_mask = _LSR_OE_MASK;
+ if (I_INPCK(info->tty))
+ info->read_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK;
+ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ info->read_status_mask |= _LSR_LB_MASK;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK;
+ if (I_IGNBRK(info->tty)) {
+ info->ignore_status_mask |= _LSR_LB_MASK;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns to (for real raw support).
+ */
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= _LSR_OE_MASK;
+ }
+
+ /* disable interrupts while reading and clearing registers */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+
+ old_rcr = readb(info->port->uart_base + BL_SICC_RCR);
+ old_tcr = readb(info->port->uart_base + BL_SICC_TxCR);
+
+
+ writeb(0, info->port->uart_base + BL_SICC_RCR);
+ writeb(0, info->port->uart_base + BL_SICC_TxCR);
+
+ /*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/
+
+
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+
+
+ /* Set baud rate */
+ writeb((quot & 0x00000F00)>>8, info->port->uart_base + BL_SICC_BRDH );
+ writeb( quot & 0x00000FF, info->port->uart_base + BL_SICC_BRDL );
+
+ /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */
+ /* For now, do NOT use FIFOs since 403 UART did not have this */
+ /* capability and this driver was inherited from 403UART. */
+ writeb(_CTL2_EXTERN, info->port->uart_base + BL_SICC_CTL2);
+
+ writeb(lcr_h, info->port->uart_base + BL_SICC_LCR);
+
+ writeb(old_rcr, info->port->uart_base + BL_SICC_RCR); // restore rcr
+ writeb(old_tcr, info->port->uart_base + BL_SICC_TxCR); // restore txcr
+
+}
+
+
+static void siccuart_put_char(struct tty_struct *tty, u_char ch)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ if (!tty || !info->xmit.buf)
+ return;
+
+ /* lock info->xmit while adding character to tx buffer */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) != 0) {
+ info->xmit.buf[info->xmit.head] = ch;
+ info->xmit.head = (info->xmit.head + 1) & (SICC_XMIT_SIZE - 1);
+ }
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+}
+
+static void siccuart_flush_chars(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ if (info->xmit.head == info->xmit.tail
+ || tty->stopped
+ || tty->hw_stopped
+ || !info->xmit.buf)
+ return;
+
+ /* disable interrupts while transmitting characters */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ siccuart_enable_tx_interrupt(info);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+}
+
+static int siccuart_write(struct tty_struct *tty,
+ const u_char * buf, int count)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+ int c, ret = 0;
+
+ if (!tty || !info->xmit.buf || !tmp_buf)
+ return 0;
+
+ /* lock info->xmit while removing characters from buffer */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ while (1) {
+ c = CIRC_SPACE_TO_END(info->xmit.head,
+ info->xmit.tail,
+ SICC_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(info->xmit.buf + info->xmit.head, buf, c);
+ info->xmit.head = (info->xmit.head + c) &
+ (SICC_XMIT_SIZE - 1);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ if (info->xmit.head != info->xmit.tail
+ && !tty->stopped
+ && !tty->hw_stopped)
+ siccuart_enable_tx_interrupt(info);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ return ret;
+}
+
+static int siccuart_write_room(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+
+ return CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE);
+}
+
+static int siccuart_chars_in_buffer(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+
+ return CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE);
+}
+
+static void siccuart_flush_buffer(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ pr_debug("siccuart_flush_buffer(%d) called\n", tty->index);
+ /* lock info->xmit while zeroing buffer counts */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ info->xmit.head = info->xmit.tail = 0;
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void siccuart_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct SICC_info *info = tty->driver_data;
+
+ info->x_char = ch;
+ if (ch)
+ siccuart_enable_tx_interrupt(info);
+}
+
+static void siccuart_throttle(struct tty_struct *tty)
+{
+ struct SICC_info *info = tty->driver_data;
+ unsigned long flags;
+
+ if (I_IXOFF(tty))
+ siccuart_send_xchar(tty, STOP_CHAR(tty));
+
+ if (tty->termios->c_cflag & CRTSCTS) {
+ /* disable interrupts while setting modem control lines */
+ spin_lock_irqsave(&info->state->sicc_lock,flags);
+ info->mctrl &= ~TIOCM_RTS;
+ info->port->set_mctrl(info->port, info->mctrl);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ }
+}
+
+static void siccuart_unthrottle(struct tty_struct *tty)
+{
+ struct SICC_info *info = (struct SICC_info *) tty->driver_data;
+ unsigned long flags;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else