diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-02-22 04:12:11 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-02-22 04:12:11 +0000 |
commit | c884ada29ead83a2777c4097489dc4db26419624 (patch) | |
tree | 98bc5e293200d703cb6d9b47ab6f052dfaba0041 /arch/mips/philips | |
parent | 95b2d612949328e196b54c7569324e22f98c6ec0 (diff) |
Support for Phillips PDAs.
Diffstat (limited to 'arch/mips/philips')
-rw-r--r-- | arch/mips/philips/drivers/.cvsignore | 2 | ||||
-rw-r--r-- | arch/mips/philips/drivers/Makefile | 20 | ||||
-rw-r--r-- | arch/mips/philips/drivers/generic_serial.c | 1078 | ||||
-rw-r--r-- | arch/mips/philips/drivers/uart-pr31700.c | 1313 | ||||
-rw-r--r-- | arch/mips/philips/drivers/uart-pr31700.h | 126 | ||||
-rw-r--r-- | arch/mips/philips/nino/.cvsignore | 2 | ||||
-rw-r--r-- | arch/mips/philips/nino/Makefile | 26 | ||||
-rw-r--r-- | arch/mips/philips/nino/int-handler.S | 138 | ||||
-rw-r--r-- | arch/mips/philips/nino/irq.c | 304 | ||||
-rw-r--r-- | arch/mips/philips/nino/kgdb.c | 83 | ||||
-rw-r--r-- | arch/mips/philips/nino/power.c | 36 | ||||
-rw-r--r-- | arch/mips/philips/nino/prom.c | 83 | ||||
-rw-r--r-- | arch/mips/philips/nino/reset.c | 37 | ||||
-rw-r--r-- | arch/mips/philips/nino/rtc.c | 34 | ||||
-rw-r--r-- | arch/mips/philips/nino/setup.c | 117 | ||||
-rw-r--r-- | arch/mips/philips/nino/time.c | 216 | ||||
-rw-r--r-- | arch/mips/philips/nino/wbflush.c | 36 |
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*)(¤t->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; +} |