summaryrefslogtreecommitdiffstats
path: root/arch/mips/philips
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-02-22 04:12:11 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-02-22 04:12:11 +0000
commitc884ada29ead83a2777c4097489dc4db26419624 (patch)
tree98bc5e293200d703cb6d9b47ab6f052dfaba0041 /arch/mips/philips
parent95b2d612949328e196b54c7569324e22f98c6ec0 (diff)
Support for Phillips PDAs.
Diffstat (limited to 'arch/mips/philips')
-rw-r--r--arch/mips/philips/drivers/.cvsignore2
-rw-r--r--arch/mips/philips/drivers/Makefile20
-rw-r--r--arch/mips/philips/drivers/generic_serial.c1078
-rw-r--r--arch/mips/philips/drivers/uart-pr31700.c1313
-rw-r--r--arch/mips/philips/drivers/uart-pr31700.h126
-rw-r--r--arch/mips/philips/nino/.cvsignore2
-rw-r--r--arch/mips/philips/nino/Makefile26
-rw-r--r--arch/mips/philips/nino/int-handler.S138
-rw-r--r--arch/mips/philips/nino/irq.c304
-rw-r--r--arch/mips/philips/nino/kgdb.c83
-rw-r--r--arch/mips/philips/nino/power.c36
-rw-r--r--arch/mips/philips/nino/prom.c83
-rw-r--r--arch/mips/philips/nino/reset.c37
-rw-r--r--arch/mips/philips/nino/rtc.c34
-rw-r--r--arch/mips/philips/nino/setup.c117
-rw-r--r--arch/mips/philips/nino/time.c216
-rw-r--r--arch/mips/philips/nino/wbflush.c36
17 files changed, 3651 insertions, 0 deletions
diff --git a/arch/mips/philips/drivers/.cvsignore b/arch/mips/philips/drivers/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/arch/mips/philips/drivers/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/arch/mips/philips/drivers/Makefile b/arch/mips/philips/drivers/Makefile
new file mode 100644
index 000000000..a0e316db0
--- /dev/null
+++ b/arch/mips/philips/drivers/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Philips Nino specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+.S.s:
+ $(CPP) $(AFLAGS) $< -o $@
+.S.o:
+ $(CC) $(AFLAGS) -c $< -o $@
+
+O_TARGET := drivers.o
+
+all: drivers.o
+
+obj-$(CONFIG_SERIAL) += uart-pr31700.o generic_serial.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/philips/drivers/generic_serial.c b/arch/mips/philips/drivers/generic_serial.c
new file mode 100644
index 000000000..acd3402f8
--- /dev/null
+++ b/arch/mips/philips/drivers/generic_serial.c
@@ -0,0 +1,1078 @@
+/*
+ * generic_serial.c
+ *
+ * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl
+ *
+ * written for the SX serial driver.
+ * Contains the code that should be shared over all the serial drivers.
+ *
+ * Credit for the idea to do it this way might go to Alan Cox.
+ *
+ *
+ * Version 0.1 -- December, 1998. Initial version.
+ * Version 0.2 -- March, 1999. Some more routines. Bugfixes. Etc.
+ * Version 0.5 -- August, 1999. Some more fixes. Reformat for Linus.
+ *
+ * BitWizard is actively maintaining this file. We sometimes find
+ * that someone submitted changes to this file. We really appreciate
+ * your help, but please submit changes through us. We're doing our
+ * best to be responsive. -- REW
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/generic_serial.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#define DEBUG
+
+static char * tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+static int gs_debug;
+
+#ifdef DEBUG
+#define gs_dprintk(f, str...) if (gs_debug & f) printk (str)
+#else
+#define gs_dprintk(f, str...) /* nothing */
+#endif
+
+#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n")
+#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n")
+
+#if NEW_WRITE_LOCKING
+#define DECL /* Nothing */
+#define LOCKIT down (& port->port_write_sem);
+#define RELEASEIT up (&port->port_write_sem);
+#else
+#define DECL unsigned long flags;
+#define LOCKIT save_flags (flags);cli ()
+#define RELEASEIT restore_flags (flags)
+#endif
+
+#define RS_EVENT_WRITE_WAKEUP 1
+
+MODULE_PARM(gs_debug, "i");
+
+
+void gs_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct gs_port *port;
+ DECL
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (! (port->flags & ASYNC_INITIALIZED)) return;
+
+ /* Take a lock on the serial tranmit buffer! */
+ LOCKIT;
+
+ if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ /* Sorry, buffer is full, drop character. Update statistics???? -- REW */
+ RELEASEIT;
+ return;
+ }
+
+ port->xmit_buf[port->xmit_head++] = ch;
+ port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+ port->xmit_cnt++; /* Characters in buffer */
+
+ RELEASEIT;
+ func_exit ();
+}
+
+
+#ifdef NEW_WRITE_LOCKING
+
+/*
+> Problems to take into account are:
+> -1- Interrupts that empty part of the buffer.
+> -2- page faults on the access to userspace.
+> -3- Other processes that are also trying to do a "write".
+*/
+
+int gs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct gs_port *port;
+ int c, total = 0;
+ int t;
+
+ func_enter ();
+
+ if (!tty) return 0;
+
+ port = tty->driver;
+
+ if (!port) return 0;
+
+ if (! (port->flags & ASYNC_INITIALIZED))
+ return 0;
+
+ /* get exclusive "write" access to this port (problem 3) */
+ /* This is not a spinlock because we can have a disk access (page
+ fault) in copy_from_user */
+ down (& port->port_write_sem);
+
+ while (1) {
+
+ c = count;
+
+ /* This is safe because we "OWN" the "head". Noone else can
+ change the "head": we own the port_write_sem. */
+ /* Don't overrun the end of the buffer */
+ t = SERIAL_XMIT_SIZE - port->xmit_head;
+ if (t < c) c = t;
+
+ /* This is safe because the xmit_cnt can only decrease. This
+ would increase "t", so we might copy too little chars. */
+ /* Don't copy past the "head" of the buffer */
+ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
+ if (t < c) c = t;
+
+ /* Can't copy more? break out! */
+ if (c <= 0) break;
+ if (from_user)
+ copy_from_user (port->xmit_buf + port->xmit_head, buf, c);
+ else
+ memcpy (port->xmit_buf + port->xmit_head, buf, c);
+
+ port -> xmit_cnt += c;
+ port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ up (& port->port_write_sem);
+
+ gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n",
+ (port->flags & GS_TX_INTEN)?"enabled": "disabled");
+
+ if (port->xmit_cnt &&
+ !tty->stopped &&
+ !tty->hw_stopped &&
+ !(port->flags & GS_TX_INTEN)) {
+ port->flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ }
+ func_exit ();
+ return total;
+}
+#else
+/*
+> Problems to take into account are:
+> -1- Interrupts that empty part of the buffer.
+> -2- page faults on the access to userspace.
+> -3- Other processes that are also trying to do a "write".
+*/
+
+int gs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct gs_port *port;
+ int c, total = 0;
+ int t;
+ unsigned long flags;
+
+ func_enter ();
+
+ /* The standard serial driver returns 0 in this case.
+ That sounds to me as "No error, I just didn't get to writing any
+ bytes. Feel free to try again."
+ The "official" way to write n bytes from buf is:
+
+ for (nwritten = 0;nwritten < n;nwritten += rv) {
+ rv = write (fd, buf+nwritten, n-nwritten);
+ if (rv < 0) break; // Error: bail out. //
+ }
+
+ which will loop endlessly in this case. The manual page for write
+ agrees with me. In practise almost everybody writes
+ "write (fd, buf,n);" but some people might have had to deal with
+ incomplete writes in the past and correctly implemented it by now...
+ */
+
+ if (!tty) return -EIO;
+
+ port = tty->driver_data;
+ if (!port || !port->xmit_buf || !tmp_buf)
+ return -EIO;
+
+ save_flags(flags);
+ if (from_user) {
+ down(&tmp_buf_sem);
+ while (1) {
+ c = count;
+
+ /* This is safe because we "OWN" the "head". Noone else can
+ change the "head": we own the port_write_sem. */
+ /* Don't overrun the end of the buffer */
+ t = SERIAL_XMIT_SIZE - port->xmit_head;
+ if (t < c) c = t;
+
+ /* This is safe because the xmit_cnt can only decrease. This
+ would increase "t", so we might copy too little chars. */
+ /* Don't copy past the "head" of the buffer */
+ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
+ if (t < c) c = t;
+
+ /* Can't copy more? break out! */
+ if (c <= 0) break;
+
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!total)
+ total = -EFAULT;
+ break;
+ }
+ cli();
+ t = SERIAL_XMIT_SIZE - port->xmit_head;
+ if (t < c) c = t;
+ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
+ if (t < c) c = t;
+
+ memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+ port->xmit_head = ((port->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ port->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ up(&tmp_buf_sem);
+ } else {
+ while (1) {
+ cli();
+ c = count;
+
+ /* This is safe because we "OWN" the "head". Noone else can
+ change the "head": we own the port_write_sem. */
+ /* Don't overrun the end of the buffer */
+ t = SERIAL_XMIT_SIZE - port->xmit_head;
+ if (t < c) c = t;
+
+ /* This is safe because the xmit_cnt can only decrease. This
+ would increase "t", so we might copy too little chars. */
+ /* Don't copy past the "head" of the buffer */
+ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
+ if (t < c) c = t;
+
+ /* Can't copy more? break out! */
+ if (c <= 0) {
+ restore_flags(flags);
+ break;
+ }
+ memcpy(port->xmit_buf + port->xmit_head, buf, c);
+ port->xmit_head = ((port->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ port->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ }
+
+ if (port->xmit_cnt &&
+ !tty->stopped &&
+ !tty->hw_stopped &&
+ !(port->flags & GS_TX_INTEN)) {
+ port->flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ }
+ func_exit ();
+ return total;
+}
+
+#endif
+
+
+
+int gs_write_room(struct tty_struct * tty)
+{
+ struct gs_port *port = tty->driver_data;
+ int ret;
+
+ func_enter ();
+ ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ func_exit ();
+ return ret;
+}
+
+
+int gs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ func_enter ();
+
+ func_exit ();
+ return port->xmit_cnt;
+}
+
+
+int gs_real_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port;
+ func_enter ();
+
+ if (!tty) return 0;
+ port = tty->driver_data;
+
+ if (!port->rd) return 0;
+ if (!port->rd->chars_in_buffer) return 0;
+
+ func_exit ();
+ return port->xmit_cnt + port->rd->chars_in_buffer (port);
+}
+
+
+static int gs_wait_tx_flushed (void * ptr, int timeout)
+{
+ struct gs_port *port = ptr;
+ long end_jiffies;
+ int jiffies_to_transmit, charsleft = 0, rv = 0;
+ int to, rcib;
+
+ func_enter();
+
+ gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port);
+ if (port) {
+ gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n",
+ port->xmit_cnt, port->xmit_buf, port->tty);
+ }
+
+ if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
+ gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
+ func_exit();
+ return -EINVAL; /* This is an error which we don't know how to handle. */
+ }
+
+ rcib = gs_real_chars_in_buffer(port->tty);
+
+ if(rcib <= 0) {
+ gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
+ func_exit();
+ return rv;
+ }
+ /* stop trying: now + twice the time it would normally take + seconds */
+ end_jiffies = jiffies;
+ if (timeout != MAX_SCHEDULE_TIMEOUT)
+ end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0;
+ end_jiffies += timeout;
+
+ gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n",
+ jiffies, end_jiffies, end_jiffies-jiffies);
+
+ to = 100;
+ /* the expression is actually jiffies < end_jiffies, but that won't
+ work around the wraparound. Tricky eh? */
+ while (to-- &&
+ (charsleft = gs_real_chars_in_buffer (port->tty)) &&
+ time_after (end_jiffies, jiffies)) {
+ /* Units check:
+ chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies!
+ check! */
+
+ charsleft += 16; /* Allow 16 chars more to be transmitted ... */
+ jiffies_to_transmit = port->baud?(1 + charsleft * 10 * HZ / port->baud):0;
+ /* ^^^ Round up.... */
+ if (jiffies_to_transmit <= 0) jiffies_to_transmit = 1;
+
+ gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies "
+ "(%d chars).\n", jiffies_to_transmit, charsleft);
+
+ set_current_state (TASK_INTERRUPTIBLE);
+ schedule_timeout(jiffies_to_transmit);
+ if (signal_pending (current)) {
+ gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
+ rv = -EINTR;
+ break;
+ }
+ }
+
+ gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft);
+ set_current_state (TASK_RUNNING);
+
+ func_exit();
+ return rv;
+}
+
+
+
+void gs_flush_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port;
+ unsigned long flags;
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ /* XXX Would the write semaphore do? */
+ save_flags(flags); cli();
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ func_exit ();
+}
+
+
+void gs_flush_chars(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !port->xmit_buf) {
+ func_exit ();
+ return;
+ }
+
+ /* Beats me -- REW */
+ port->flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ func_exit ();
+}
+
+
+void gs_stop(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt &&
+ port->xmit_buf &&
+ (port->flags & GS_TX_INTEN) ) {
+ port->flags &= ~GS_TX_INTEN;
+ port->rd->disable_tx_interrupts (port);
+ }
+ func_exit ();
+}
+
+
+void gs_start(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt &&
+ port->xmit_buf &&
+ !(port->flags & GS_TX_INTEN) ) {
+ port->flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ }
+ func_exit ();
+}
+
+
+void gs_shutdown_port (struct gs_port *port)
+{
+ long flags;
+
+ func_enter();
+
+ if (!port) return;
+
+ if (!(port->flags & ASYNC_INITIALIZED))
+ return;
+
+ save_flags (flags);
+ cli ();
+
+ if (port->xmit_buf) {
+ free_page((unsigned long) port->xmit_buf);
+ port->xmit_buf = 0;
+ }
+
+ if (port->tty)
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
+
+ port->rd->shutdown_port (port);
+
+ port->flags &= ~ASYNC_INITIALIZED;
+ restore_flags (flags);
+
+ func_exit();
+}
+
+
+void gs_hangup(struct tty_struct *tty)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+ tty = port->tty;
+ if (!tty)
+ return;
+
+ gs_shutdown_port (port);
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE |GS_ACTIVE);
+ port->tty = NULL;
+ port->count = 0;
+
+ wake_up_interruptible(&port->open_wait);
+ func_exit ();
+}
+
+
+void gs_do_softint(void *private_)
+{
+ struct gs_port *port = private_;
+ struct tty_struct *tty;
+
+ func_enter ();
+
+ if (!port) return;
+
+ tty = port->tty;
+
+ if (!tty) return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ func_exit ();
+}
+
+
+int gs_block_til_ready(void *port_, struct file * filp)
+{
+ struct gs_port *port = port_;
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0;
+ int CD;
+ struct tty_struct *tty;
+
+ func_enter ();
+
+ if (!port) return 0;
+
+ tty = port->tty;
+
+ if (!tty) return 0;
+
+ gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n");
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&port->close_wait);
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+ }
+
+ gs_dprintk (GS_DEBUG_BTR, "after hung up\n");
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == GS_TYPE_CALLOUT) {
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_SESSION_LOCKOUT) &&
+ (port->session != current->session))
+ return -EBUSY;
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_PGRP_LOCKOUT) &&
+ (port->pgrp != current->pgrp))
+ return -EBUSY;
+ port->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ gs_dprintk (GS_DEBUG_BTR, "after subtype\n");
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ gs_dprintk (GS_DEBUG_BTR, "after nonblock\n");
+
+ if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (port->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (C_CLOCAL(tty))
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, port->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+
+ add_wait_queue(&port->open_wait, &wait);
+
+ gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n");
+ cli();
+ if (!tty_hung_up_p(filp))
+ port->count--;
+ sti();
+ port->blocked_open++;
+ while (1) {
+ CD = port->rd->get_CD (port);
+ gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD);
+ set_current_state (TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(port->flags & ASYNC_INITIALIZED)) {
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(port->flags & ASYNC_CLOSING) &&
+ (do_clocal || CD))
+ break;
+ gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n",
+ (int)signal_pending (current), *(long*)(&current->blocked));
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n",
+ port->blocked_open);
+ set_current_state (TASK_RUNNING);
+ remove_wait_queue(&port->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ port->count++;
+ port->blocked_open--;
+ if (retval)
+ return retval;
+
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ func_exit ();
+ return 0;
+}
+
+
+void gs_close(struct tty_struct * tty, struct file * filp)
+{
+ unsigned long flags;
+ struct gs_port *port;
+
+ func_enter ();
+
+ if (!tty) return;
+
+ port = (struct gs_port *) tty->driver_data;
+
+ if (!port) return;
+
+ if (!port->tty) {
+ /* This seems to happen when this is called from vhangup. */
+ gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n");
+ port->tty = tty;
+ }
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ port->rd->hungup (port);
+ func_exit ();
+ return;
+ }
+
+ if ((tty->count == 1) && (port->count != 1)) {
+ printk(KERN_ERR "gs: gs_close: bad port count;"
+ " tty->count is 1, port count is %d\n", port->count);
+ port->count = 1;
+ }
+ if (--port->count < 0) {
+ printk(KERN_ERR "gs: gs_close: bad port count: %d\n", port->count);
+ port->count = 0;
+ }
+ if (port->count) {
+ gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count);
+ restore_flags(flags);
+ func_exit ();
+ return;
+ }
+ port->flags |= ASYNC_CLOSING;
+
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ port->normal_termios = *tty->termios;
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ port->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ /* if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, port->closing_wait); */
+
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+
+ port->rd->disable_rx_interrupts (port);
+
+ /* close has no way of returning "EINTR", so discard return value */
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ gs_wait_tx_flushed (port, port->closing_wait);
+
+ port->flags &= ~GS_ACTIVE;
+
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+
+ port->event = 0;
+ port->rd->close (port);
+ port->rd->shutdown_port (port);
+ port->tty = 0;
+
+ if (port->blocked_open) {
+ if (port->close_delay) {
+ set_current_state (TASK_INTERRUPTIBLE);
+ schedule_timeout(port->close_delay);
+ }
+ wake_up_interruptible(&port->open_wait);
+ }
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING | ASYNC_INITIALIZED);
+ wake_up_interruptible(&port->close_wait);
+
+ restore_flags(flags);
+ func_exit ();
+}
+
+
+static unsigned int gs_baudrates[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+};
+
+
+void gs_set_termios (struct tty_struct * tty,
+ struct termios * old_termios)
+{
+ struct gs_port *port;
+ int baudrate, tmp, rv;
+ struct termios *tiosp;
+
+ func_enter();
+
+ if (!tty) return;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ tiosp = tty->termios;
+
+ if (gs_debug & GS_DEBUG_TERMIOS) {
+ gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp);
+ }
+
+#if 0
+ /* This is an optimization that is only allowed for dumb cards */
+ /* Smart cards require knowledge of iflags and oflags too: that
+ might change hardware cooking mode.... */
+#endif
+ if (old_termios) {
+ if( (tiosp->c_iflag == old_termios->c_iflag)
+ && (tiosp->c_oflag == old_termios->c_oflag)
+ && (tiosp->c_cflag == old_termios->c_cflag)
+ && (tiosp->c_lflag == old_termios->c_lflag)
+ && (tiosp->c_line == old_termios->c_line)
+ && (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) {
+ gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n");
+ return /* 0 */;
+ }
+ } else
+ gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: "
+ "no optimization\n");
+
+ if(old_termios && (gs_debug & GS_DEBUG_TERMIOS)) {
+ if(tiosp->c_iflag != old_termios->c_iflag) printk("c_iflag changed\n");
+ if(tiosp->c_oflag != old_termios->c_oflag) printk("c_oflag changed\n");
+ if(tiosp->c_cflag != old_termios->c_cflag) printk("c_cflag changed\n");
+ if(tiosp->c_lflag != old_termios->c_lflag) printk("c_lflag changed\n");
+ if(tiosp->c_line != old_termios->c_line) printk("c_line changed\n");
+ if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
+ }
+
+ baudrate = tiosp->c_cflag & CBAUD;
+ if (baudrate & CBAUDEX) {
+ baudrate &= ~CBAUDEX;
+ if ((baudrate < 1) || (baudrate > 4))
+ tiosp->c_cflag &= ~CBAUDEX;
+ else
+ baudrate += 15;
+ }
+
+ baudrate = gs_baudrates[baudrate];
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baudrate = 57600;
+ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baudrate = 115200;
+ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ baudrate = 230400;
+ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ baudrate = 460800;
+ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ baudrate = (port->baud_base / port->custom_divisor);
+ }
+
+ /* I recommend using THIS instead of the mess in termios (and
+ duplicating the above code). Next we should create a clean
+ interface towards this variable. If your card supports arbitrary
+ baud rates, (e.g. CD1400 or 16550 based cards) then everything
+ will be very easy..... */
+ port->baud = baudrate;
+
+ /* Two timer ticks seems enough to wakeup something like SLIP driver */
+ /* Baudrate/10 is cps. Divide by HZ to get chars per tick. */
+ tmp = (baudrate / 10 / HZ) * 2;
+
+ if (tmp < 0) tmp = 0;
+ if (tmp >= SERIAL_XMIT_SIZE) tmp = SERIAL_XMIT_SIZE-1;
+
+ port->wakeup_chars = tmp;
+
+ /* We should really wait for the characters to be all sent before
+ changing the settings. -- CAL */
+ rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ if (rv < 0) return /* rv */;
+
+ rv = port->rd->set_real_termios(port);
+ if (rv < 0) return /* rv */;
+
+ if ((!old_termios ||
+ (old_termios->c_cflag & CRTSCTS)) &&
+ !( tiosp->c_cflag & CRTSCTS)) {
+ tty->stopped = 0;
+ gs_start(tty);
+ }
+
+#ifdef tytso_patch_94Nov25_1726
+ /* This "makes sense", Why is it commented out? */
+
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+
+ func_exit();
+ return /* 0 */;
+}
+
+
+
+/* Must be called with interrupts enabled */
+int gs_init_port(struct gs_port *port)
+{
+ unsigned long flags;
+ unsigned long page;
+
+ save_flags (flags);
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+
+ cli (); /* Don't expect this to make a difference. */
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ restore_flags (flags);
+
+ if (!tmp_buf) {
+ return -ENOMEM;
+ }
+ }
+
+ if (port->flags & ASYNC_INITIALIZED)
+ return 0;
+
+ if (!port->xmit_buf) {
+ /* We may sleep in get_free_page() */
+ unsigned long tmp;
+
+ tmp = get_free_page(GFP_KERNEL);
+
+ /* Spinlock? */
+ cli ();
+ if (port->xmit_buf)
+ free_page (tmp);
+ else
+ port->xmit_buf = (unsigned char *) tmp;
+ restore_flags (flags);
+
+ if (!port->xmit_buf)
+ return -ENOMEM;
+ }
+
+ cli();
+
+ if (port->tty)
+ clear_bit(TTY_IO_ERROR, &port->tty->flags);
+
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+
+ gs_set_termios(port->tty, NULL);
+
+ port->flags |= ASYNC_INITIALIZED;
+ port->flags &= ~GS_TX_INTEN;
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+int gs_setserial(struct gs_port *port, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+
+ copy_from_user(&sio, sp, sizeof(struct serial_struct));
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if ((sio.baud_base != port->baud_base) ||
+ (sio.close_delay != port->close_delay) ||
+ ((sio.flags & ~ASYNC_USR_MASK) !=
+ (port->flags & ~ASYNC_USR_MASK)))
+ return(-EPERM);
+ }
+
+ port->flags = (port->flags & ~ASYNC_USR_MASK) |
+ (sio.flags & ASYNC_USR_MASK);
+
+ port->baud_base = sio.baud_base;
+ port->close_delay = sio.close_delay;
+ port->closing_wait = sio.closing_wait;
+ port->custom_divisor = sio.custom_divisor;
+
+ gs_set_termios (port->tty, NULL);
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+
+/*
+ * Generate the serial struct info.
+ */
+
+void gs_getserial(struct gs_port *port, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+
+ memset(&sio, 0, sizeof(struct serial_struct));
+ sio.flags = port->flags;
+ sio.baud_base = port->baud_base;
+ sio.close_delay = port->close_delay;
+ sio.closing_wait = port->closing_wait;
+ sio.custom_divisor = port->custom_divisor;
+ sio.hub6 = 0;
+
+ /* If you want you can override these. */
+ sio.type = PORT_UNKNOWN;
+ sio.xmit_fifo_size = -1;
+ sio.line = -1;
+ sio.port = -1;
+ sio.irq = -1;
+
+ if (port->rd->getserial)
+ port->rd->getserial (port, &sio);
+
+ copy_to_user(sp, &sio, sizeof(struct serial_struct));
+}
+
+EXPORT_SYMBOL(gs_put_char);
+EXPORT_SYMBOL(gs_write);
+EXPORT_SYMBOL(gs_write_room);
+EXPORT_SYMBOL(gs_chars_in_buffer);
+EXPORT_SYMBOL(gs_flush_buffer);
+EXPORT_SYMBOL(gs_flush_chars);
+EXPORT_SYMBOL(gs_stop);
+EXPORT_SYMBOL(gs_start);
+EXPORT_SYMBOL(gs_hangup);
+EXPORT_SYMBOL(gs_do_softint);
+EXPORT_SYMBOL(gs_block_til_ready);
+EXPORT_SYMBOL(gs_close);
+EXPORT_SYMBOL(gs_set_termios);
+EXPORT_SYMBOL(gs_init_port);
+EXPORT_SYMBOL(gs_setserial);
+EXPORT_SYMBOL(gs_getserial);
+
diff --git a/arch/mips/philips/drivers/uart-pr31700.c b/arch/mips/philips/drivers/uart-pr31700.c
new file mode 100644
index 000000000..92adb7056
--- /dev/null
+++ b/arch/mips/philips/drivers/uart-pr31700.c
@@ -0,0 +1,1313 @@
+/*
+ * Serial driver for r39xx
+ *
+ * Copyright (C) 2000 Jim Pick <jim@jimpick.com>
+ *
+ * Inspired by, and/or includes bits from:
+ *
+ * drivers/char/serial.c (standard serial driver)
+ * drivers/char/sx.c (use of generic_serial interface)
+ * drivers/char/esp.c (another UART that uses DMA)
+ * arch/mips/vr41xx/serial.c (another MIPS serial driver)
+ *
+ * Please see those files for credits.
+ *
+ * Also, the original rough serial console code was:
+ *
+ * Copyright (C) 1999 Harald Koerfgen
+ *
+ * $Id: r39xx_serial.c,v 1.16 2001/01/11 20:24:47 pavel Exp $
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <asm/uaccess.h>
+#include <asm/philips/pr31700.h>
+#include <asm/delay.h>
+#include <asm/wbflush.h>
+#include "uart-pr31700.h"
+
+/* Prototypes */
+
+static void rs_disable_tx_interrupts (void * ptr);
+static void rs_enable_tx_interrupts (void * ptr);
+static void rs_disable_rx_interrupts (void * ptr);
+static void rs_enable_rx_interrupts (void * ptr);
+static int rs_get_CD (void * ptr);
+static void rs_shutdown_port (void * ptr);
+static int rs_set_real_termios (void *ptr);
+static int rs_chars_in_buffer (void * ptr);
+static void rs_hungup (void *ptr);
+static void rs_close (void *ptr);
+
+
+
+static struct real_driver rs_real_driver = {
+ disable_tx_interrupts: rs_disable_tx_interrupts,
+ enable_tx_interrupts: rs_enable_tx_interrupts,
+ disable_rx_interrupts: rs_disable_rx_interrupts,
+ enable_rx_interrupts: rs_enable_rx_interrupts,
+ get_CD: rs_get_CD,
+ shutdown_port: rs_shutdown_port,
+ set_real_termios: rs_set_real_termios,
+ chars_in_buffer: rs_chars_in_buffer,
+ close: rs_close,
+ hungup: rs_hungup,
+};
+
+static struct tty_driver rs_driver, rs_callout_driver;
+
+static struct tty_struct * rs_table[RS_NPORTS] = { NULL, };
+static struct termios ** rs_termios;
+static struct termios ** rs_termios_locked;
+
+struct rs_port *rs_ports;
+int rs_refcount;
+int rs_initialized = 0;
+
+#ifdef CONFIG_PM
+static struct pm_dev *pmdev;
+static int pm_request(struct pm_dev* dev, pm_request_t req, void* data);
+#endif
+
+#define DEBUG
+#undef DEBUG2
+
+#ifdef DEBUG2
+int rs_debug = RS_DEBUG_ALL & ~RS_DEBUG_TRANSMIT;
+#else
+int rs_debug = 0;
+#endif
+
+
+/*
+ * Helper routines
+ */
+
+
+static inline unsigned int serial_in(struct rs_port *port, int offset)
+{
+ unsigned int tmp;
+
+ tmp = *(volatile unsigned int *)(port->base + offset);
+ tmp &= 0xff;
+ barrier();
+ return tmp;
+}
+
+static inline void serial_out(struct rs_port *port, int offset, int value)
+{
+ *(volatile unsigned int *)(port->base + offset) = (unsigned char)value;
+
+ barrier();
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+
+
+static inline void receive_char_pio(struct rs_port *port)
+{
+ struct tty_struct *tty = port->gs.tty;
+ unsigned char ch;
+ int counter = 2048;
+
+ /* While there are characters, get them ... */
+ while (counter>0) {
+ // printk("R%08x", (int)port->base[UART_R39XX_CTRL1]);
+ if (!(port->base[UART_R39XX_CTRL1] & UART_RX_HOLD_FULL)) {
+ break;
+ }
+ ch = serial_in(port, UART_R39XX_DATA);
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ *tty->flip.char_buf_ptr++ = ch;
+ *tty->flip.flag_buf_ptr++ = 0;
+ tty->flip.count++;
+ }
+ udelay(1); /* Allow things to happen - it take a while */
+ counter--;
+ }
+ if (!counter)
+ printk( "Ugh, looped in receive_char_pio!\n" );
+
+ tty_flip_buffer_push(tty);
+#if 0
+ /* Now handle error conditions */
+ if (*status & (INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_PARITYERR_INT) |
+ INTTYPE(UART_BREAK_INT))) {
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ */
+ if (*status & port->ignore_status_mask) {
+ goto ignore_char;
+ }
+ *status &= port->read_status_mask;
+
+ if (*status & INTTYPE(UART_BREAK_INT)) {
+ rs_dprintk(RS_DEBUG_INTERRUPTS, "handling break....");
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ }
+ else if (*status & INTTYPE(UART_PARITYERR_INT)) {
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ }
+ else if (*status & INTTYPE(UART_FRAMEERR_INT)) {
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ }
+ if (*status & INTTYPE(UART_RXOVERRUN_INT)) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+ }
+ }
+
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+
+ ignore_char:
+
+ tty_flip_buffer_push(tty);
+#endif
+}
+
+static inline void transmit_char_pio(struct rs_port *port)
+{
+ /* While I'm able to transmit ... */
+ for (;;) {
+ // printk("T%08x", (int)port->base[UART_R39XX_CTRL1]);
+ if (!(port->base[UART_R39XX_CTRL1] & UART_TX_EMPTY)) {
+ break;
+ }
+ else if (port->x_char) {
+ serial_out(port, UART_R39XX_DATA, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ }
+ else if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped ||
+ port->gs.tty->hw_stopped) {
+ break;
+ }
+ else {
+ serial_out(port, UART_R39XX_DATA, port->gs.xmit_buf[port->gs.xmit_tail++]);
+ port->icount.tx++;
+ port->gs.xmit_tail &= SERIAL_XMIT_SIZE-1;
+ if (--port->gs.xmit_cnt <= 0) {
+ break;
+ }
+ }
+ udelay(10); /* Allow things to happen - it take a while */
+ }
+
+ if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped ||
+ port->gs.tty->hw_stopped) {
+ rs_disable_tx_interrupts(port);
+ }
+
+ if (port->gs.xmit_cnt <= port->gs.wakeup_chars) {
+ if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ port->gs.tty->ldisc.write_wakeup)
+ (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+ rs_dprintk (RS_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+ port->gs.wakeup_chars);
+ wake_up_interruptible(&port->gs.tty->write_wait);
+ }
+}
+
+
+
+static inline void check_modem_status(struct rs_port *port)
+{
+ /* We don't have a carrier detect line - but just respond
+ like we had one anyways so that open() becomes unblocked */
+ wake_up_interruptible(&port->gs.open_wait);
+}
+
+int count = 0;
+
+/*
+ * This is the serial driver's interrupt routine (inlined, because
+ * there are two different versions of this, one for each serial port,
+ * differing only by the bits used in interrupt status 2 register)
+ */
+
+static inline void rs_rx_interrupt(int irq, void *dev_id,
+ struct pt_regs * regs, int intshift)
+{
+ struct rs_port * port;
+ unsigned long int2status;
+ unsigned long flags;
+ unsigned long ints;
+
+ save_and_cli(flags);
+
+ port = (struct rs_port *)dev_id;
+ /* rs_dprintk (RS_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); */
+
+ /* Get the interrrupts we have enabled */
+ int2status = IntStatus2 & IntEnable2;
+
+ /* Get interrupts in easy to use form */
+ ints = int2status >> intshift;
+ // printk("IR%03x%03x", (int)(IntStatus2 >> intshift), (int)(IntEnable2 >> intshift));
+
+ /* Clear any interrupts we might be about to handle */
+ IntClear2 = int2status & (
+ (INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_BREAK_INT) |
+ INTTYPE(UART_PARITYERR_INT) |
+ INTTYPE(UART_RX_INT)) << intshift);
+
+ if (!port || !port->gs.tty) {
+ restore_flags(flags);
+ return;
+ }
+
+ /* RX Receiver Holding Register Overrun */
+ if (ints & INTTYPE(UART_RXOVERRUN_INT)) {
+ rs_dprintk (RS_DEBUG_INTERRUPTS, "overrun");
+ port->icount.overrun++;
+ }
+
+ /* RX Frame Error */
+ if (ints & INTTYPE(UART_FRAMEERR_INT)) {
+ rs_dprintk (RS_DEBUG_INTERRUPTS, "frame error");
+ port->icount.frame++;
+ }
+
+ /* Break signal received */
+ if (ints & INTTYPE(UART_BREAK_INT)) {
+ rs_dprintk (RS_DEBUG_INTERRUPTS, "break");
+ port->icount.brk++;
+ }
+
+ /* RX Parity Error */
+ if (ints & INTTYPE(UART_PARITYERR_INT)) {
+ rs_dprintk (RS_DEBUG_INTERRUPTS, "parity error");
+ port->icount.parity++;
+ }
+
+ /* Receive byte (non-DMA) */
+ if (ints & INTTYPE(UART_RX_INT)) {
+ receive_char_pio(port);
+ }
+
+ /*
+ check_modem_status();
+ */
+
+ // printk("OR%03x", (int)((IntStatus2 & IntEnable2) >> intshift));
+
+ restore_flags(flags);
+
+/* rs_dprintk (RS_DEBUG_INTERRUPTS, "end.\n"); */
+
+}
+
+static inline void rs_tx_interrupt(int irq, void *dev_id,
+ struct pt_regs * regs, int intshift)
+{
+ struct rs_port * port;
+ unsigned long int2status;
+ unsigned long flags;
+ unsigned long ints;
+
+ save_and_cli(flags);
+
+ port = (struct rs_port *)dev_id;
+ /* rs_dprintk (RS_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); */
+
+ /* Get the interrrupts we have enabled */
+ int2status = IntStatus2 & IntEnable2;
+
+ if (!port || !port->gs.tty) {
+ restore_flags(flags);
+ return;
+ }
+
+ /* Get interrupts in easy to use form */
+ ints = int2status >> intshift;
+
+ /* Clear any interrupts we might be about to handle */
+ IntClear2 = int2status & (
+ (INTTYPE(UART_TX_INT) |
+ INTTYPE(UART_EMPTY_INT) |
+ INTTYPE(UART_TXOVERRUN_INT)) << intshift);
+
+ //printk("IT%03x", (int)ints);
+
+ /* TX holding register empty, so transmit byte (non-DMA) */
+ if (ints & (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT))) {
+ transmit_char_pio(port);
+ }
+
+ /* TX Transmit Holding Register Overrun (shouldn't happen) */
+ if (ints & INTTYPE(UART_TXOVERRUN_INT)) {
+ printk ( "rs: TX overrun\n");
+ }
+
+ /*
+ check_modem_status();
+ */
+
+ restore_flags(flags);
+
+/* rs_dprintk (RS_DEBUG_INTERRUPTS, "end.\n"); */
+
+}
+
+static void rs_rx_interrupt_uarta(int irq, void *dev_id,
+ struct pt_regs * regs)
+{
+ rs_rx_interrupt(irq, dev_id, regs, UARTA_SHIFT);
+}
+
+static void rs_tx_interrupt_uarta(int irq, void *dev_id,
+ struct pt_regs * regs)
+{
+ rs_tx_interrupt(irq, dev_id, regs, UARTA_SHIFT);
+}
+
+#if 0
+static void rs_interrupt_uartb(int irq, void *dev_id,
+ struct pt_regs * regs)
+{
+ rs_interrupt(irq, dev_id, regs, UARTB_SHIFT);
+}
+#endif
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+
+
+
+
+/* ********************************************************************** *
+ * Here are the routines that actually *
+ * interface with the generic_serial driver *
+ * ********************************************************************** */
+
+static void rs_disable_tx_interrupts (void * ptr)
+{
+ struct rs_port *port = ptr;
+ unsigned long flags;
+
+ save_and_cli(flags);
+ port->gs.flags &= ~GS_TX_INTEN;
+
+ IntEnable2 &= ~((INTTYPE(UART_TX_INT) |
+ INTTYPE(UART_EMPTY_INT) |
+ INTTYPE(UART_TXOVERRUN_INT)) << port->intshift);
+
+ IntClear2 = (INTTYPE(UART_TX_INT) |
+ INTTYPE(UART_EMPTY_INT) |
+ INTTYPE(UART_TXOVERRUN_INT)) << port->intshift;
+
+ restore_flags(flags);
+}
+
+
+static void rs_enable_tx_interrupts (void * ptr)
+{
+ struct rs_port *port = ptr;
+ unsigned long flags;
+
+ save_and_cli(flags);
+
+ IntClear2 = (INTTYPE(UART_TX_INT) |
+ INTTYPE(UART_EMPTY_INT) |
+ INTTYPE(UART_TXOVERRUN_INT)) << port->intshift;
+
+ IntEnable2 |= (INTTYPE(UART_TX_INT) |
+ INTTYPE(UART_EMPTY_INT) |
+ INTTYPE(UART_TXOVERRUN_INT)) << port->intshift;
+
+ /* Send a char to start TX interrupts happening */
+ transmit_char_pio(port);
+
+ restore_flags(flags);
+}
+
+
+static void rs_disable_rx_interrupts (void * ptr)
+{
+ struct rs_port *port = ptr;
+ unsigned long flags;
+
+ save_and_cli(flags);
+
+ IntEnable2 &= ~((INTTYPE(UART_RX_INT) |
+ INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_BREAK_INT) |
+ INTTYPE(UART_PARITYERR_INT)) << port->intshift);
+
+ IntClear2 = (INTTYPE(UART_RX_INT) |
+ INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_BREAK_INT) |
+ INTTYPE(UART_PARITYERR_INT)) << port->intshift;
+
+ restore_flags(flags);
+}
+
+static void rs_enable_rx_interrupts (void * ptr)
+{
+ struct rs_port *port = ptr;
+ unsigned long flags;
+
+ save_and_cli(flags);
+
+ IntEnable2 |= (INTTYPE(UART_RX_INT) |
+ INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_BREAK_INT) |
+ INTTYPE(UART_PARITYERR_INT)) << port->intshift;
+
+ /* Empty the input buffer - apparently this is *vital* */
+ while (port->base[UART_R39XX_CTRL1] & UART_RX_HOLD_FULL) {
+ serial_in(port, UART_R39XX_DATA);
+ }
+
+ IntClear2 = (INTTYPE(UART_RX_INT) |
+ INTTYPE(UART_RXOVERRUN_INT) |
+ INTTYPE(UART_FRAMEERR_INT) |
+ INTTYPE(UART_BREAK_INT) |
+ INTTYPE(UART_PARITYERR_INT)) << port->intshift;
+
+ restore_flags(flags);
+}
+
+
+static int rs_get_CD (void * ptr)
+{
+ struct rs_port *port = ptr;
+ func_enter2();
+
+ /* No Carried Detect in Hardware - just return true */
+
+ func_exit();
+ return (1);
+}
+
+
+
+
+static void rs_shutdown_port (void * ptr)
+{
+ struct rs_port *port = ptr;
+
+ func_enter();
+
+ port->gs.flags &= ~ GS_ACTIVE;
+
+ /* Jim: Disable interrupts and power down port? */
+
+ func_exit();
+}
+
+
+
+static int rs_set_real_termios (void *ptr)
+{
+ struct rs_port *port = ptr;
+ int t;
+
+ func_enter2();
+
+ switch (port->gs.baud) {
+ /* Save some typing work... */
+#define e(x) case x:t= SER_BAUD_ ## x ; break
+ e(300);e(600);e(1200);e(2400);e(4800);e(9600);
+ e(19200);e(38400);e(57600);e(76800);e(115200);e(230400);
+ case 0 :t = -1;
+ break;
+ default:
+ /* Can I return "invalid"? */
+ t = SER_BAUD_9600;
+ printk (KERN_INFO "rs: unsupported baud rate: %d.\n", port->gs.baud);
+ break;
+ }
+#undef e
+ if (t >= 0) {
+ /* Jim: Set Hardware Baud rate - there is some good
+ code in drivers/char/serial.c */
+
+ /* Program hardware for parity, data bits, stop bits (note: these are hardcoded to 8N1 */
+ UartA_Ctrl1 &= 0xf000000f;
+ UartA_Ctrl1 &= ~(UART_DIS_TXD | SER_SEVEN_BIT | SER_EVEN_PARITY | SER_TWO_STOP);
+
+#define CFLAG port->gs.tty->termios->c_cflag
+ if (C_PARENB(port->gs.tty))
+ if (!C_PARODD(port->gs.tty))
+ UartA_Ctrl1 |= SER_EVEN_PARITY;
+ else
+ UartA_Ctrl1 |= SER_ODD_PARITY;
+ if ((CFLAG & CSIZE)==CS6)
+ printk(KERN_ERR "6 bits not supported\n");
+ if ((CFLAG & CSIZE)==CS5)
+ printk(KERN_ERR "5 bits not supported\n");
+ if ((CFLAG & CSIZE)==CS7)
+ UartA_Ctrl1 |= SER_SEVEN_BIT;
+ if (C_CSTOPB(port->gs.tty))
+ UartA_Ctrl1 |= SER_TWO_STOP;
+
+ UartA_Ctrl2 = t;
+ UartA_DMActl1 = 0;
+ UartA_DMActl2 = 0;
+ UartA_Ctrl1 |= UART_ON;
+ }
+
+ /* Jim: Lots of good stuff in drivers/char/serial.c:change_speed() */
+
+ func_exit ();
+ return 0;
+}
+
+
+static int rs_chars_in_buffer (void * ptr)
+{
+ struct rs_port *port = ptr;
+ int scratch;
+/* func_enter2(); */
+
+ scratch = serial_in(port, UART_R39XX_CTRL1);
+
+/* func_exit(); */
+ return ( (scratch & UART_TX_EMPTY) ? 0 : 1 );
+}
+
+
+
+/* ********************************************************************** *
+ * Here are the routines that actually *
+ * interface with the rest of the system *
+ * ********************************************************************** */
+
+
+static int rs_open (struct tty_struct * tty, struct file * filp)
+{
+ struct rs_port *port;
+ int retval, line;
+
+ func_enter();
+
+ if (!rs_initialized) {
+ return -EIO;
+ }
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ rs_dprintk (RS_DEBUG_OPEN, "%d: opening line %d. tty=%p ctty=%p)\n",
+ (int) current->pid, line, tty, current->tty);
+
+ if ((line < 0) || (line >= RS_NPORTS))
+ return -ENODEV;
+
+ /* Pre-initialized already */
+ port = & rs_ports[line];
+
+ rs_dprintk (RS_DEBUG_OPEN, "port = %p\n", port);
+
+ tty->driver_data = port;
+ port->gs.tty = tty;
+ port->gs.count++;
+
+ rs_dprintk (RS_DEBUG_OPEN, "starting port\n");
+
+ /*
+ * Start up serial port
+ */
+ retval = gs_init_port(&port->gs);
+ rs_dprintk (RS_DEBUG_OPEN, "done gs_init\n");
+ if (retval) {
+ port->gs.count--;
+ return retval;
+ }
+
+ port->gs.flags |= GS_ACTIVE;
+
+ rs_dprintk (RS_DEBUG_OPEN, "before inc_use_count (count=%d.\n",
+ port->gs.count);
+ if (port->gs.count == 1) {
+ MOD_INC_USE_COUNT;
+ }
+ rs_dprintk (RS_DEBUG_OPEN, "after inc_use_count\n");
+
+ /* Jim: Initialize port hardware here */
+
+ /* Enable high-priority interrupts for UARTA */
+ IntEnable6 |= INT6_UARTARXINT;
+ rs_enable_rx_interrupts(&rs_ports[0]);
+
+ retval = gs_block_til_ready(&port->gs, filp);
+ rs_dprintk (RS_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n",
+ retval, port->gs.count);
+
+ if (retval) {
+ MOD_DEC_USE_COUNT;
+ port->gs.count--;
+ return retval;
+ }
+ /* tty->low_latency = 1; */
+
+ if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = port->gs.normal_termios;
+ else
+ *tty->termios = port->gs.callout_termios;
+ rs_set_real_termios (port);
+ }
+
+ port->gs.session = current->session;
+ port->gs.pgrp = current->pgrp;
+ func_exit();
+
+ /* Jim */
+/* cli(); */
+
+ return 0;
+
+}
+
+
+
+static void rs_close (void *ptr)
+{
+ func_enter ();
+
+ /* Anything to do here? */
+
+ MOD_DEC_USE_COUNT;
+ func_exit ();
+}
+
+
+/* I haven't the foggiest why the decrement use count has to happen
+ here. The whole linux serial drivers stuff needs to be redesigned.
+ My guess is that this is a hack to minimize the impact of a bug
+ elsewhere. Thinking about it some more. (try it sometime) Try
+ running minicom on a serial port that is driven by a modularized
+ driver. Have the modem hangup. Then remove the driver module. Then
+ exit minicom. I expect an "oops". -- REW */
+static void rs_hungup (void *ptr)
+{
+ func_enter ();
+ MOD_DEC_USE_COUNT;
+ func_exit ();
+}
+
+static int rs_ioctl (struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ struct rs_port *port = tty->driver_data;
+ int ival;
+
+ /* func_enter2(); */
+
+ rc = 0;
+ switch (cmd) {
+ case TIOCGSOFTCAR:
+ rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
+ (unsigned int *) arg);
+ break;
+ case TIOCSSOFTCAR:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(int))) == 0) {
+ get_user(ival, (unsigned int *) arg);
+ tty->termios->c_cflag =
+ (tty->termios->c_cflag & ~CLOCAL) |
+ (ival ? CLOCAL : 0);
+ }
+ break;
+ case TIOCGSERIAL:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_struct))) == 0)
+ gs_getserial(&port->gs, (struct serial_struct *) arg);
+ break;
+ case TIOCSSERIAL:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(struct serial_struct))) == 0)
+ rc = gs_setserial(&port->gs, (struct serial_struct *) arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ /* func_exit(); */
+ return rc;
+}
+
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct * tty, char ch)
+{
+ struct rs_port *port = (struct rs_port *)tty->driver_data;
+ func_enter ();
+
+ port->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+ rs_enable_tx_interrupts(tty);
+ }
+
+ func_exit();
+}
+
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+#ifdef RS_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ func_enter ();
+
+ if (I_IXOFF(tty))
+ rs_send_xchar(tty, STOP_CHAR(tty));
+
+ func_exit ();
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+ struct rs_port *port = (struct rs_port *)tty->driver_data;
+#ifdef RS_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ func_enter();
+
+ if (I_IXOFF(tty)) {
+ if (port->x_char)
+ port->x_char = 0;
+ else
+ rs_send_xchar(tty, START_CHAR(tty));
+ }
+
+ func_exit();
+}
+
+
+
+
+
+/* ********************************************************************** *
+ * Here are the initialization routines. *
+ * ********************************************************************** */
+
+void * ckmalloc (int size)
+{
+ void *p;
+
+ p = kmalloc(size, GFP_KERNEL);
+ if (p)
+ memset(p, 0, size);
+ return p;
+}
+
+
+
+static int rs_init_portstructs(void)
+{
+ struct rs_port *port;
+ int i;
+
+ func_enter();
+
+ /* Many drivers statically allocate the maximum number of ports
+ There is no reason not to allocate them dynamically. Is there? -- REW */
+ rs_ports = ckmalloc(RS_NPORTS * sizeof (struct rs_port));
+ if (!rs_ports)
+ return -ENOMEM;
+
+ rs_termios = ckmalloc(RS_NPORTS * sizeof (struct termios *));
+ if (!rs_termios) {
+ kfree (rs_ports);
+ return -ENOMEM;
+ }
+
+ rs_termios_locked = ckmalloc(RS_NPORTS * sizeof (struct termios *));
+ if (!rs_termios_locked) {
+ kfree (rs_ports);
+ kfree (rs_termios);
+ return -ENOMEM;
+ }
+
+ /* Adjust the values in the "driver" */
+ rs_driver.termios = rs_termios;
+ rs_driver.termios_locked = rs_termios_locked;
+
+ port = rs_ports;
+ for (i=0; i < RS_NPORTS;i++) {
+ rs_dprintk (RS_DEBUG_INIT, "initing port %d\n", i);
+ port->gs.callout_termios = tty_std_termios;
+ port->gs.normal_termios = tty_std_termios;
+ port->gs.magic = SERIAL_MAGIC;
+ port->gs.close_delay = HZ/2;
+ port->gs.closing_wait = 30 * HZ;
+ port->gs.rd = &rs_real_driver;
+#ifdef NEW_WRITE_LOCKING
+ port->gs.port_write_sem = MUTEX;
+#endif
+#ifdef DECLARE_WAITQUEUE
+ init_waitqueue_head(&port->gs.open_wait);
+ init_waitqueue_head(&port->gs.close_wait);
+#endif
+ port->base = (i == 0) ? (unsigned long *) &UartA_Ctrl1 :
+ (unsigned long *) &UartB_Ctrl1;
+ port->intshift = (i == 0) ? UARTA_SHIFT : UARTB_SHIFT;
+ rs_dprintk (RS_DEBUG_INIT, "base %p intshift %d\n",
+ port->base, port->intshift);
+ port++;
+ }
+
+ func_exit();
+ return 0;
+}
+
+static int rs_init_drivers(void)
+{
+ int error;
+
+ func_enter();
+
+ memset(&rs_driver, 0, sizeof(rs_driver));
+ rs_driver.magic = TTY_DRIVER_MAGIC;
+ rs_driver.driver_name = "serial";
+ rs_driver.name = "ttyS";
+ rs_driver.major = TTY_MAJOR;
+ rs_driver.minor_start = 64;
+ rs_driver.num = RS_NPORTS;
+ rs_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ rs_driver.subtype = SERIAL_TYPE_NORMAL;
+ rs_driver.init_termios = tty_std_termios;
+ rs_driver.init_termios.c_cflag =
+ //B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+ rs_driver.flags = TTY_DRIVER_REAL_RAW;
+ rs_driver.refcount = &rs_refcount;
+ rs_driver.table = rs_table;
+ rs_driver.termios = rs_termios;
+ rs_driver.termios_locked = rs_termios_locked;
+
+ rs_driver.open = rs_open;
+ rs_driver.close = gs_close;
+ rs_driver.write = gs_write;
+ rs_driver.put_char = gs_put_char;
+ rs_driver.flush_chars = gs_flush_chars;
+ rs_driver.write_room = gs_write_room;
+ rs_driver.chars_in_buffer = gs_chars_in_buffer;
+ rs_driver.flush_buffer = gs_flush_buffer;
+ rs_driver.ioctl = rs_ioctl;
+ rs_driver.throttle = rs_throttle;
+ rs_driver.unthrottle = rs_unthrottle;
+ rs_driver.set_termios = gs_set_termios;
+ rs_driver.stop = gs_stop;
+ rs_driver.start = gs_start;
+ rs_driver.hangup = gs_hangup;
+
+ rs_callout_driver = rs_driver;
+ rs_callout_driver.name = "cua";
+ rs_callout_driver.major = TTYAUX_MAJOR;
+ rs_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if ((error = tty_register_driver(&rs_driver))) {
+ printk(KERN_ERR "Couldn't register serial driver, error = %d\n",
+ error);
+ return 1;
+ }
+ if ((error = tty_register_driver(&rs_callout_driver))) {
+ tty_unregister_driver(&rs_driver);
+ printk(KERN_ERR "Couldn't register callout driver, error = %d\n",
+ error);
+ return 1;
+ }
+
+ func_exit();
+ return 0;
+}
+
+
+static void rs_release_drivers(void)
+{
+ func_enter();
+ tty_unregister_driver(&rs_driver);
+ tty_unregister_driver(&rs_callout_driver);
+#ifdef CONFIG_PM
+ pm_unregister(pmdev);
+#endif
+ func_exit();
+}
+
+#if defined(CONFIG_VTECH_HELIO) && defined(CONFIG_PM)
+static
+int pm_request(struct pm_dev* dev, pm_request_t req, void* data)
+{
+ static unsigned long ctrlA;
+ static unsigned long ctrlB;
+ static unsigned long clk;
+ static unsigned long out;
+
+ switch (req) {
+ case PM_SUSPEND:
+ /* disable both uarts */
+ ctrlA = UartA_Ctrl1 & (UART_ENABLE | UART_DIS_TXD);
+ UartA_Ctrl1 &= ~UART_ENABLE;
+#if 0 /* would be nice if this worked, but it hangs the suspend process for me - 20001030 nop */
+ while (UartA_Ctrl1 & UART_ON)
+ /*wait till its empty */;
+#endif
+ UartA_Ctrl1 |= UART_DIS_TXD;
+ ctrlB = UartB_Ctrl1 & (UART_ENABLE | UART_DIS_TXD);
+ UartB_Ctrl1 &= ~UART_ENABLE;
+#if 0 /* would be nice if this worked */
+ while (UartB_Ctrl1 & UART_ON)
+ /*wait till its empty */;
+#endif
+ UartB_Ctrl1 |= UART_DIS_TXD;
+
+ /* turn the clocks off */
+ clk = ClockControl & (CLK_EN_UART_A | CLK_EN_UART_B);
+ ClockControl &= ~(CLK_EN_UART_A | CLK_EN_UART_B);
+
+ /* remember the state of these pins */
+ out = MFIOOutput & (MFIO_PIN_UART_TX_ENABLE |
+ MFIO_PIN_UART_RX_DISABLE |
+ MFIO_PIN_MODEM_RTS);
+
+ MFIOOutput &= ~MFIO_PIN_UART_TX_ENABLE;
+ MFIOOutput |= (MFIO_PIN_UART_RX_DISABLE | /* Set High (Disable RX) */
+ MFIO_PIN_MODEM_RTS);
+ break;
+
+ case PM_RESUME:
+
+ /* restore the driver pin state */
+ MFIOOutput = (MFIOOutput & ~(MFIO_PIN_UART_TX_ENABLE |
+ MFIO_PIN_UART_RX_DISABLE |
+ MFIO_PIN_MODEM_RTS)) | out;
+
+ /* restore the clock */
+ ClockControl = (ClockControl & ~(CLK_EN_UART_A | CLK_EN_UART_B)) | clk;
+
+ /* restore the Uart state */
+ UartA_Ctrl1 = (UartA_Ctrl1 & ~(UART_ENABLE | UART_DIS_TXD))
+ | ctrlA;
+ UartB_Ctrl1 = (UartB_Ctrl1 & ~(UART_ENABLE | UART_DIS_TXD))
+ | ctrlB;
+ break;
+ }
+ return 0;
+}
+#endif
+
+
+int __init rs_init(void)
+{
+ int rc;
+
+ func_enter();
+ rs_dprintk (RS_DEBUG_INIT, "Initing serial module... (rs_debug=%d)\n", rs_debug);
+
+ if (abs ((long) (&rs_debug) - rs_debug) < 0x10000) {
+ printk (KERN_WARNING "rs: rs_debug is an address, instead of a value. "
+ "Assuming -1.\n");
+ printk ("(%p)\n", &rs_debug);
+ rs_debug=-1;
+ }
+
+ rc = rs_init_portstructs ();
+ rs_init_drivers ();
+ if (request_irq(2, rs_tx_interrupt_uarta, SA_SHIRQ | SA_INTERRUPT,
+ "serial", &rs_ports[0])) {
+ printk(KERN_ERR "rs: Cannot allocate irq for UARTA.\n");
+ rc = 0;
+ }
+ if (request_irq(3, rs_rx_interrupt_uarta, SA_SHIRQ | SA_INTERRUPT,
+ "serial", &rs_ports[0])) {
+ printk(KERN_ERR "rs: Cannot allocate irq for UARTA.\n");
+ rc = 0;
+ }
+
+ IntEnable6 |= INT6_UARTARXINT;
+ rs_enable_rx_interrupts(&rs_ports[0]);
+
+#ifndef CONFIG_SERIAL_CONSOLE
+ printk( "Initializing uart...\n" );
+ earlyInitUartPR31700();
+ printk( "okay\n" );
+#endif
+
+ /* Note: I didn't do anything to enable the second UART */
+
+ if (rc >= 0)
+ rs_initialized++;
+
+ func_exit();
+ return 0;
+}
+
+
+void rs_exit(void)
+{
+ func_enter();
+ rs_dprintk (RS_DEBUG_CLEANUP, "Cleaning up drivers (%d)\n", rs_initialized);
+ if (rs_initialized)
+ rs_release_drivers ();
+
+ kfree (rs_ports);
+ kfree (rs_termios);
+ kfree (rs_termios_locked);
+ func_exit();
+
+}
+
+module_init(rs_init);
+module_exit(rs_exit);
+
+
+#ifdef DEBUG
+void my_hd (unsigned char *addr, int len)
+{
+ int i, j, ch;
+
+ for (i=0;i<len;i+=16) {
+ printk ("%08x ", (int) addr+i);
+ for (j=0;j<16;j++) {
+ printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+ }
+ for (j=0;j<16;j++) {
+ ch = addr[j+i];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ }
+}
+#endif
+
+
+
+
+/*
+ *
+ * Very simple routines to get UART humming...
+ *
+ */
+
+/* not static, its called from prom_init() too */
+void earlyInitUartPR31700(void)
+{
+ /* Setup master clock for UART */
+ ClockControl &= ~CLK_SIBMCLKDIV_MASK;
+ ClockControl |= CLK_SIBMCLKDIR | CLK_ENSIBMCLK |
+ ((2 << CLK_SIBMCLKDIV_SHIFT) & CLK_SIBMCLKDIV_MASK) |
+ CLK_CSERSEL;
+
+ /* Configure UARTA clock */
+ ClockControl |= ((3 << CLK_CSERDIV_SHIFT) & CLK_CSERDIV_MASK) |
+ CLK_ENCSERCLK | CLK_EN_UART_A;
+
+ /* Setup UARTA for 115200 baud, 8N1 */
+ UartA_Ctrl1 &= 0xf000000f;
+ UartA_Ctrl1 &= ~UART_DIS_TXD; /* turn on txd */
+ UartA_Ctrl1 &= ~SER_SEVEN_BIT; /* use 8-bit data */
+ UartA_Ctrl1 &= ~SER_EVEN_PARITY; /* no parity */
+ UartA_Ctrl1 &= ~SER_TWO_STOP; /* 1 stop bit */
+ UartA_Ctrl2 = SER_BAUD_115200;
+ UartA_DMActl1 = 0; /* No DMA */
+ UartA_DMActl2 = 0; /* No DMA */
+ UartA_Ctrl1 |= UART_ON; /* Turn UART on */
+
+ while (~UartA_Ctrl1 & UART_ON);
+}
+
+void serial_outc(unsigned char c)
+{
+ int i;
+ unsigned long int2;
+ #define BUSY_WAIT 10000
+
+ /*
+ * Turn UART A Interrupts off
+ */
+ int2 = IntEnable2;
+ IntEnable2 &=
+ ~(INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY);
+
+ /*
+ * The UART_TX_EMPTY bit in UartA_Ctrl1 seems
+ * not to be very reliable :-(
+ *
+ * Wait for the Tx register to become empty
+ */
+ for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < BUSY_WAIT); i++);
+
+ IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY;
+
+ UartA_Data = c;
+ for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < BUSY_WAIT); i++);
+ IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY;
+
+ IntEnable2 = int2;
+}
+
+void serial_console_read_raw(struct console *co, char *buf, int size)
+{
+ int i;
+ unsigned int int2, flags;
+
+ save_and_cli(flags);
+
+ int2 = IntEnable2;
+ IntEnable2 = 0;
+
+ for (i=0; i<size; i++) {
+ while (!(UartA_Ctrl1 & UART_RX_HOLD_FULL))
+ ;
+ buf[i] = UartA_Data;
+ udelay(10); /* Allow things to happen - it take a while */
+ }
+ IntEnable2 = int2;
+ restore_flags(flags);
+}
+
+int serial_console_wait_key(struct console *co)
+{
+ unsigned int int2, res;
+
+ int2 = IntEnable2;
+ IntEnable2 = 0;
+
+ while (!(UartA_Ctrl1 & UART_RX_HOLD_FULL))
+ ;
+ res = UartA_Data;
+ udelay(10); /* Allow things to happen - it take a while */
+
+ IntEnable2 = int2;
+ return res;
+}
+
+/* used in slip? in Slip??? SLIP /ought/ to work with sl->tty! (PM2000) */
+void serial_console_write_raw(struct console *co, const char *s,
+ unsigned count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ serial_outc(*s++);
+ }
+}
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+void serial_console_write(struct console *co, const char *s,
+ unsigned count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ if (*s == '\n')
+ serial_outc('\r');
+ serial_outc(*s++);
+ }
+}
+
+static kdev_t serial_console_device(struct console *c)
+{
+ return MKDEV(TTY_MAJOR, 64 + c->index);
+}
+
+static __init int serial_console_setup(struct console *co, char *options)
+{
+ earlyInitUartPR31700();
+
+ return 0;
+}
+
+
+static struct console sercons = {
+ name: "ttyS",
+ write: serial_console_write,
+ device: serial_console_device,
+ wait_key: serial_console_wait_key,
+ setup: serial_console_setup,
+ flags: CON_PRINTBUFFER,
+ index: -1
+};
+
+/*
+ * Register console.
+ */
+
+void __init serial_console_init(void)
+{
+ register_console(&sercons);
+}
+
+#endif
diff --git a/arch/mips/philips/drivers/uart-pr31700.h b/arch/mips/philips/drivers/uart-pr31700.h
new file mode 100644
index 000000000..221baa89f
--- /dev/null
+++ b/arch/mips/philips/drivers/uart-pr31700.h
@@ -0,0 +1,126 @@
+/*
+ * Serial driver for r39xx
+ *
+ * Copyright (C) 2000 Jim Pick <jim@jimpick.com>
+ *
+ * Inspired by, and/or includes bits from:
+ *
+ * drivers/char/serial.c (standard serial driver)
+ * drivers/char/sx.c (use of generic_serial interface)
+ * drivers/char/esp.c (another UART that uses DMA)
+ * arch/mips/vr41xx/serial.c (another MIPS serial driver)
+ *
+ * Please see those files for credits.
+ *
+ * Also, the original rough serial console code was:
+ *
+ * Copyright (C) 1999 Harald Koerfgen
+ *
+ * $Id: r39xx_serial.h,v 1.3 2000/11/01 03:02:27 nop Exp $
+ */
+
+#include <linux/serial.h>
+#include <linux/generic_serial.h>
+
+
+/* r39xx UART Register Offsets */
+#define UART_R39XX_CTRL1 0
+#define UART_R39XX_CTRL2 1
+#define UART_R39XX_DMACTRL1 2
+#define UART_R39XX_DMACTRL2 3
+#define UART_R39XX_DMACNT 4
+#define UART_R39XX_DATA 5
+
+/* UART Interrupt (Interrupt 2) bits (UARTA,UARTB) */
+#define UART_RX_INT 9 /* receiver holding register full (31, 21) */
+#define UART_RXOVERRUN_INT 8 /* receiver overrun error (30, 20) */
+#define UART_FRAMEERR_INT 7 /* receiver frame error (29, 19) */
+#define UART_BREAK_INT 6 /* received break signal (28, 18) */
+#define UART_PARITYERR_INT 5 /* receiver parity error (27, 17) */
+#define UART_TX_INT 4 /* transmit holding register empty (26, 16) */
+#define UART_TXOVERRUN_INT 3 /* transmit overrun error (25, 15) */
+#define UART_EMPTY_INT 2 /* both trans/recv regs empty (24, 14) */
+#define UART_DMAFULL_INT 1 /* DMA at end of buffer (23, 13) */
+#define UART_DMAHALF_INT 0 /* DMA halfway through buffer */ (22, 12) */
+
+#define UARTA_SHIFT 22
+#define UARTB_SHIFT 12
+
+#define INT2BIT(interrupttype, intshift) (1 << (interrupttype + intshift))
+#define INTTYPE(interrupttype) (1 << interrupttype)
+
+/* Driver status flags */
+#define RS_STAT_RX_TIMEOUT 0x01
+#define RS_STAT_DMA_RX 0x02
+#define RS_STAT_DMA_TX 0x04
+#define RS_STAT_NEVER_DMA 0x08
+#define RS_STAT_USE_PIO 0x10
+
+/*
+ This driver can spew a whole lot of debugging output at you. If you
+ need maximum performance, you should disable the DEBUG define. To
+ aid in debugging in the field, I'm leaving the compile-time debug
+ features enabled, and disable them "runtime". That allows me to
+ instruct people with problems to enable debugging without requiring
+ them to recompile...
+*/
+#define DEBUG
+
+
+#ifdef DEBUG
+#define rs_dprintk(f, str...) if (rs_debug & f) printk (str)
+#else
+#define rs_dprintk(f, str...) /* nothing */
+#endif
+
+
+
+#define func_enter() rs_dprintk (RS_DEBUG_FLOW, "rs: enter " __FUNCTION__ "\n")
+#define func_exit() rs_dprintk (RS_DEBUG_FLOW, "rs: exit " __FUNCTION__ "\n")
+
+#define func_enter2() rs_dprintk (RS_DEBUG_FLOW, "rs: enter " __FUNCTION__ \
+ "(port %p, base %p)\n", port, port->base)
+
+
+/* Debug flags. Add these together to get more debug info. */
+
+#define RS_DEBUG_OPEN 0x00000001
+#define RS_DEBUG_SETTING 0x00000002
+#define RS_DEBUG_FLOW 0x00000004
+#define RS_DEBUG_MODEMSIGNALS 0x00000008
+#define RS_DEBUG_TERMIOS 0x00000010
+#define RS_DEBUG_TRANSMIT 0x00000020
+#define RS_DEBUG_RECEIVE 0x00000040
+#define RS_DEBUG_INTERRUPTS 0x00000080
+#define RS_DEBUG_PROBE 0x00000100
+#define RS_DEBUG_INIT 0x00000200
+#define RS_DEBUG_CLEANUP 0x00000400
+#define RS_DEBUG_CLOSE 0x00000800
+#define RS_DEBUG_FIRMWARE 0x00001000
+#define RS_DEBUG_MEMTEST 0x00002000
+#define RS_DEBUG_THROTTLE 0x00004000
+
+#define RS_DEBUG_ALL 0xffffffff
+
+
+#define RS_NPORTS 2
+
+struct rs_port {
+ /* must be first field! */
+ struct gs_port gs;
+
+ /* rest is private for this driver */
+ unsigned long *base;
+ int intshift; /* for interrupt register */
+ struct wait_queue *shutdown_wait;
+ int stat_flags;
+ struct async_icount icount; /* kernel counters for the 4
+ input interrupts */
+ int read_status_mask;
+ int ignore_status_mask;
+ int x_char; /* xon/xoff character */
+};
+
+
+
+#define SERIAL_MAGIC 0x5301
diff --git a/arch/mips/philips/nino/.cvsignore b/arch/mips/philips/nino/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/arch/mips/philips/nino/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/arch/mips/philips/nino/Makefile b/arch/mips/philips/nino/Makefile
new file mode 100644
index 000000000..7d31a66fb
--- /dev/null
+++ b/arch/mips/philips/nino/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for the Philips Nino specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+.S.s:
+ $(CPP) $(AFLAGS) $< -o $@
+.S.o:
+ $(CC) $(AFLAGS) -c $< -o $@
+
+O_TARGET := nino.o
+
+all: nino.o
+
+obj-y := int-handler.o setup.o irq.o time.o reset.o rtc.o prom.o power.o wbflush.o
+
+obj-$(CONFIG_BLK_DEV_INITRD) += ../boot/ramdisk.o
+
+obj-$(CONFIG_REMOTE_DEBUG) += kgdb.o
+
+int-handler.o: int-handler.S
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/philips/nino/int-handler.S b/arch/mips/philips/nino/int-handler.S
new file mode 100644
index 000000000..90d7f8818
--- /dev/null
+++ b/arch/mips/philips/nino/int-handler.S
@@ -0,0 +1,138 @@
+/*
+ * linux/arch/mips/philips/nino/int-handler.S
+ *
+ * Copyright (C) 1999 Harald Koerfgen (Harald.Koerfgen@home.ivm.de)
+ * Copyright (C) 2000 Jim Pick (jim@jimpick.com)
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Interrupt handler for PR31700.
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/philips/pr31700.h>
+
+
+ .data
+ .globl HighPriVect
+
+HighPriVect: .word spurious # Reserved
+ .word io_posnegint0 # IOPOSINT(0) or IONEGINT(0)
+ .word spurious # CHIDMACNTINT
+ .word spurious # TELDMACNTINT
+ .word spurious # SNDDMACNTINT
+ .word spurious # Reserved
+ .word io_negint56 # IONEGINT(6) or IONEGINT(5)
+ .word spurious # Reserved
+ .word io_posint56 # IOPOSINT(6) or IOPOSINT(5)
+ .word spurious # Reserved
+ .word spurious # UARTBRXINT
+ .word uarta_rx # UARTARXINT
+ .word spurious # Reserved
+ .word periodic_timer # PERINT
+ .word spurious # ALARMINT
+ .word spurious # POSPWROKINT or NEGPWROKINT
+
+/*
+ * Here is the entry point to handle all interrupts.
+ */
+ .text
+ .set noreorder
+ .align 5
+ NESTED(nino_handle_int, PT_SIZE, ra)
+ .set noat
+ SAVE_ALL
+ CLI
+ .set at
+
+ /*
+ * Get pending Interrupts
+ */
+ mfc0 t0, CP0_CAUSE # Get pending interrupts
+ andi t2, t0, IE_IRQ4 # IRQ4 (high priority)
+ bne t2, IE_IRQ4, low_priority
+ nop
+
+/*
+ * Ok, we've got a high priority interrupt (a.k.a. an external interrupt).
+ * Read Interrupt Status Register 6 to get vector.
+ */
+high_priority:
+ lui t0, %hi(IntStatus6)
+ lw t1, %lo(IntStatus6)(t0)
+ andi t1, INT6_INTVECT
+ la t2, HighPriVect
+ addu t1, t1, t2
+ lw t2, 0(t1)
+ jr t2
+ nop
+
+/*
+ * Ok, we've got one of over a hundred other interupts.
+ */
+low_priority:
+ lui t0, %hi(IntStatus1)
+ lw t1, %lo(IntStatus1)(t0)
+ j handle_it
+ li a0, 20
+
+/*
+ * We don't currently handle spurious interrupts.
+ */
+spurious:
+ j spurious_interrupt
+ nop
+
+/*
+ * We have the IRQ number, dispatch to the real handler.
+ */
+handle_it: jal do_IRQ
+ move a1,sp
+ j ret_from_irq
+ nop
+
+/************************************
+ * High priority interrupt mappings *
+ ************************************/
+
+/*
+ * Periodic timer - IRQ 0
+ */
+periodic_timer:
+ j handle_it
+ li a0, 0
+
+/*
+ * UARTA RX - IRQ 3
+ */
+uarta_rx:
+ j handle_it
+ li a0, 3
+
+/*
+ * GPIO Pin 0 transition - IRQ 10
+ */
+io_posnegint0:
+ j handle_it
+ li a0, 10
+
+/*
+ * GPIO Pin 5 or 6 transition (0-to-1) - IRQ 11
+ */
+io_posint56:
+ j handle_it
+ li a0, 11
+
+/*
+ * GPIO Pin 5 or 6 transition (1-to-0) - IRQ 12
+ */
+io_negint56:
+ j handle_it
+ li a0, 12
+
+ END(nino_handle_int)
diff --git a/arch/mips/philips/nino/irq.c b/arch/mips/philips/nino/irq.c
new file mode 100644
index 000000000..71aba8599
--- /dev/null
+++ b/arch/mips/philips/nino/irq.c
@@ -0,0 +1,304 @@
+/*
+ * linux/arch/mips/philips/nino/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Copyright (C) 1999 Harald Koerfgen (Harald.Koerfgen@home.ivm.de)
+ * Copyright (C) 2000 Pavel Machek (pavel@suse.cz)
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Generic interrupt handler for PR31700.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/philips/pr31700.h>
+
+unsigned long spurious_count = 0;
+
+irq_cpustat_t irq_stat [NR_CPUS];
+
+static inline void mask_irq(unsigned int irq_nr)
+{
+ switch (irq_nr) {
+ case 0: /* Periodic Timer Interrupt */
+ IntClear5 = INT5_PERIODICINT;
+ IntClear6 = INT6_PERIODICINT;
+ IntEnable6 &= ~INT6_PERIODICINT;
+ break;
+
+ case 3: /* Serial port receive interrupt */
+ break;
+
+ case 2: /* Serial port transmit interrupt */
+ break;
+
+ default:
+ printk( "Attempt to mask unknown IRQ %d?\n", irq_nr );
+ }
+}
+
+static inline void unmask_irq(unsigned int irq_nr)
+{
+ switch (irq_nr) {
+ case 0:
+ IntEnable6 |= INT6_PERIODICINT;
+ break;
+
+ case 3: /* Serial port receive interrupt */
+ /* FIXME: currently handled in driver */
+ break;
+
+ case 2: /* Serial port transmit interrupt */
+ /* FIXME: currently handled in driver */
+ break;
+
+ default:
+ printk( "Attempt to unmask unknown IRQ %d?\n", irq_nr );
+ }
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ mask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ unmask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+/*
+ * Pointers to the low-level handlers: first the general ones, then the
+ * fast ones, then the bad ones.
+ */
+extern void interrupt(void);
+
+static struct irqaction *irq_action[NR_IRQS] =
+{
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ struct irqaction *action;
+
+ for (i = 0; i < NR_IRQS; i++) {
+ action = irq_action[i];
+ if (!action)
+ continue;
+ len += sprintf(buf + len, "%2d: %8d %c %s",
+ i, kstat.irqs[0][i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ for (action = action->next; action; action = action->next) {
+ len += sprintf(buf + len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ len += sprintf(buf + len, "\n");
+ }
+ return len;
+}
+
+atomic_t __mips_bh_counter;
+
+/*
+ * do_IRQ handles IRQ's that have been installed without the
+ * SA_INTERRUPT flag: it uses the full signal-handling return
+ * and runs with other interrupts enabled. All relatively slow
+ * IRQ's should use this format: notably the keyboard/timer
+ * routines.
+ */
+asmlinkage void do_IRQ(int irq, struct pt_regs *regs)
+{
+ struct irqaction *action;
+ int do_random, cpu;
+
+ cpu = smp_processor_id();
+ irq_enter(cpu, irq);
+ kstat.irqs[cpu][irq]++;
+
+ if (irq == 20) {
+ printk("20 %08lx %08lx\n %08lx %08lx\n %08lx\n",
+ IntStatus1, IntStatus2, IntStatus3,
+ IntStatus4, IntStatus5 );
+ printk("20 %08lx %08lx\n %08lx %08lx\n %08lx\n",
+ IntEnable1, IntEnable2, IntEnable3,
+ IntEnable4, IntEnable5 );
+
+ }
+
+ mask_irq(irq);
+ action = *(irq + irq_action);
+ if (action) {
+ if (!(action->flags & SA_INTERRUPT))
+ __sti();
+ do_random = 0;
+ do {
+ do_random |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ if (do_random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ unmask_irq(irq);
+ __cli();
+ } else {
+ IntClear3 = ~0;
+ IntClear4 = ~0;
+ IntClear5 = ~0;
+ unmask_irq(irq);
+ }
+ irq_exit(cpu, irq);
+
+ /* unmasking and bottom half handling is done magically for us. */
+}
+
+/*
+ * Idea is to put all interrupts
+ * in a single table and differenciate them just by number.
+ */
+int setup_nino_irq(int irq, struct irqaction *new)
+{
+ int shared = 0;
+ struct irqaction *old, **p;
+ unsigned long flags;
+
+ p = irq_action + irq;
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ))
+ return -EBUSY;
+
+ /* Can't share interrupts unless both are same type */
+ if ((old->flags ^ new->flags) & SA_INTERRUPT)
+ return -EBUSY;
+
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ shared = 1;
+ }
+ if (new->flags & SA_SAMPLE_RANDOM)
+ rand_initialize_irq(irq);
+
+ save_and_cli(flags);
+ *p = new;
+
+ if (!shared) {
+ unmask_irq(irq);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+int request_irq(unsigned int irq,
+ void (*handler) (int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char *devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction *action;
+
+ if (irq >= NR_IRQS)
+ return -EINVAL;
+ if (!handler)
+ return -EINVAL;
+
+ action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_nino_irq(irq, action);
+
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction *action, **p;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS) {
+ printk(KERN_CRIT __FUNCTION__ ": trying to free IRQ%d\n", irq);
+ return;
+ }
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now free it */
+ save_and_cli(flags);
+ *p = action->next;
+ if (!irq[irq_action])
+ mask_irq(irq);
+ restore_flags(flags);
+ kfree(action);
+ return;
+ }
+ printk(KERN_CRIT __FUNCTION__ ": trying to free free IRQ%d\n", irq);
+}
+
+unsigned long probe_irq_on(void)
+{
+ /* TODO */
+ return 0;
+}
+
+int probe_irq_off(unsigned long irqs)
+{
+ /* TODO */
+ return 0;
+}
+
+void __init init_IRQ(void)
+{
+ irq_setup();
+}
diff --git a/arch/mips/philips/nino/kgdb.c b/arch/mips/philips/nino/kgdb.c
new file mode 100644
index 000000000..0bb03e86f
--- /dev/null
+++ b/arch/mips/philips/nino/kgdb.c
@@ -0,0 +1,83 @@
+/*
+ * linux/arch/mips/philips/nino/kgdb.c
+ *
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Low level functions for remote debugging on PR31700.
+ */
+#include <asm/system.h>
+#include <asm/philips/pr31700.h>
+
+static int remoteDebugInitialized = 0;
+
+void debugInit(void)
+{
+/*
+ * If low-level debugging (before GDB or console operational) is
+ * configured, then we do not need to re-initialize the UART.
+ */
+#ifndef CONFIG_DEBUG_LL
+ earlyInitUartPR31700();
+#endif
+}
+
+char getDebugChar(void)
+{
+ char buf;
+ unsigned long int2, flags;
+
+ if (!remoteDebugInitialized) {
+ debugInit();
+ remoteDebugInitialized = 1;
+ }
+
+ save_and_cli(flags);
+
+ int2 = IntEnable2;
+
+ IntEnable2 = 0;
+
+ while(!(UartA_Ctrl1 & UART_RX_HOLD_FULL));
+
+ buf = UartA_Data;
+
+ IntEnable2 = int2;
+
+ restore_flags(flags);
+
+ return buf;
+}
+
+int putDebugChar(char c)
+{
+ int i;
+ unsigned long int2;
+
+ if (!remoteDebugInitialized) {
+ debugInit();
+ remoteDebugInitialized = 1;
+ }
+
+ int2 = IntEnable2;
+
+ IntEnable2 &=
+ ~(INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY);
+
+ for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < 10000); i++);
+
+ IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY;
+
+ UartA_Data = c;
+
+ for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < 10000); i++);
+
+ IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY;
+
+ IntEnable2 = int2;
+
+ return 1;
+}
diff --git a/arch/mips/philips/nino/power.c b/arch/mips/philips/nino/power.c
new file mode 100644
index 000000000..907b134b7
--- /dev/null
+++ b/arch/mips/philips/nino/power.c
@@ -0,0 +1,36 @@
+/*
+ * linux/arch/mips/philips/nino/power.c
+ *
+ * Copyright (C) 2000 Jim Pick <jim@jimpick.com>
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Routines for power management on the Nino.
+ */
+#include <asm/philips/pr31700.h>
+
+void nino_wait(void)
+{
+ /* We stop the CPU to conserve power */
+ PowerControl |= PWR_STOPCPU;
+
+ /*
+ * We wait until an interrupt happens...
+ */
+
+ /* We resume here */
+ PowerControl &= ~PWR_STOPCPU;
+
+ /* Give ourselves a little delay */
+ __asm__ __volatile__(
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t");
+}
diff --git a/arch/mips/philips/nino/prom.c b/arch/mips/philips/nino/prom.c
new file mode 100644
index 000000000..bb4582099
--- /dev/null
+++ b/arch/mips/philips/nino/prom.c
@@ -0,0 +1,83 @@
+/*
+ * linux/arch/mips/philips-hpc/nino/prom.c
+ *
+ * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Early initialization code for the Nino.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/bootinfo.h>
+
+char arcs_cmdline[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_LL_DEBUG
+extern void init_uart(void);
+
+int __init prom_printf(const char * fmt, ...)
+{
+ extern void serial_outc(char);
+ static char buf[1024];
+ va_list args;
+ char c;
+ int i = 0;
+
+ /*
+ * Printing messages via serial port
+ */
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+
+ for (i = 0; buf[i] != '\0'; i++) {
+ c = buf[i];
+ if (c == '\n')
+ serial_outc('\r');
+ serial_outc(c);
+ }
+
+ return i;
+}
+#endif
+
+/* Do basic initialization */
+void __init prom_init(int argc, char **argv,
+ unsigned long magic, int *prom_vec)
+{
+ int i;
+
+ /*
+ * collect args and prepare cmd_line
+ */
+ for (i = 1; i < argc; i++) {
+ strcat(arcs_cmdline, argv[i]);
+ if (i < (argc - 1))
+ strcat(arcs_cmdline, " ");
+ }
+
+#ifdef CONFIG_LL_DEBUG
+ earlyInitUartPR31700();
+#endif
+
+ mips_machgroup = MACH_GROUP_PHILIPS;
+ mips_machtype = MACH_PHILIPS_NINO;
+
+ /* Add memory region */
+#ifdef CONFIG_NINO_4MB
+ add_memory_region(0, 4 << 20, BOOT_MEM_RAM);
+#elif CONFIG_NINO_8MB
+ add_memory_region(0, 8 << 20, BOOT_MEM_RAM);
+#elif CONFIG_NINO_16MB
+ add_memory_region(0, 16 << 20, BOOT_MEM_RAM);
+#endif
+}
+
+void __init prom_free_prom_memory (void)
+{
+}
diff --git a/arch/mips/philips/nino/reset.c b/arch/mips/philips/nino/reset.c
new file mode 100644
index 000000000..c809b6b93
--- /dev/null
+++ b/arch/mips/philips/nino/reset.c
@@ -0,0 +1,37 @@
+/*
+ * linux/arch/mips/philips/nino/reset.c
+ *
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Generic restart, halt and power off functions.
+ */
+#include <linux/init.h>
+#include <asm/reboot.h>
+
+void (*reset_vector)(void) = (void (*)(void)) 0xBFC00000;
+
+void nino_machine_restart(char *command)
+{
+ reset_vector();
+}
+
+void nino_machine_halt(void)
+{
+ reset_vector();
+}
+
+void nino_machine_power_off(void)
+{
+ reset_vector();
+}
+
+void __init setup_nino_reset_vectors(void)
+{
+ _machine_restart = nino_machine_restart;
+ _machine_halt = nino_machine_halt;
+ _machine_power_off = nino_machine_power_off;
+}
diff --git a/arch/mips/philips/nino/rtc.c b/arch/mips/philips/nino/rtc.c
new file mode 100644
index 000000000..f1127fae0
--- /dev/null
+++ b/arch/mips/philips/nino/rtc.c
@@ -0,0 +1,34 @@
+/*
+ * linux/arch/mips/philips-mobile/nino/rtc.c
+ *
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Functions to access the RTC on the PR31700 chip.
+ */
+#include <linux/spinlock.h>
+#include <linux/mc146818rtc.h>
+
+static unsigned char nino_rtc_read_data(unsigned long addr)
+{
+ return 0;
+}
+
+static void nino_rtc_write_data(unsigned char data, unsigned long addr)
+{
+}
+
+static int nino_rtc_bcd_mode(void)
+{
+ return 0;
+}
+
+struct rtc_ops nino_rtc_ops =
+{
+ &nino_rtc_read_data,
+ &nino_rtc_write_data,
+ &nino_rtc_bcd_mode
+};
diff --git a/arch/mips/philips/nino/setup.c b/arch/mips/philips/nino/setup.c
new file mode 100644
index 000000000..7ac6cdf06
--- /dev/null
+++ b/arch/mips/philips/nino/setup.c
@@ -0,0 +1,117 @@
+/*
+ * linux/arch/mips/philips/nino/setup.c
+ *
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Interrupt and exception initialization for PR31700.
+ */
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mc146818rtc.h>
+#include <linux/sched.h>
+#include <asm/addrspace.h>
+#include <asm/gdb-stub.h>
+#include <asm/irq.h>
+#include <asm/wbflush.h>
+#include <asm/philips/pr31700.h>
+
+extern struct rtc_ops nino_rtc_ops;
+
+extern void nino_wait(void);
+extern void setup_nino_reset_vectors(void);
+extern asmlinkage void nino_handle_int(void);
+extern int setup_nino_irq(int, struct irqaction *);
+void (*board_time_init) (struct irqaction * irq);
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern void set_debug_traps(void);
+extern void breakpoint(void);
+static int remote_debug = 0;
+#endif
+
+static void __init nino_irq_setup(void)
+{
+ unsigned int tmp;
+
+ /* Turn all interrupts off */
+ IntEnable1 = 0;
+ IntEnable2 = 0;
+ IntEnable3 = 0;
+ IntEnable4 = 0;
+ IntEnable5 = 0;
+ IntEnable6 = 0;
+
+ /* Clear all interrupts */
+ IntClear1 = 0xffffffff;
+ IntClear2 = 0xffffffff;
+ IntClear3 = 0xffffffff;
+ IntClear4 = 0xffffffff;
+ IntClear5 = 0xffffffff;
+ IntClear6 = 0xffffffff;
+
+ /*
+ * Enable only the interrupts for the UART and negative
+ * edge (1-to-0) triggered multi-function I/O pins.
+ */
+ set_cp0_status(ST0_BEV, 0);
+ tmp = read_32bit_cp0_register(CP0_STATUS);
+ set_cp0_status(ST0_IM, tmp | IE_IRQ2 | IE_IRQ4);
+
+ /* Register the global interrupt handler */
+ set_except_vector(0, nino_handle_int);
+
+#ifdef CONFIG_REMOTE_DEBUG
+ if (remote_debug) {
+ set_debug_traps();
+ breakpoint();
+ }
+#endif
+}
+
+static __init void nino_time_init(struct irqaction *irq)
+{
+ /*
+ * Enable periodic interrupts
+ */
+ setup_nino_irq(0, irq);
+
+ RTCperiodTimer = PER_TIMER_COUNT;
+ RTCtimerControl = TIM_ENPERTIMER;
+ IntEnable5 |= INT5_PERIODICINT;
+ ClockControl |= CLK_ENTIMERCLK;
+
+ /* Enable all interrupts */
+ IntEnable6 |= INT6_GLOBALEN | INT6_PERIODICINT;
+}
+
+void __init nino_setup(void)
+{
+ irq_setup = nino_irq_setup;
+
+ board_time_init = nino_time_init;
+
+ /* Base address to use for PC type I/O accesses */
+ mips_io_port_base = KSEG1ADDR(0x08000000);
+
+ setup_nino_reset_vectors();
+
+ /* Function called during process idle (cpu_idle) */
+ cpu_wait = nino_wait;
+
+#ifdef CONFIG_FB
+ conswitchp = &dummy_con;
+#endif
+
+#ifdef CONFIG_REMOTE_DEBUG
+ remote_debug = 1;
+#endif
+
+ rtc_ops = &nino_rtc_ops;
+
+ wbflush_setup();
+}
diff --git a/arch/mips/philips/nino/time.c b/arch/mips/philips/nino/time.c
new file mode 100644
index 000000000..b3bc87e7e
--- /dev/null
+++ b/arch/mips/philips/nino/time.c
@@ -0,0 +1,216 @@
+/*
+ * linux/arch/mips/philips/nino/time.c
+ *
+ * Copyright (C) 1999 Harald Koerfgen (Harald.Koerfgen@home.ivm.de)
+ * Copyright (C) 2000 Pavel Machek (pavel@suse.cz)
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Time handling details for PR31700.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/delay.h>
+#include <asm/philips/pr31700.h>
+
+extern volatile unsigned long wall_jiffies;
+extern rwlock_t xtime_lock;
+
+static struct timeval xbase;
+
+#define USECS_PER_JIFFY (1000000/HZ)
+
+/*
+ * Poll the Interrupt Status Registers
+ */
+#undef POLL_STATUS
+
+static unsigned long do_gettimeoffset(void)
+{
+ /*
+ * This is a kludge
+ */
+ return 0;
+}
+
+static
+void inline readRTC(unsigned long *high, unsigned long *low)
+{
+ /* read twice, and keep reading till we find two
+ * the same pairs. This is needed in case the RTC
+ * was updating its registers and we read a old
+ * High but a new Low. */
+ do {
+ *high = RTChigh & RTC_HIGHMASK;
+ *low = RTClow;
+ } while (*high != (RTChigh & RTC_HIGHMASK) || RTClow!=*low);
+}
+
+/*
+ * This version of gettimeofday has near millisecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ unsigned long high, low;
+
+ read_lock_irqsave(&xtime_lock, flags);
+ // 40 bit RTC, driven by 32khz source:
+ // +-----------+-----------------------------------------+
+ // | HHHH.HHHH | LLLL.LLLL.LLLL.LLLL.LMMM.MMMM.MMMM.MMMM |
+ // +-----------+-----------------------------------------+
+ readRTC(&high,&low);
+ tv->tv_sec = (high << 17) | (low >> 15);
+ tv->tv_usec = (low % 32768) * 1953 / 64;
+ tv->tv_sec += xbase.tv_sec;
+ tv->tv_usec += xbase.tv_usec;
+
+ tv->tv_usec += do_gettimeoffset();
+
+ /*
+ * xtime is atomically updated in timer_bh. lost_ticks is
+ * nonzero if the timer bottom half hasnt executed yet.
+ */
+ if (jiffies - wall_jiffies)
+ tv->tv_usec += USECS_PER_JIFFY;
+
+ read_unlock_irqrestore(&xtime_lock, flags);
+
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+ write_lock_irq(&xtime_lock);
+ /* This is revolting. We need to set the xtime.tv_usec
+ * correctly. However, the value in this location is
+ * is value at the last tick.
+ * Discover what correction gettimeofday
+ * would have done, and then undo it!
+ */
+ tv->tv_usec -= do_gettimeoffset();
+
+ if (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec--;
+ }
+
+ /* reset RTC to 0 (real time is xbase + RTC) */
+ xbase = *tv;
+ RTCtimerControl |= TIM_RTCCLEAR;
+ RTCtimerControl &= ~TIM_RTCCLEAR;
+ RTCalarmHigh = RTCalarmLow = ~0UL;
+
+ xtime = *tv;
+ time_state = TIME_BAD;
+ time_maxerror = MAXPHASE;
+ time_esterror = MAXPHASE;
+ write_unlock_irq(&xtime_lock);
+}
+
+static int set_rtc_mmss(unsigned long nowtime)
+{
+ int retval = 0;
+
+ return retval;
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+
+int do_write = 1;
+
+static void
+timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+#ifdef POLL_STATUS
+ static unsigned long old_IntStatus1 = 0;
+ static unsigned long old_IntStatus3 = 0;
+ static unsigned long old_IntStatus4 = 0;
+ static unsigned long old_IntStatus5 = 0;
+ static int counter = 0;
+ int i;
+
+ new_spircv = SPIData & 0xff;
+ if ((old_spircv != new_spircv) && (new_spircv != 0xff)) {
+ printk( "SPIData changed: %x\n", new_spircv );
+ }
+ old_spircv = new_spircv;
+ if (do_write)
+ SPIData = 0;
+#endif
+
+ if (!user_mode(regs)) {
+ if (prof_buffer && current->pid) {
+ extern int _stext;
+ unsigned long pc = regs->cp0_epc;
+
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+ /*
+ * Dont ignore out-of-bounds pc values silently,
+ * put them into the last histogram slot, so if
+ * present, they will show up as a sharp peak.
+ */
+ if (pc > prof_len - 1)
+ pc = prof_len - 1;
+ atomic_inc((atomic_t *) & prof_buffer[pc]);
+ }
+ }
+
+ /*
+ * aaaand... action!
+ */
+ do_timer(regs);
+
+ /*
+ * If we have an externally syncronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+ if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+ xtime.tv_usec > 500000 - (tick >> 1) &&
+ xtime.tv_usec < 500000 + (tick >> 1))
+ {
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
+}
+
+static struct irqaction irq0 = {timer_interrupt, SA_INTERRUPT, 0,
+ "timer", NULL, NULL};
+
+void (*board_time_init) (struct irqaction * irq);
+
+int __init time_init(void)
+{
+ struct timeval starttime;
+
+ starttime.tv_sec = mktime(2000, 1, 1, 0, 0, 0);
+ starttime.tv_usec = 0;
+ do_settimeofday(&starttime);
+
+ board_time_init(&irq0);
+
+ return 0;
+}
diff --git a/arch/mips/philips/nino/wbflush.c b/arch/mips/philips/nino/wbflush.c
new file mode 100644
index 000000000..2b1056259
--- /dev/null
+++ b/arch/mips/philips/nino/wbflush.c
@@ -0,0 +1,36 @@
+/*
+ * linux/arch/mips/philips/nino/wbflush.c
+ *
+ * Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Function to flush the write buffer on the PR31700 chip.
+ */
+#include <linux/init.h>
+
+void (*__wbflush) (void);
+
+void nino_wbflush(void)
+{
+ /*
+ * The status of the writeback buffer is available
+ * via the Coprocessor 0 condition
+ */
+ __asm__ __volatile__(
+ ".set\tpush\n\t"
+ ".set\tnoreorder\n\t"
+ ".set\tmips3\n\t"
+ "sync\n\t"
+ "nop\n\t"
+ "1:\tbc0f\t1b\n\t"
+ "nop\n\t"
+ ".set\tpop");
+}
+
+void __init wbflush_setup(void)
+{
+ __wbflush = nino_wbflush;
+}