diff options
Diffstat (limited to 'drivers/staging/generic_serial')
52 files changed, 20315 insertions, 0 deletions
diff --git a/drivers/staging/generic_serial/Kconfig b/drivers/staging/generic_serial/Kconfig new file mode 100644 index 0000000..795daea --- /dev/null +++ b/drivers/staging/generic_serial/Kconfig @@ -0,0 +1,45 @@ +config A2232 + tristate "Commodore A2232 serial support (EXPERIMENTAL)" + depends on EXPERIMENTAL && ZORRO && BROKEN + ---help--- + This option supports the 2232 7-port serial card shipped with the + Amiga 2000 and other Zorro-bus machines, dating from 1989. At + a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip + each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The + ports were connected with 8 pin DIN connectors on the card bracket, + for which 8 pin to DB25 adapters were supplied. The card also had + jumpers internally to toggle various pinning configurations. + + This driver can be built as a module; but then "generic_serial" + will also be built as a module. This has to be loaded before + "ser_a2232". If you want to do this, answer M here. + +config SX + tristate "Specialix SX (and SI) card support" + depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) && BROKEN + help + This is a driver for the SX and SI multiport serial cards. + Please read the file <file:Documentation/serial/sx.txt> for details. + + This driver can only be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called sx. If you want to do that, say M here. + +config RIO + tristate "Specialix RIO system support" + depends on SERIAL_NONSTANDARD && BROKEN + help + This is a driver for the Specialix RIO, a smart serial card which + drives an outboard box that can support up to 128 ports. Product + information is at <http://www.perle.com/support/documentation.html#multiport>. + There are both ISA and PCI versions. + +config RIO_OLDPCI + bool "Support really old RIO/PCI cards" + depends on RIO + help + Older RIO PCI cards need some initialization-time configuration to + determine the IRQ and some control addresses. If you have a RIO and + this doesn't seem to work, try setting this to Y. + + diff --git a/drivers/staging/generic_serial/Makefile b/drivers/staging/generic_serial/Makefile new file mode 100644 index 0000000..ffc90c8 --- /dev/null +++ b/drivers/staging/generic_serial/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o +obj-$(CONFIG_SX) += sx.o generic_serial.o +obj-$(CONFIG_RIO) += rio/ generic_serial.o diff --git a/drivers/staging/generic_serial/TODO b/drivers/staging/generic_serial/TODO new file mode 100644 index 0000000..8875645 --- /dev/null +++ b/drivers/staging/generic_serial/TODO @@ -0,0 +1,6 @@ +These are a few tty/serial drivers that either do not build, +or work if they do build, or if they seem to work, are for obsolete +hardware, or are full of unfixable races and no one uses them anymore. + +If no one steps up to adopt any of these drivers, they will be removed +in the 2.6.41 release. diff --git a/drivers/staging/generic_serial/generic_serial.c b/drivers/staging/generic_serial/generic_serial.c new file mode 100644 index 0000000..466988d --- /dev/null +++ b/drivers/staging/generic_serial/generic_serial.c @@ -0,0 +1,844 @@ +/* + * 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/sched.h> +#include <linux/serial.h> +#include <linux/mm.h> +#include <linux/generic_serial.h> +#include <linux/interrupt.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/gfp.h> +#include <asm/uaccess.h> + +#define DEBUG + +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 %s\n", __func__) +#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __func__) + +#define RS_EVENT_WRITE_WAKEUP 1 + +module_param(gs_debug, int, 0644); + + +int gs_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct gs_port *port; + + func_enter (); + + port = tty->driver_data; + + if (!port) return 0; + + if (! (port->port.flags & ASYNC_INITIALIZED)) return 0; + + /* Take a lock on the serial tranmit buffer! */ + mutex_lock(& port->port_write_mutex); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + /* Sorry, buffer is full, drop character. Update statistics???? -- REW */ + mutex_unlock(&port->port_write_mutex); + return 0; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; /* Characters in buffer */ + + mutex_unlock(&port->port_write_mutex); + func_exit (); + return 1; +} + + +/* +> 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, + const unsigned char *buf, int count) +{ + struct gs_port *port; + int c, total = 0; + int t; + + func_enter (); + + port = tty->driver_data; + + if (!port) return 0; + + if (! (port->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 */ + mutex_lock(& port->port_write_mutex); + + while (1) { + + c = count; + + /* This is safe because we "OWN" the "head". Noone else can + change the "head": we own the port_write_mutex. */ + /* 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; + + 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; + } + mutex_unlock(& port->port_write_mutex); + + gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", + (port->port.flags & GS_TX_INTEN)?"enabled": "disabled"); + + if (port->xmit_cnt && + !tty->stopped && + !tty->hw_stopped && + !(port->port.flags & GS_TX_INTEN)) { + port->port.flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + } + func_exit (); + return total; +} + + + +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; +} + + +static int gs_real_chars_in_buffer(struct tty_struct *tty) +{ + struct gs_port *port; + func_enter (); + + 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, unsigned long timeout) +{ + struct gs_port *port = ptr; + unsigned long end_jiffies; + int jiffies_to_transmit, charsleft = 0, rv = 0; + int 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->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->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 */ + if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT; + 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); + + /* the expression is actually jiffies < end_jiffies, but that won't + work around the wraparound. Tricky eh? */ + while ((charsleft = gs_real_chars_in_buffer (port->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); + + msleep_interruptible(jiffies_to_msecs(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 (); + + port = tty->driver_data; + + if (!port) return; + + /* XXX Would the write semaphore do? */ + spin_lock_irqsave (&port->driver_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore (&port->driver_lock, flags); + + tty_wakeup(tty); + func_exit (); +} + + +void gs_flush_chars(struct tty_struct * tty) +{ + struct gs_port *port; + + func_enter (); + + 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->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 (); + + port = tty->driver_data; + + if (!port) return; + + if (port->xmit_cnt && + port->xmit_buf && + (port->port.flags & GS_TX_INTEN) ) { + port->port.flags &= ~GS_TX_INTEN; + port->rd->disable_tx_interrupts (port); + } + func_exit (); +} + + +void gs_start(struct tty_struct * tty) +{ + struct gs_port *port; + + port = tty->driver_data; + + if (!port) return; + + if (port->xmit_cnt && + port->xmit_buf && + !(port->port.flags & GS_TX_INTEN) ) { + port->port.flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + } + func_exit (); +} + + +static void gs_shutdown_port (struct gs_port *port) +{ + unsigned long flags; + + func_enter(); + + if (!port) return; + + if (!(port->port.flags & ASYNC_INITIALIZED)) + return; + + spin_lock_irqsave(&port->driver_lock, flags); + + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + if (port->port.tty) + set_bit(TTY_IO_ERROR, &port->port.tty->flags); + + port->rd->shutdown_port (port); + + port->port.flags &= ~ASYNC_INITIALIZED; + spin_unlock_irqrestore(&port->driver_lock, flags); + + func_exit(); +} + + +void gs_hangup(struct tty_struct *tty) +{ + struct gs_port *port; + unsigned long flags; + + func_enter (); + + port = tty->driver_data; + tty = port->port.tty; + if (!tty) + return; + + gs_shutdown_port (port); + spin_lock_irqsave(&port->port.lock, flags); + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE); + port->port.tty = NULL; + port->port.count = 0; + spin_unlock_irqrestore(&port->port.lock, flags); + + wake_up_interruptible(&port->port.open_wait); + func_exit (); +} + + +int gs_block_til_ready(void *port_, struct file * filp) +{ + struct gs_port *gp = port_; + struct tty_port *port = &gp->port; + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + int CD; + struct tty_struct *tty; + unsigned long flags; + + func_enter (); + + if (!port) return 0; + + tty = port->tty; + + 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 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))) { + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + gs_dprintk (GS_DEBUG_BTR, "after nonblock\n"); + + 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"); + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) { + port->count--; + } + port->blocked_open++; + spin_unlock_irqrestore(&port->lock, flags); + while (1) { + CD = tty_port_carrier_raised(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_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); + + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) { + port->count++; + } + port->blocked_open--; + if (retval == 0) + port->flags |= ASYNC_NORMAL_ACTIVE; + spin_unlock_irqrestore(&port->lock, flags); + func_exit (); + return retval; +} + + +void gs_close(struct tty_struct * tty, struct file * filp) +{ + unsigned long flags; + struct gs_port *port; + + func_enter (); + + port = tty->driver_data; + + if (!port) return; + + if (!port->port.tty) { + /* This seems to happen when this is called from vhangup. */ + gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; + } + + spin_lock_irqsave(&port->port.lock, flags); + + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->port.lock, flags); + if (port->rd->hungup) + port->rd->hungup (port); + func_exit (); + return; + } + + if ((tty->count == 1) && (port->port.count != 1)) { + printk(KERN_ERR "gs: gs_close port %p: bad port count;" + " tty->count is 1, port count is %d\n", port, port->port.count); + port->port.count = 1; + } + if (--port->port.count < 0) { + printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->port.count); + port->port.count = 0; + } + + if (port->port.count) { + gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->port.count); + spin_unlock_irqrestore(&port->port.lock, flags); + func_exit (); + return; + } + port->port.flags |= ASYNC_CLOSING; + + /* + * 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. + */ + + spin_lock(&port->driver_lock); + port->rd->disable_rx_interrupts (port); + spin_unlock(&port->driver_lock); + spin_unlock_irqrestore(&port->port.lock, flags); + + /* 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->port.flags &= ~GS_ACTIVE; + + gs_flush_buffer(tty); + + tty_ldisc_flush(tty); + tty->closing = 0; + + spin_lock_irqsave(&port->driver_lock, flags); + port->event = 0; + port->rd->close (port); + port->rd->shutdown_port (port); + spin_unlock_irqrestore(&port->driver_lock, flags); + + spin_lock_irqsave(&port->port.lock, flags); + port->port.tty = NULL; + + if (port->port.blocked_open) { + if (port->close_delay) { + spin_unlock_irqrestore(&port->port.lock, flags); + msleep_interruptible(jiffies_to_msecs(port->close_delay)); + spin_lock_irqsave(&port->port.lock, flags); + } + wake_up_interruptible(&port->port.open_wait); + } + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); + spin_unlock_irqrestore(&port->port.lock, flags); + wake_up_interruptible(&port->port.close_wait); + + func_exit (); +} + + +void gs_set_termios (struct tty_struct * tty, + struct ktermios * old_termios) +{ + struct gs_port *port; + int baudrate, tmp, rv; + struct ktermios *tiosp; + + func_enter(); + + port = tty->driver_data; + + if (!port) return; + if (!port->port.tty) { + /* This seems to happen when this is called after gs_close. */ + gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; + } + + + tiosp = tty->termios; + + if (gs_debug & GS_DEBUG_TERMIOS) { + gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); + } + + 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 = tty_get_baud_rate(tty); + + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ( (port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; + else if ((port->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(&port->gs.open_wait); +#endif + + func_exit(); + return /* 0 */; +} + + + +/* Must be called with interrupts enabled */ +int gs_init_port(struct gs_port *port) +{ + unsigned long flags; + + func_enter (); + + if (port->port.flags & ASYNC_INITIALIZED) { + func_exit (); + return 0; + } + if (!port->xmit_buf) { + /* We may sleep in get_zeroed_page() */ + unsigned long tmp; + + tmp = get_zeroed_page(GFP_KERNEL); + spin_lock_irqsave (&port->driver_lock, flags); + if (port->xmit_buf) + free_page (tmp); + else + port->xmit_buf = (unsigned char *) tmp; + spin_unlock_irqrestore(&port->driver_lock, flags); + if (!port->xmit_buf) { + func_exit (); + return -ENOMEM; + } + } + + spin_lock_irqsave (&port->driver_lock, flags); + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + mutex_init(&port->port_write_mutex); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&port->driver_lock, flags); + gs_set_termios(port->port.tty, NULL); + spin_lock_irqsave (&port->driver_lock, flags); + port->port.flags |= ASYNC_INITIALIZED; + port->port.flags &= ~GS_TX_INTEN; + + spin_unlock_irqrestore(&port->driver_lock, flags); + func_exit (); + return 0; +} + + +int gs_setserial(struct gs_port *port, struct serial_struct __user *sp) +{ + struct serial_struct sio; + + if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) + return(-EFAULT); + + if (!capable(CAP_SYS_ADMIN)) { + if ((sio.baud_base != port->baud_base) || + (sio.close_delay != port->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (port->port.flags & ~ASYNC_USR_MASK))) + return(-EPERM); + } + + port->port.flags = (port->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->port.tty, NULL); + + return 0; +} + + +/*****************************************************************************/ + +/* + * Generate the serial struct info. + */ + +int gs_getserial(struct gs_port *port, struct serial_struct __user *sp) +{ + struct serial_struct sio; + + memset(&sio, 0, sizeof(struct serial_struct)); + sio.flags = port->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); + + if (copy_to_user(sp, &sio, sizeof(struct serial_struct))) + return -EFAULT; + return 0; + +} + + +void gs_got_break(struct gs_port *port) +{ + func_enter (); + + tty_insert_flip_char(port->port.tty, 0, TTY_BREAK); + tty_schedule_flip(port->port.tty); + if (port->port.flags & ASYNC_SAK) { + do_SAK (port->port.tty); + } + + func_exit (); +} + + +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_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); +EXPORT_SYMBOL(gs_got_break); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/generic_serial/rio/Makefile b/drivers/staging/generic_serial/rio/Makefile new file mode 100644 index 0000000..1661875 --- /dev/null +++ b/drivers/staging/generic_serial/rio/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the linux rio-subsystem. +# +# (C) R.E.Wolff@BitWizard.nl +# +# This file is GPL. See other files for the full Blurb. I'm lazy today. +# + +obj-$(CONFIG_RIO) += rio.o + +rio-y := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \ + rioparam.o rioroute.o riotable.o riotty.o diff --git a/drivers/staging/generic_serial/rio/board.h b/drivers/staging/generic_serial/rio/board.h new file mode 100644 index 0000000..bdea633 --- /dev/null +++ b/drivers/staging/generic_serial/rio/board.h @@ -0,0 +1,132 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : board.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:07 +** Retrieved : 11/6/98 11:34:20 +** +** ident @(#)board.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_board_h__ +#define __rio_board_h__ + +/* +** board.h contains the definitions for the *hardware* of the host cards. +** It describes the memory overlay for the dual port RAM area. +*/ + +#define DP_SRAM1_SIZE 0x7C00 +#define DP_SRAM2_SIZE 0x0200 +#define DP_SRAM3_SIZE 0x7000 +#define DP_SCRATCH_SIZE 0x1000 +#define DP_PARMMAP_ADDR 0x01FE /* offset into SRAM2 */ +#define DP_STARTUP_ADDR 0x01F8 /* offset into SRAM2 */ + +/* +** The shape of the Host Control area, at offset 0x7C00, Write Only +*/ +struct s_Ctrl { + u8 DpCtl; /* 7C00 */ + u8 Dp_Unused2_[127]; + u8 DpIntSet; /* 7C80 */ + u8 Dp_Unused3_[127]; + u8 DpTpuReset; /* 7D00 */ + u8 Dp_Unused4_[127]; + u8 DpIntReset; /* 7D80 */ + u8 Dp_Unused5_[127]; +}; + +/* +** The PROM data area on the host (0x7C00), Read Only +*/ +struct s_Prom { + u16 DpSlxCode[2]; + u16 DpRev; + u16 Dp_Unused6_; + u16 DpUniq[4]; + u16 DpJahre; + u16 DpWoche; + u16 DpHwFeature[5]; + u16 DpOemId; + u16 DpSiggy[16]; +}; + +/* +** Union of the Ctrl and Prom areas +*/ +union u_CtrlProm { /* This is the control/PROM area (0x7C00) */ + struct s_Ctrl DpCtrl; + struct s_Prom DpProm; +}; + +/* +** The top end of memory! +*/ +struct s_ParmMapS { /* Area containing Parm Map Pointer */ + u8 Dp_Unused8_[DP_PARMMAP_ADDR]; + u16 DpParmMapAd; +}; + +struct s_StartUpS { + u8 Dp_Unused9_[DP_STARTUP_ADDR]; + u8 Dp_LongJump[0x4]; + u8 Dp_Unused10_[2]; + u8 Dp_ShortJump[0x2]; +}; + +union u_Sram2ParmMap { /* This is the top of memory (0x7E00-0x7FFF) */ + u8 DpSramMem[DP_SRAM2_SIZE]; + struct s_ParmMapS DpParmMapS; + struct s_StartUpS DpStartUpS; +}; + +/* +** This is the DP RAM overlay. +*/ +struct DpRam { + u8 DpSram1[DP_SRAM1_SIZE]; /* 0000 - 7BFF */ + union u_CtrlProm DpCtrlProm; /* 7C00 - 7DFF */ + union u_Sram2ParmMap DpSram2ParmMap; /* 7E00 - 7FFF */ + u8 DpScratch[DP_SCRATCH_SIZE]; /* 8000 - 8FFF */ + u8 DpSram3[DP_SRAM3_SIZE]; /* 9000 - FFFF */ +}; + +#define DpControl DpCtrlProm.DpCtrl.DpCtl +#define DpSetInt DpCtrlProm.DpCtrl.DpIntSet +#define DpResetTpu DpCtrlProm.DpCtrl.DpTpuReset +#define DpResetInt DpCtrlProm.DpCtrl.DpIntReset + +#define DpSlx DpCtrlProm.DpProm.DpSlxCode +#define DpRevision DpCtrlProm.DpProm.DpRev +#define DpUnique DpCtrlProm.DpProm.DpUniq +#define DpYear DpCtrlProm.DpProm.DpJahre +#define DpWeek DpCtrlProm.DpProm.DpWoche +#define DpSignature DpCtrlProm.DpProm.DpSiggy + +#define DpParmMapR DpSram2ParmMap.DpParmMapS.DpParmMapAd +#define DpSram2 DpSram2ParmMap.DpSramMem + +#endif diff --git a/drivers/staging/generic_serial/rio/cirrus.h b/drivers/staging/generic_serial/rio/cirrus.h new file mode 100644 index 0000000..5ab5167 --- /dev/null +++ b/drivers/staging/generic_serial/rio/cirrus.h @@ -0,0 +1,210 @@ +/**************************************************************************** + ******* ******* + ******* CIRRUS.H ******* + ******* ******* + **************************************************************************** + + Author : Jeremy Rolls + Date : 3 Aug 1990 + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _cirrus_h +#define _cirrus_h 1 + +/* Bit fields for particular registers shared with driver */ + +/* COR1 - driver and RTA */ +#define RIOC_COR1_ODD 0x80 /* Odd parity */ +#define RIOC_COR1_EVEN 0x00 /* Even parity */ +#define RIOC_COR1_NOP 0x00 /* No parity */ +#define RIOC_COR1_FORCE 0x20 /* Force parity */ +#define RIOC_COR1_NORMAL 0x40 /* With parity */ +#define RIOC_COR1_1STOP 0x00 /* 1 stop bit */ +#define RIOC_COR1_15STOP 0x04 /* 1.5 stop bits */ +#define RIOC_COR1_2STOP 0x08 /* 2 stop bits */ +#define RIOC_COR1_5BITS 0x00 /* 5 data bits */ +#define RIOC_COR1_6BITS 0x01 /* 6 data bits */ +#define RIOC_COR1_7BITS 0x02 /* 7 data bits */ +#define RIOC_COR1_8BITS 0x03 /* 8 data bits */ + +#define RIOC_COR1_HOST 0xef /* Safe host bits */ + +/* RTA only */ +#define RIOC_COR1_CINPCK 0x00 /* Check parity of received characters */ +#define RIOC_COR1_CNINPCK 0x10 /* Don't check parity */ + +/* COR2 bits for both RTA and driver use */ +#define RIOC_COR2_IXANY 0x80 /* IXANY - any character is XON */ +#define RIOC_COR2_IXON 0x40 /* IXON - enable tx soft flowcontrol */ +#define RIOC_COR2_RTSFLOW 0x02 /* Enable tx hardware flow control */ + +/* Additional driver bits */ +#define RIOC_COR2_HUPCL 0x20 /* Hang up on close */ +#define RIOC_COR2_CTSFLOW 0x04 /* Enable rx hardware flow control */ +#define RIOC_COR2_IXOFF 0x01 /* Enable rx software flow control */ +#define RIOC_COR2_DTRFLOW 0x08 /* Enable tx hardware flow control */ + +/* RTA use only */ +#define RIOC_COR2_ETC 0x20 /* Embedded transmit options */ +#define RIOC_COR2_LOCAL 0x10 /* Local loopback mode */ +#define RIOC_COR2_REMOTE 0x08 /* Remote loopback mode */ +#define RIOC_COR2_HOST 0xc2 /* Safe host bits */ + +/* COR3 - RTA use only */ +#define RIOC_COR3_SCDRNG 0x80 /* Enable special char detect for range */ +#define RIOC_COR3_SCD34 0x40 /* Special character detect for SCHR's 3 + 4 */ +#define RIOC_COR3_FCT 0x20 /* Flow control transparency */ +#define RIOC_COR3_SCD12 0x10 /* Special character detect for SCHR's 1 + 2 */ +#define RIOC_COR3_FIFO12 0x0c /* 12 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO10 0x0a /* 10 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO8 0x08 /* 8 chars for receive FIFO threshold */ +#define RIOC_COR3_FIFO6 0x06 /* 6 chars for receive FIFO threshold */ + +#define RIOC_COR3_THRESHOLD RIOC_COR3_FIFO8 /* MUST BE LESS THAN MCOR_THRESHOLD */ + +#define RIOC_COR3_DEFAULT (RIOC_COR3_FCT | RIOC_COR3_THRESHOLD) + /* Default bits for COR3 */ + +/* COR4 driver and RTA use */ +#define RIOC_COR4_IGNCR 0x80 /* Throw away CR's on input */ +#define RIOC_COR4_ICRNL 0x40 /* Map CR -> NL on input */ +#define RIOC_COR4_INLCR 0x20 /* Map NL -> CR on input */ +#define RIOC_COR4_IGNBRK 0x10 /* Ignore Break */ +#define RIOC_COR4_NBRKINT 0x08 /* No interrupt on break (-BRKINT) */ +#define RIOC_COR4_RAISEMOD 0x01 /* Raise modem output lines on non-zero baud */ + + +/* COR4 driver only */ +#define RIOC_COR4_IGNPAR 0x04 /* IGNPAR (ignore characters with errors) */ +#define RIOC_COR4_PARMRK 0x02 /* PARMRK */ + +#define RIOC_COR4_HOST 0xf8 /* Safe host bits */ + +/* COR4 RTA only */ +#define RIOC_COR4_CIGNPAR 0x02 /* Thrown away bad characters */ +#define RIOC_COR4_CPARMRK 0x04 /* PARMRK characters */ +#define RIOC_COR4_CNPARMRK 0x03 /* Don't PARMRK */ + +/* COR5 driver and RTA use */ +#define RIOC_COR5_ISTRIP 0x80 /* Strip input chars to 7 bits */ +#define RIOC_COR5_LNE 0x40 /* Enable LNEXT processing */ +#define RIOC_COR5_CMOE 0x20 /* Match good and errored characters */ +#define RIOC_COR5_ONLCR 0x02 /* NL -> CR NL on output */ +#define RIOC_COR5_OCRNL 0x01 /* CR -> NL on output */ + +/* +** Spare bits - these are not used in the CIRRUS registers, so we use +** them to set various other features. +*/ +/* +** tstop and tbusy indication +*/ +#define RIOC_COR5_TSTATE_ON 0x08 /* Turn on monitoring of tbusy and tstop */ +#define RIOC_COR5_TSTATE_OFF 0x04 /* Turn off monitoring of tbusy and tstop */ +/* +** TAB3 +*/ +#define RIOC_COR5_TAB3 0x10 /* TAB3 mode */ + +#define RIOC_COR5_HOST 0xc3 /* Safe host bits */ + +/* CCSR */ +#define RIOC_CCSR_TXFLOFF 0x04 /* Tx is xoffed */ + +/* MSVR1 */ +/* NB. DTR / CD swapped from Cirrus spec as the pins are also reversed on the + RTA. This is because otherwise DCD would get lost on the 1 parallel / 3 + serial option. +*/ +#define RIOC_MSVR1_CD 0x80 /* CD (DSR on Cirrus) */ +#define RIOC_MSVR1_RTS 0x40 /* RTS (CTS on Cirrus) */ +#define RIOC_MSVR1_RI 0x20 /* RI */ +#define RIOC_MSVR1_DTR 0x10 /* DTR (CD on Cirrus) */ +#define RIOC_MSVR1_CTS 0x01 /* CTS output pin (RTS on Cirrus) */ +/* Next two used to indicate state of tbusy and tstop to driver */ +#define RIOC_MSVR1_TSTOP 0x08 /* Set if port flow controlled */ +#define RIOC_MSVR1_TEMPTY 0x04 /* Set if port tx buffer empty */ + +#define RIOC_MSVR1_HOST 0xf3 /* The bits the host wants */ + +/* Defines for the subscripts of a CONFIG packet */ +#define RIOC_CONFIG_COR1 1 /* Option register 1 */ +#define RIOC_CONFIG_COR2 2 /* Option register 2 */ +#define RIOC_CONFIG_COR4 3 /* Option register 4 */ +#define RIOC_CONFIG_COR5 4 /* Option register 5 */ +#define RIOC_CONFIG_TXXON 5 /* Tx XON character */ +#define RIOC_CONFIG_TXXOFF 6 /* Tx XOFF character */ +#define RIOC_CONFIG_RXXON 7 /* Rx XON character */ +#define RIOC_CONFIG_RXXOFF 8 /* Rx XOFF character */ +#define RIOC_CONFIG_LNEXT 9 /* LNEXT character */ +#define RIOC_CONFIG_TXBAUD 10 /* Tx baud rate */ +#define RIOC_CONFIG_RXBAUD 11 /* Rx baud rate */ + +#define RIOC_PRE_EMPTIVE 0x80 /* Pre-emptive bit in command field */ + +/* Packet types going from Host to remote - with the exception of OPEN, MOPEN, + CONFIG, SBREAK and MEMDUMP the remaining bytes of the data array will not + be used +*/ +#define RIOC_OPEN 0x00 /* Open a port */ +#define RIOC_CONFIG 0x01 /* Configure a port */ +#define RIOC_MOPEN 0x02 /* Modem open (block for DCD) */ +#define RIOC_CLOSE 0x03 /* Close a port */ +#define RIOC_WFLUSH (0x04 | RIOC_PRE_EMPTIVE) /* Write flush */ +#define RIOC_RFLUSH (0x05 | RIOC_PRE_EMPTIVE) /* Read flush */ +#define RIOC_RESUME (0x06 | RIOC_PRE_EMPTIVE) /* Resume if xoffed */ +#define RIOC_SBREAK 0x07 /* Start break */ +#define RIOC_EBREAK 0x08 /* End break */ +#define RIOC_SUSPEND (0x09 | RIOC_PRE_EMPTIVE) /* Susp op (behave as tho xoffed) */ +#define RIOC_FCLOSE (0x0a | RIOC_PRE_EMPTIVE) /* Force close */ +#define RIOC_XPRINT 0x0b /* Xprint packet */ +#define RIOC_MBIS (0x0c | RIOC_PRE_EMPTIVE) /* Set modem lines */ +#define RIOC_MBIC (0x0d | RIOC_PRE_EMPTIVE) /* Clear modem lines */ +#define RIOC_MSET (0x0e | RIOC_PRE_EMPTIVE) /* Set modem lines */ +#define RIOC_PCLOSE 0x0f /* Pseudo close - Leaves rx/tx enabled */ +#define RIOC_MGET (0x10 | RIOC_PRE_EMPTIVE) /* Force update of modem status */ +#define RIOC_MEMDUMP (0x11 | RIOC_PRE_EMPTIVE) /* Send back mem from addr supplied */ +#define RIOC_READ_REGISTER (0x12 | RIOC_PRE_EMPTIVE) /* Read CD1400 register (debug) */ + +/* "Command" packets going from remote to host COMPLETE and MODEM_STATUS + use data[4] / data[3] to indicate current state and modem status respectively +*/ + +#define RIOC_COMPLETE (0x20 | RIOC_PRE_EMPTIVE) + /* Command complete */ +#define RIOC_BREAK_RECEIVED (0x21 | RIOC_PRE_EMPTIVE) + /* Break received */ +#define RIOC_MODEM_STATUS (0x22 | RIOC_PRE_EMPTIVE) + /* Change in modem status */ + +/* "Command" packet that could go either way - handshake wake-up */ +#define RIOC_HANDSHAKE (0x23 | RIOC_PRE_EMPTIVE) + /* Wake-up to HOST / RTA */ + +#endif diff --git a/drivers/staging/generic_serial/rio/cmdblk.h b/drivers/staging/generic_serial/rio/cmdblk.h new file mode 100644 index 0000000..9ed4f86 --- /dev/null +++ b/drivers/staging/generic_serial/rio/cmdblk.h @@ -0,0 +1,53 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : cmdblk.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:09 +** Retrieved : 11/6/98 11:34:20 +** +** ident @(#)cmdblk.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_cmdblk_h__ +#define __rio_cmdblk_h__ + +/* +** the structure of a command block, used to queue commands destined for +** a rup. +*/ + +struct CmdBlk { + struct CmdBlk *NextP; /* Pointer to next command block */ + struct PKT Packet; /* A packet, to copy to the rup */ + /* The func to call to check if OK */ + int (*PreFuncP) (unsigned long, struct CmdBlk *); + int PreArg; /* The arg for the func */ + /* The func to call when completed */ + int (*PostFuncP) (unsigned long, struct CmdBlk *); + int PostArg; /* The arg for the func */ +}; + +#define NUM_RIO_CMD_BLKS (3 * (MAX_RUP * 4 + LINKS_PER_UNIT * 4)) +#endif diff --git a/drivers/staging/generic_serial/rio/cmdpkt.h b/drivers/staging/generic_serial/rio/cmdpkt.h new file mode 100644 index 0000000..c1e7a27 --- /dev/null +++ b/drivers/staging/generic_serial/rio/cmdpkt.h @@ -0,0 +1,177 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : cmdpkt.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:09 +** Retrieved : 11/6/98 11:34:20 +** +** ident @(#)cmdpkt.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ +#ifndef __rio_cmdpkt_h__ +#define __rio_cmdpkt_h__ + +/* +** overlays for the data area of a packet. Used in both directions +** (to build a packet to send, and to interpret a packet that arrives) +** and is very inconvenient for MIPS, so they appear as two separate +** structures - those used for modifying/reading packets on the card +** and those for modifying/reading packets in real memory, which have an _M +** suffix. +*/ + +#define RTA_BOOT_DATA_SIZE (PKT_MAX_DATA_LEN-2) + +/* +** The boot information packet looks like this: +** This structure overlays a PktCmd->CmdData structure, and so starts +** at Data[2] in the actual pkt! +*/ +struct BootSequence { + u16 NumPackets; + u16 LoadBase; + u16 CodeSize; +}; + +#define BOOT_SEQUENCE_LEN 8 + +struct SamTop { + u8 Unit; + u8 Link; +}; + +struct CmdHdr { + u8 PcCommand; + union { + u8 PcPhbNum; + u8 PcLinkNum; + u8 PcIDNum; + } U0; +}; + + +struct PktCmd { + union { + struct { + struct CmdHdr CmdHdr; + struct BootSequence PcBootSequence; + } S1; + struct { + u16 PcSequence; + u8 PcBootData[RTA_BOOT_DATA_SIZE]; + } S2; + struct { + u16 __crud__; + u8 PcUniqNum[4]; /* this is really a uint. */ + u8 PcModuleTypes; /* what modules are fitted */ + } S3; + struct { + struct CmdHdr CmdHdr; + u8 __undefined__; + u8 PcModemStatus; + u8 PcPortStatus; + u8 PcSubCommand; /* commands like mem or register dump */ + u16 PcSubAddr; /* Address for command */ + u8 PcSubData[64]; /* Date area for command */ + } S4; + struct { + struct CmdHdr CmdHdr; + u8 PcCommandText[1]; + u8 __crud__[20]; + u8 PcIDNum2; /* It had to go somewhere! */ + } S5; + struct { + struct CmdHdr CmdHdr; + struct SamTop Topology[LINKS_PER_UNIT]; + } S6; + } U1; +}; + +struct PktCmd_M { + union { + struct { + struct { + u8 PcCommand; + union { + u8 PcPhbNum; + u8 PcLinkNum; + u8 PcIDNum; + } U0; + } CmdHdr; + struct { + u16 NumPackets; + u16 LoadBase; + u16 CodeSize; + } PcBootSequence; + } S1; + struct { + u16 PcSequence; + u8 PcBootData[RTA_BOOT_DATA_SIZE]; + } S2; + struct { + u16 __crud__; + u8 PcUniqNum[4]; /* this is really a uint. */ + u8 PcModuleTypes; /* what modules are fitted */ + } S3; + struct { + u16 __cmd_hdr__; + u8 __undefined__; + u8 PcModemStatus; + u8 PcPortStatus; + u8 PcSubCommand; + u16 PcSubAddr; + u8 PcSubData[64]; + } S4; + struct { + u16 __cmd_hdr__; + u8 PcCommandText[1]; + u8 __crud__[20]; + u8 PcIDNum2; /* Tacked on end */ + } S5; + struct { + u16 __cmd_hdr__; + struct Top Topology[LINKS_PER_UNIT]; + } S6; + } U1; +}; + +#define Command U1.S1.CmdHdr.PcCommand +#define PhbNum U1.S1.CmdHdr.U0.PcPhbNum +#define IDNum U1.S1.CmdHdr.U0.PcIDNum +#define IDNum2 U1.S5.PcIDNum2 +#define LinkNum U1.S1.CmdHdr.U0.PcLinkNum +#define Sequence U1.S2.PcSequence +#define BootData U1.S2.PcBootData +#define BootSequence U1.S1.PcBootSequence +#define UniqNum U1.S3.PcUniqNum +#define ModemStatus U1.S4.PcModemStatus +#define PortStatus U1.S4.PcPortStatus +#define SubCommand U1.S4.PcSubCommand +#define SubAddr U1.S4.PcSubAddr +#define SubData U1.S4.PcSubData +#define CommandText U1.S5.PcCommandText +#define RouteTopology U1.S6.Topology +#define ModuleTypes U1.S3.PcModuleTypes + +#endif diff --git a/drivers/staging/generic_serial/rio/daemon.h b/drivers/staging/generic_serial/rio/daemon.h new file mode 100644 index 0000000..4af9032 --- /dev/null +++ b/drivers/staging/generic_serial/rio/daemon.h @@ -0,0 +1,307 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : daemon.h +** SID : 1.3 +** Last Modified : 11/6/98 11:34:09 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)daemon.h 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_daemon_h__ +#define __rio_daemon_h__ + + +/* +** structures used on /dev/rio +*/ + +struct Error { + unsigned int Error; + unsigned int Entry; + unsigned int Other; +}; + +struct DownLoad { + char __user *DataP; + unsigned int Count; + unsigned int ProductCode; +}; + +/* +** A few constants.... +*/ +#ifndef MAX_VERSION_LEN +#define MAX_VERSION_LEN 256 +#endif + +#ifndef MAX_XP_CTRL_LEN +#define MAX_XP_CTRL_LEN 16 /* ALSO IN PORT.H */ +#endif + +struct PortSetup { + unsigned int From; /* Set/Clear XP & IXANY Control from this port.... */ + unsigned int To; /* .... to this port */ + unsigned int XpCps; /* at this speed */ + char XpOn[MAX_XP_CTRL_LEN]; /* this is the start string */ + char XpOff[MAX_XP_CTRL_LEN]; /* this is the stop string */ + u8 IxAny; /* enable/disable IXANY */ + u8 IxOn; /* enable/disable IXON */ + u8 Lock; /* lock port params */ + u8 Store; /* store params across closes */ + u8 Drain; /* close only when drained */ +}; + +struct LpbReq { + unsigned int Host; + unsigned int Link; + struct LPB __user *LpbP; +}; + +struct RupReq { + unsigned int HostNum; + unsigned int RupNum; + struct RUP __user *RupP; +}; + +struct PortReq { + unsigned int SysPort; + struct Port __user *PortP; +}; + +struct StreamInfo { + unsigned int SysPort; + int RQueue; + int WQueue; +}; + +struct HostReq { + unsigned int HostNum; + struct Host __user *HostP; +}; + +struct HostDpRam { + unsigned int HostNum; + struct DpRam __user *DpRamP; +}; + +struct DebugCtrl { + unsigned int SysPort; + unsigned int Debug; + unsigned int Wait; +}; + +struct MapInfo { + unsigned int FirstPort; /* 8 ports, starting from this (tty) number */ + unsigned int RtaUnique; /* reside on this RTA (unique number) */ +}; + +struct MapIn { + unsigned int NumEntries; /* How many port sets are we mapping? */ + struct MapInfo *MapInfoP; /* Pointer to (user space) info */ +}; + +struct SendPack { + unsigned int PortNum; + unsigned char Len; + unsigned char Data[PKT_MAX_DATA_LEN]; +}; + +struct SpecialRupCmd { + struct PKT Packet; + unsigned short Host; + unsigned short RupNum; +}; + +struct IdentifyRta { + unsigned long RtaUnique; + u8 ID; +}; + +struct KillNeighbour { + unsigned long UniqueNum; + u8 Link; +}; + +struct rioVersion { + char version[MAX_VERSION_LEN]; + char relid[MAX_VERSION_LEN]; + int buildLevel; + char buildDate[MAX_VERSION_LEN]; +}; + + +/* +** RIOC commands are for the daemon type operations +** +** 09.12.1998 ARG - ESIL 0776 part fix +** Definition for 'RIOC' also appears in rioioctl.h, so we'd better do a +** #ifndef here first. +** rioioctl.h also now has #define 'RIO_QUICK_CHECK' as this ioctl is now +** allowed to be used by customers. +*/ +#ifndef RIOC +#define RIOC ('R'<<8)|('i'<<16)|('o'<<24) +#endif + +/* +** Boot stuff +*/ +#define RIO_GET_TABLE (RIOC | 100) +#define RIO_PUT_TABLE (RIOC | 101) +#define RIO_ASSIGN_RTA (RIOC | 102) +#define RIO_DELETE_RTA (RIOC | 103) +#define RIO_HOST_FOAD (RIOC | 104) +#define RIO_QUICK_CHECK (RIOC | 105) +#define RIO_SIGNALS_ON (RIOC | 106) +#define RIO_SIGNALS_OFF (RIOC | 107) +#define RIO_CHANGE_NAME (RIOC | 108) +#define RIO_DOWNLOAD (RIOC | 109) +#define RIO_GET_LOG (RIOC | 110) +#define RIO_SETUP_PORTS (RIOC | 111) +#define RIO_ALL_MODEM (RIOC | 112) + +/* +** card state, debug stuff +*/ +#define RIO_NUM_HOSTS (RIOC | 120) +#define RIO_HOST_LPB (RIOC | 121) +#define RIO_HOST_RUP (RIOC | 122) +#define RIO_HOST_PORT (RIOC | 123) +#define RIO_PARMS (RIOC | 124) +#define RIO_HOST_REQ (RIOC | 125) +#define RIO_READ_CONFIG (RIOC | 126) +#define RIO_SET_CONFIG (RIOC | 127) +#define RIO_VERSID (RIOC | 128) +#define RIO_FLAGS (RIOC | 129) +#define RIO_SETDEBUG (RIOC | 130) +#define RIO_GETDEBUG (RIOC | 131) +#define RIO_READ_LEVELS (RIOC | 132) +#define RIO_SET_FAST_BUS (RIOC | 133) +#define RIO_SET_SLOW_BUS (RIOC | 134) +#define RIO_SET_BYTE_MODE (RIOC | 135) +#define RIO_SET_WORD_MODE (RIOC | 136) +#define RIO_STREAM_INFO (RIOC | 137) +#define RIO_START_POLLER (RIOC | 138) +#define RIO_STOP_POLLER (RIOC | 139) +#define RIO_LAST_ERROR (RIOC | 140) +#define RIO_TICK (RIOC | 141) +#define RIO_TOCK (RIOC | 241) /* I did this on purpose, you know. */ +#define RIO_SEND_PACKET (RIOC | 142) +#define RIO_SET_BUSY (RIOC | 143) +#define SPECIAL_RUP_CMD (RIOC | 144) +#define RIO_FOAD_RTA (RIOC | 145) +#define RIO_ZOMBIE_RTA (RIOC | 146) +#define RIO_IDENTIFY_RTA (RIOC | 147) +#define RIO_KILL_NEIGHBOUR (RIOC | 148) +#define RIO_DEBUG_MEM (RIOC | 149) +/* +** 150 - 167 used..... See below +*/ +#define RIO_GET_PORT_SETUP (RIOC | 168) +#define RIO_RESUME (RIOC | 169) +#define RIO_MESG (RIOC | 170) +#define RIO_NO_MESG (RIOC | 171) +#define RIO_WHAT_MESG (RIOC | 172) +#define RIO_HOST_DPRAM (RIOC | 173) +#define RIO_MAP_B50_TO_50 (RIOC | 174) +#define RIO_MAP_B50_TO_57600 (RIOC | 175) +#define RIO_MAP_B110_TO_110 (RIOC | 176) +#define RIO_MAP_B110_TO_115200 (RIOC | 177) +#define RIO_GET_PORT_PARAMS (RIOC | 178) +#define RIO_SET_PORT_PARAMS (RIOC | 179) +#define RIO_GET_PORT_TTY (RIOC | 180) +#define RIO_SET_PORT_TTY (RIOC | 181) +#define RIO_SYSLOG_ONLY (RIOC | 182) +#define RIO_SYSLOG_CONS (RIOC | 183) +#define RIO_CONS_ONLY (RIOC | 184) +#define RIO_BLOCK_OPENS (RIOC | 185) + +/* +** 02.03.1999 ARG - ESIL 0820 fix : +** RIOBootMode is no longer use by the driver, so these ioctls +** are now obsolete : +** +#define RIO_GET_BOOT_MODE (RIOC | 186) +#define RIO_SET_BOOT_MODE (RIOC | 187) +** +*/ + +#define RIO_MEM_DUMP (RIOC | 189) +#define RIO_READ_REGISTER (RIOC | 190) +#define RIO_GET_MODTYPE (RIOC | 191) +#define RIO_SET_TIMER (RIOC | 192) +#define RIO_READ_CHECK (RIOC | 196) +#define RIO_WAITING_FOR_RESTART (RIOC | 197) +#define RIO_BIND_RTA (RIOC | 198) +#define RIO_GET_BINDINGS (RIOC | 199) +#define RIO_PUT_BINDINGS (RIOC | 200) + +#define RIO_MAKE_DEV (RIOC | 201) +#define RIO_MINOR (RIOC | 202) + +#define RIO_IDENTIFY_DRIVER (RIOC | 203) +#define RIO_DISPLAY_HOST_CFG (RIOC | 204) + + +/* +** MAKE_DEV / MINOR stuff +*/ +#define RIO_DEV_DIRECT 0x0000 +#define RIO_DEV_MODEM 0x0200 +#define RIO_DEV_XPRINT 0x0400 +#define RIO_DEV_MASK 0x0600 + +/* +** port management, xprint stuff +*/ +#define rIOCN(N) (RIOC|(N)) +#define rIOCR(N,T) (RIOC|(N)) +#define rIOCW(N,T) (RIOC|(N)) + +#define RIO_GET_XP_ON rIOCR(150,char[16]) /* start xprint string */ +#define RIO_SET_XP_ON rIOCW(151,char[16]) +#define RIO_GET_XP_OFF rIOCR(152,char[16]) /* finish xprint string */ +#define RIO_SET_XP_OFF rIOCW(153,char[16]) +#define RIO_GET_XP_CPS rIOCR(154,int) /* xprint CPS */ +#define RIO_SET_XP_CPS rIOCW(155,int) +#define RIO_GET_IXANY rIOCR(156,int) /* ixany allowed? */ +#define RIO_SET_IXANY rIOCW(157,int) +#define RIO_SET_IXANY_ON rIOCN(158) /* allow ixany */ +#define RIO_SET_IXANY_OFF rIOCN(159) /* disallow ixany */ +#define RIO_GET_MODEM rIOCR(160,int) /* port is modem/direct line? */ +#define RIO_SET_MODEM rIOCW(161,int) +#define RIO_SET_MODEM_ON rIOCN(162) /* port is a modem */ +#define RIO_SET_MODEM_OFF rIOCN(163) /* port is direct */ +#define RIO_GET_IXON rIOCR(164,int) /* ixon allowed? */ +#define RIO_SET_IXON rIOCW(165,int) +#define RIO_SET_IXON_ON rIOCN(166) /* allow ixon */ +#define RIO_SET_IXON_OFF rIOCN(167) /* disallow ixon */ + +#define RIO_GET_SIVIEW ((('s')<<8) | 106) /* backwards compatible with SI */ + +#define RIO_IOCTL_UNKNOWN -2 + +#endif diff --git a/drivers/staging/generic_serial/rio/errors.h b/drivers/staging/generic_serial/rio/errors.h new file mode 100644 index 0000000..bdb0523 --- /dev/null +++ b/drivers/staging/generic_serial/rio/errors.h @@ -0,0 +1,98 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : errors.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:10 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)errors.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_errors_h__ +#define __rio_errors_h__ + +/* +** error codes +*/ + +#define NOTHING_WRONG_AT_ALL 0 +#define BAD_CHARACTER_IN_NAME 1 +#define TABLE_ENTRY_ISNT_PROPERLY_NULL 2 +#define UNKNOWN_HOST_NUMBER 3 +#define ZERO_RTA_ID 4 +#define BAD_RTA_ID 5 +#define DUPLICATED_RTA_ID 6 +#define DUPLICATE_UNIQUE_NUMBER 7 +#define BAD_TTY_NUMBER 8 +#define TTY_NUMBER_IN_USE 9 +#define NAME_USED_TWICE 10 +#define HOST_ID_NOT_ZERO 11 +#define BOOT_IN_PROGRESS 12 +#define COPYIN_FAILED 13 +#define HOST_FILE_TOO_LARGE 14 +#define COPYOUT_FAILED 15 +#define NOT_SUPER_USER 16 +#define RIO_ALREADY_POLLING 17 + +#define ID_NUMBER_OUT_OF_RANGE 18 +#define PORT_NUMBER_OUT_OF_RANGE 19 +#define HOST_NUMBER_OUT_OF_RANGE 20 +#define RUP_NUMBER_OUT_OF_RANGE 21 +#define TTY_NUMBER_OUT_OF_RANGE 22 +#define LINK_NUMBER_OUT_OF_RANGE 23 + +#define HOST_NOT_RUNNING 24 +#define IOCTL_COMMAND_UNKNOWN 25 +#define RIO_SYSTEM_HALTED 26 +#define WAIT_FOR_DRAIN_BROKEN 27 +#define PORT_NOT_MAPPED_INTO_SYSTEM 28 +#define EXCLUSIVE_USE_SET 29 +#define WAIT_FOR_NOT_CLOSING_BROKEN 30 +#define WAIT_FOR_PORT_TO_OPEN_BROKEN 31 +#define WAIT_FOR_CARRIER_BROKEN 32 +#define WAIT_FOR_NOT_IN_USE_BROKEN 33 +#define WAIT_FOR_CAN_ADD_COMMAND_BROKEN 34 +#define WAIT_FOR_ADD_COMMAND_BROKEN 35 +#define WAIT_FOR_NOT_PARAM_BROKEN 36 +#define WAIT_FOR_RETRY_BROKEN 37 +#define HOST_HAS_ALREADY_BEEN_BOOTED 38 +#define UNIT_IS_IN_USE 39 +#define COULDNT_FIND_ENTRY 40 +#define RTA_UNIQUE_NUMBER_ZERO 41 +#define CLOSE_COMMAND_FAILED 42 +#define WAIT_FOR_CLOSE_BROKEN 43 +#define CPS_VALUE_OUT_OF_RANGE 44 +#define ID_ALREADY_IN_USE 45 +#define SIGNALS_ALREADY_SET 46 +#define NOT_RECEIVING_PROCESS 47 +#define RTA_NUMBER_WRONG 48 +#define NO_SUCH_PRODUCT 49 +#define HOST_SYSPORT_BAD 50 +#define ID_NOT_TENTATIVE 51 +#define XPRINT_CPS_OUT_OF_RANGE 52 +#define NOT_ENOUGH_CORE_FOR_PCI_COPY 53 + + +#endif /* __rio_errors_h__ */ diff --git a/drivers/staging/generic_serial/rio/func.h b/drivers/staging/generic_serial/rio/func.h new file mode 100644 index 0000000..078d44f --- /dev/null +++ b/drivers/staging/generic_serial/rio/func.h @@ -0,0 +1,143 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : func.h +** SID : 1.3 +** Last Modified : 11/6/98 11:34:10 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)func.h 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __func_h_def +#define __func_h_def + +#include <linux/kdev_t.h> + +/* rioboot.c */ +int RIOBootCodeRTA(struct rio_info *, struct DownLoad *); +int RIOBootCodeHOST(struct rio_info *, struct DownLoad *); +int RIOBootCodeUNKNOWN(struct rio_info *, struct DownLoad *); +void msec_timeout(struct Host *); +int RIOBootRup(struct rio_info *, unsigned int, struct Host *, struct PKT __iomem *); +int RIOBootOk(struct rio_info *, struct Host *, unsigned long); +int RIORtaBound(struct rio_info *, unsigned int); +void rio_fill_host_slot(int, int, unsigned int, struct Host *); + +/* riocmd.c */ +int RIOFoadRta(struct Host *, struct Map *); +int RIOZombieRta(struct Host *, struct Map *); +int RIOCommandRta(struct rio_info *, unsigned long, int (*func) (struct Host *, struct Map *)); +int RIOIdentifyRta(struct rio_info *, void __user *); +int RIOKillNeighbour(struct rio_info *, void __user *); +int RIOSuspendBootRta(struct Host *, int, int); +int RIOFoadWakeup(struct rio_info *); +struct CmdBlk *RIOGetCmdBlk(void); +void RIOFreeCmdBlk(struct CmdBlk *); +int RIOQueueCmdBlk(struct Host *, unsigned int, struct CmdBlk *); +void RIOPollHostCommands(struct rio_info *, struct Host *); +int RIOWFlushMark(unsigned long, struct CmdBlk *); +int RIORFlushEnable(unsigned long, struct CmdBlk *); +int RIOUnUse(unsigned long, struct CmdBlk *); + +/* rioctrl.c */ +int riocontrol(struct rio_info *, dev_t, int, unsigned long, int); + +int RIOPreemptiveCmd(struct rio_info *, struct Port *, unsigned char); + +/* rioinit.c */ +void rioinit(struct rio_info *, struct RioHostInfo *); +void RIOInitHosts(struct rio_info *, struct RioHostInfo *); +void RIOISAinit(struct rio_info *, int); +int RIODoAT(struct rio_info *, int, int); +caddr_t RIOCheckForATCard(int); +int RIOAssignAT(struct rio_info *, int, void __iomem *, int); +int RIOBoardTest(unsigned long, void __iomem *, unsigned char, int); +void RIOAllocDataStructs(struct rio_info *); +void RIOSetupDataStructs(struct rio_info *); +int RIODefaultName(struct rio_info *, struct Host *, unsigned int); +struct rioVersion *RIOVersid(void); +void RIOHostReset(unsigned int, struct DpRam __iomem *, unsigned int); + +/* riointr.c */ +void RIOTxEnable(char *); +void RIOServiceHost(struct rio_info *, struct Host *); +int riotproc(struct rio_info *, struct ttystatics *, int, int); + +/* rioparam.c */ +int RIOParam(struct Port *, int, int, int); +int RIODelay(struct Port *PortP, int); +int RIODelay_ni(struct Port *PortP, int); +void ms_timeout(struct Port *); +int can_add_transmit(struct PKT __iomem **, struct Port *); +void add_transmit(struct Port *); +void put_free_end(struct Host *, struct PKT __iomem *); +int can_remove_receive(struct PKT __iomem **, struct Port *); +void remove_receive(struct Port *); + +/* rioroute.c */ +int RIORouteRup(struct rio_info *, unsigned int, struct Host *, struct PKT __iomem *); +void RIOFixPhbs(struct rio_info *, struct Host *, unsigned int); +unsigned int GetUnitType(unsigned int); +int RIOSetChange(struct rio_info *); +int RIOFindFreeID(struct rio_info *, struct Host *, unsigned int *, unsigned int *); + + +/* riotty.c */ + +int riotopen(struct tty_struct *tty, struct file *filp); +int riotclose(void *ptr); +int riotioctl(struct rio_info *, struct tty_struct *, int, caddr_t); +void ttyseth(struct Port *, struct ttystatics *, struct old_sgttyb *sg); + +/* riotable.c */ +int RIONewTable(struct rio_info *); +int RIOApel(struct rio_info *); +int RIODeleteRta(struct rio_info *, struct Map *); +int RIOAssignRta(struct rio_info *, struct Map *); +int RIOReMapPorts(struct rio_info *, struct Host *, struct Map *); +int RIOChangeName(struct rio_info *, struct Map *); + +#if 0 +/* riodrvr.c */ +struct rio_info *rio_install(struct RioHostInfo *); +int rio_uninstall(struct rio_info *); +int rio_open(struct rio_info *, int, struct file *); +int rio_close(struct rio_info *, struct file *); +int rio_read(struct rio_info *, struct file *, char *, int); +int rio_write(struct rio_info *, struct file *f, char *, int); +int rio_ioctl(struct rio_info *, struct file *, int, char *); +int rio_select(struct rio_info *, struct file *f, int, struct sel *); +int rio_intr(char *); +int rio_isr_thread(char *); +struct rio_info *rio_info_store(int cmd, struct rio_info *p); +#endif + +extern void rio_copy_to_card(void *from, void __iomem *to, int len); +extern int rio_minor(struct tty_struct *tty); +extern int rio_ismodem(struct tty_struct *tty); + +extern void rio_start_card_running(struct Host *HostP); + +#endif /* __func_h_def */ diff --git a/drivers/staging/generic_serial/rio/host.h b/drivers/staging/generic_serial/rio/host.h new file mode 100644 index 0000000..78f2454 --- /dev/null +++ b/drivers/staging/generic_serial/rio/host.h @@ -0,0 +1,123 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : host.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:10 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)host.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_host_h__ +#define __rio_host_h__ + +/* +** the host structure - one per host card in the system. +*/ + +#define MAX_EXTRA_UNITS 64 + +/* +** Host data structure. This is used for the software equiv. of +** the host. +*/ +struct Host { + struct pci_dev *pdev; + unsigned char Type; /* RIO_EISA, RIO_MCA, ... */ + unsigned char Ivec; /* POLLED or ivec number */ + unsigned char Mode; /* Control stuff */ + unsigned char Slot; /* Slot */ + void __iomem *Caddr; /* KV address of DPRAM */ + struct DpRam __iomem *CardP; /* KV address of DPRAM, with overlay */ + unsigned long PaddrP; /* Phys. address of DPRAM */ + char Name[MAX_NAME_LEN]; /* The name of the host */ + unsigned int UniqueNum; /* host unique number */ + spinlock_t HostLock; /* Lock structure for MPX */ + unsigned int WorkToBeDone; /* set to true each interrupt */ + unsigned int InIntr; /* Being serviced? */ + unsigned int IntSrvDone; /* host's interrupt has been serviced */ + void (*Copy) (void *, void __iomem *, int); /* copy func */ + struct timer_list timer; + /* + ** I M P O R T A N T ! + ** + ** The rest of this data structure is cleared to zero after + ** a RIO_HOST_FOAD command. + */ + + unsigned long Flags; /* Whats going down */ +#define RC_WAITING 0 +#define RC_STARTUP 1 +#define RC_RUNNING 2 +#define RC_STUFFED 3 +#define RC_READY 7 +#define RUN_STATE 7 +/* +** Boot mode applies to the way in which hosts in this system will +** boot RTAs +*/ +#define RC_BOOT_ALL 0x8 /* Boot all RTAs attached */ +#define RC_BOOT_OWN 0x10 /* Only boot RTAs bound to this system */ +#define RC_BOOT_NONE 0x20 /* Don't boot any RTAs (slave mode) */ + + struct Top Topology[LINKS_PER_UNIT]; /* one per link */ + struct Map Mapping[MAX_RUP]; /* Mappings for host */ + struct PHB __iomem *PhbP; /* Pointer to the PHB array */ + unsigned short __iomem *PhbNumP; /* Ptr to Number of PHB's */ + struct LPB __iomem *LinkStrP; /* Link Structure Array */ + struct RUP __iomem *RupP; /* Sixteen real rups here */ + struct PARM_MAP __iomem *ParmMapP; /* points to the parmmap */ + unsigned int ExtraUnits[MAX_EXTRA_UNITS]; /* unknown things */ + unsigned int NumExtraBooted; /* how many of the above */ + /* + ** Twenty logical rups. + ** The first sixteen are the real Rup entries (above), the last four + ** are the link RUPs. + */ + struct UnixRup UnixRups[MAX_RUP + LINKS_PER_UNIT]; + int timeout_id; /* For calling 100 ms delays */ + int timeout_sem; /* For calling 100 ms delays */ + unsigned long locks; /* long req'd for set_bit --RR */ + char ____end_marker____; +}; +#define Control CardP->DpControl +#define SetInt CardP->DpSetInt +#define ResetTpu CardP->DpResetTpu +#define ResetInt CardP->DpResetInt +#define Signature CardP->DpSignature +#define Sram1 CardP->DpSram1 +#define Sram2 CardP->DpSram2 +#define Sram3 CardP->DpSram3 +#define Scratch CardP->DpScratch +#define __ParmMapR CardP->DpParmMapR +#define SLX CardP->DpSlx +#define Revision CardP->DpRevision +#define Unique CardP->DpUnique +#define Year CardP->DpYear +#define Week CardP->DpWeek + +#define RIO_DUMBPARM 0x0860 /* what not to expect */ + +#endif diff --git a/drivers/staging/generic_serial/rio/link.h b/drivers/staging/generic_serial/rio/link.h new file mode 100644 index 0000000..f3bf11a0 --- /dev/null +++ b/drivers/staging/generic_serial/rio/link.h @@ -0,0 +1,96 @@ +/**************************************************************************** + ******* ******* + ******* L I N K + ******* ******* + **************************************************************************** + + Author : Ian Nandhra / Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _link_h +#define _link_h 1 + +/************************************************* + * Define the Link Status stuff + ************************************************/ +/* Boot request stuff */ +#define BOOT_REQUEST ((ushort) 0) /* Request for a boot */ +#define BOOT_ABORT ((ushort) 1) /* Abort a boot */ +#define BOOT_SEQUENCE ((ushort) 2) /* Packet with the number of packets + and load address */ +#define BOOT_COMPLETED ((ushort) 3) /* Boot completed */ + + +struct LPB { + u16 link_number; /* Link Number */ + u16 in_ch; /* Link In Channel */ + u16 out_ch; /* Link Out Channel */ + u8 attached_serial[4]; /* Attached serial number */ + u8 attached_host_serial[4]; + /* Serial number of Host who + booted the other end */ + u16 descheduled; /* Currently Descheduled */ + u16 state; /* Current state */ + u16 send_poll; /* Send a Poll Packet */ + u16 ltt_p; /* Process Descriptor */ + u16 lrt_p; /* Process Descriptor */ + u16 lrt_status; /* Current lrt status */ + u16 ltt_status; /* Current ltt status */ + u16 timeout; /* Timeout value */ + u16 topology; /* Topology bits */ + u16 mon_ltt; + u16 mon_lrt; + u16 WaitNoBoot; /* Secs to hold off booting */ + u16 add_packet_list; /* Add packets to here */ + u16 remove_packet_list; /* Send packets from here */ + + u16 lrt_fail_chan; /* Lrt's failure channel */ + u16 ltt_fail_chan; /* Ltt's failure channel */ + + /* RUP structure for HOST to driver communications */ + struct RUP rup; + struct RUP link_rup; /* RUP for the link (POLL, + topology etc.) */ + u16 attached_link; /* Number of attached link */ + u16 csum_errors; /* csum errors */ + u16 num_disconnects; /* number of disconnects */ + u16 num_sync_rcvd; /* # sync's received */ + u16 num_sync_rqst; /* # sync requests */ + u16 num_tx; /* Num pkts sent */ + u16 num_rx; /* Num pkts received */ + u16 module_attached; /* Module tpyes of attached */ + u16 led_timeout; /* LED timeout */ + u16 first_port; /* First port to service */ + u16 last_port; /* Last port to service */ +}; + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/linux_compat.h b/drivers/staging/generic_serial/rio/linux_compat.h new file mode 100644 index 0000000..34c0d28 --- /dev/null +++ b/drivers/staging/generic_serial/rio/linux_compat.h @@ -0,0 +1,77 @@ +/* + * (C) 2000 R.E.Wolff@BitWizard.nl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/interrupt.h> + + +#define DEBUG_ALL + +struct ttystatics { + struct termios tm; +}; + +extern int rio_debug; + +#define RIO_DEBUG_INIT 0x000001 +#define RIO_DEBUG_BOOT 0x000002 +#define RIO_DEBUG_CMD 0x000004 +#define RIO_DEBUG_CTRL 0x000008 +#define RIO_DEBUG_INTR 0x000010 +#define RIO_DEBUG_PARAM 0x000020 +#define RIO_DEBUG_ROUTE 0x000040 +#define RIO_DEBUG_TABLE 0x000080 +#define RIO_DEBUG_TTY 0x000100 +#define RIO_DEBUG_FLOW 0x000200 +#define RIO_DEBUG_MODEMSIGNALS 0x000400 +#define RIO_DEBUG_PROBE 0x000800 +#define RIO_DEBUG_CLEANUP 0x001000 +#define RIO_DEBUG_IFLOW 0x002000 +#define RIO_DEBUG_PFE 0x004000 +#define RIO_DEBUG_REC 0x008000 +#define RIO_DEBUG_SPINLOCK 0x010000 +#define RIO_DEBUG_DELAY 0x020000 +#define RIO_DEBUG_MOD_COUNT 0x040000 + + +/* Copied over from riowinif.h . This is ugly. The winif file declares +also much other stuff which is incompatible with the headers from +the older driver. The older driver includes "brates.h" which shadows +the definitions from Linux, and is incompatible... */ + +/* RxBaud and TxBaud definitions... */ +#define RIO_B0 0x00 /* RTS / DTR signals dropped */ +#define RIO_B50 0x01 /* 50 baud */ +#define RIO_B75 0x02 /* 75 baud */ +#define RIO_B110 0x03 /* 110 baud */ +#define RIO_B134 0x04 /* 134.5 baud */ +#define RIO_B150 0x05 /* 150 baud */ +#define RIO_B200 0x06 /* 200 baud */ +#define RIO_B300 0x07 /* 300 baud */ +#define RIO_B600 0x08 /* 600 baud */ +#define RIO_B1200 0x09 /* 1200 baud */ +#define RIO_B1800 0x0A /* 1800 baud */ +#define RIO_B2400 0x0B /* 2400 baud */ +#define RIO_B4800 0x0C /* 4800 baud */ +#define RIO_B9600 0x0D /* 9600 baud */ +#define RIO_B19200 0x0E /* 19200 baud */ +#define RIO_B38400 0x0F /* 38400 baud */ +#define RIO_B56000 0x10 /* 56000 baud */ +#define RIO_B57600 0x11 /* 57600 baud */ +#define RIO_B64000 0x12 /* 64000 baud */ +#define RIO_B115200 0x13 /* 115200 baud */ +#define RIO_B2000 0x14 /* 2000 baud */ diff --git a/drivers/staging/generic_serial/rio/map.h b/drivers/staging/generic_serial/rio/map.h new file mode 100644 index 0000000..8366978 --- /dev/null +++ b/drivers/staging/generic_serial/rio/map.h @@ -0,0 +1,98 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : map.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:11 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)map.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_map_h__ +#define __rio_map_h__ + +/* +** mapping structure passed to and from the config.rio program to +** determine the current topology of the world +*/ + +#define MAX_MAP_ENTRY 17 +#define TOTAL_MAP_ENTRIES (MAX_MAP_ENTRY*RIO_SLOTS) +#define MAX_NAME_LEN 32 + +struct Map { + unsigned int HostUniqueNum; /* Supporting hosts unique number */ + unsigned int RtaUniqueNum; /* Unique number */ + /* + ** The next two IDs must be swapped on big-endian architectures + ** when using a v2.04 /etc/rio/config with a v3.00 driver (when + ** upgrading for example). + */ + unsigned short ID; /* ID used in the subnet */ + unsigned short ID2; /* ID of 2nd block of 8 for 16 port */ + unsigned long Flags; /* Booted, ID Given, Disconnected */ + unsigned long SysPort; /* First tty mapped to this port */ + struct Top Topology[LINKS_PER_UNIT]; /* ID connected to each link */ + char Name[MAX_NAME_LEN]; /* Cute name by which RTA is known */ +}; + +/* +** Flag values: +*/ +#define RTA_BOOTED 0x00000001 +#define RTA_NEWBOOT 0x00000010 +#define MSG_DONE 0x00000020 +#define RTA_INTERCONNECT 0x00000040 +#define RTA16_SECOND_SLOT 0x00000080 +#define BEEN_HERE 0x00000100 +#define SLOT_TENTATIVE 0x40000000 +#define SLOT_IN_USE 0x80000000 + +/* +** HostUniqueNum is the unique number from the host card that this RTA +** is to be connected to. +** RtaUniqueNum is the unique number of the RTA concerned. It will be ZERO +** if the slot in the table is unused. If it is the same as the HostUniqueNum +** then this slot represents a host card. +** Flags contains current boot/route state info +** SysPort is a value in the range 0-504, being the number of the first tty +** on this RTA. Each RTA supports 8 ports. The SysPort value must be modulo 8. +** SysPort 0-127 correspond to /dev/ttyr001 to /dev/ttyr128, with minor +** numbers 0-127. SysPort 128-255 correspond to /dev/ttyr129 to /dev/ttyr256, +** again with minor numbers 0-127, and so on for SysPorts 256-383 and 384-511 +** ID will be in the range 0-16 for a `known' RTA. ID will be 0xFFFF for an +** unused slot/unknown ID etc. +** The Topology array contains the ID of the unit connected to each of the +** four links on this unit. The entry will be 0xFFFF if NOTHING is connected +** to the link, or will be 0xFF00 if an UNKNOWN unit is connected to the link. +** The Name field is a null-terminated string, upto 31 characters, containing +** the 'cute' name that the sysadmin/users know the RTA by. It is permissible +** for this string to contain any character in the range \040 to \176 inclusive. +** In particular, ctrl sequences and DEL (0x7F, \177) are not allowed. The +** special character '%' IS allowable, and needs no special action. +** +*/ + +#endif diff --git a/drivers/staging/generic_serial/rio/param.h b/drivers/staging/generic_serial/rio/param.h new file mode 100644 index 0000000..7e9b628 --- /dev/null +++ b/drivers/staging/generic_serial/rio/param.h @@ -0,0 +1,55 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : param.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:12 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)param.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_param_h__ +#define __rio_param_h__ + +/* +** the param command block, as used in OPEN and PARAM calls. +*/ + +struct phb_param { + u8 Cmd; /* It is very important that these line up */ + u8 Cor1; /* with what is expected at the other end. */ + u8 Cor2; /* to confirm that you've got it right, */ + u8 Cor4; /* check with cirrus/cirrus.h */ + u8 Cor5; + u8 TxXon; /* Transmit X-On character */ + u8 TxXoff; /* Transmit X-Off character */ + u8 RxXon; /* Receive X-On character */ + u8 RxXoff; /* Receive X-Off character */ + u8 LNext; /* Literal-next character */ + u8 TxBaud; /* Transmit baudrate */ + u8 RxBaud; /* Receive baudrate */ +}; + +#endif diff --git a/drivers/staging/generic_serial/rio/parmmap.h b/drivers/staging/generic_serial/rio/parmmap.h new file mode 100644 index 0000000..acc8fa4 --- /dev/null +++ b/drivers/staging/generic_serial/rio/parmmap.h @@ -0,0 +1,81 @@ +/**************************************************************************** + ******* ******* + ******* H O S T M E M O R Y M A P + ******* ******* + **************************************************************************** + + Author : Ian Nandhra / Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- +6/4/1991 jonb Made changes to accommodate Mips R3230 bus + ***************************************************************************/ + +#ifndef _parmap_h +#define _parmap_h + +typedef struct PARM_MAP PARM_MAP; + +struct PARM_MAP { + u16 phb_ptr; /* Pointer to the PHB array */ + u16 phb_num_ptr; /* Ptr to Number of PHB's */ + u16 free_list; /* Free List pointer */ + u16 free_list_end; /* Free List End pointer */ + u16 q_free_list_ptr; /* Ptr to Q_BUF variable */ + u16 unit_id_ptr; /* Unit Id */ + u16 link_str_ptr; /* Link Structure Array */ + u16 bootloader_1; /* 1st Stage Boot Loader */ + u16 bootloader_2; /* 2nd Stage Boot Loader */ + u16 port_route_map_ptr; /* Port Route Map */ + u16 route_ptr; /* Unit Route Map */ + u16 map_present; /* Route Map present */ + s16 pkt_num; /* Total number of packets */ + s16 q_num; /* Total number of Q packets */ + u16 buffers_per_port; /* Number of buffers per port */ + u16 heap_size; /* Initial size of heap */ + u16 heap_left; /* Current Heap left */ + u16 error; /* Error code */ + u16 tx_max; /* Max number of tx pkts per phb */ + u16 rx_max; /* Max number of rx pkts per phb */ + u16 rx_limit; /* For high / low watermarks */ + s16 links; /* Links to use */ + s16 timer; /* Interrupts per second */ + u16 rups; /* Pointer to the RUPs */ + u16 max_phb; /* Mostly for debugging */ + u16 living; /* Just increments!! */ + u16 init_done; /* Initialisation over */ + u16 booting_link; + u16 idle_count; /* Idle time counter */ + u16 busy_count; /* Busy counter */ + u16 idle_control; /* Control Idle Process */ + u16 tx_intr; /* TX interrupt pending */ + u16 rx_intr; /* RX interrupt pending */ + u16 rup_intr; /* RUP interrupt pending */ +}; + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/pci.h b/drivers/staging/generic_serial/rio/pci.h new file mode 100644 index 0000000..6032f91 --- /dev/null +++ b/drivers/staging/generic_serial/rio/pci.h @@ -0,0 +1,72 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : pci.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:12 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)pci.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_pci_h__ +#define __rio_pci_h__ + +/* +** PCI stuff +*/ + +#define PCITpFastClock 0x80 +#define PCITpSlowClock 0x00 +#define PCITpFastLinks 0x40 +#define PCITpSlowLinks 0x00 +#define PCITpIntEnable 0x04 +#define PCITpIntDisable 0x00 +#define PCITpBusEnable 0x02 +#define PCITpBusDisable 0x00 +#define PCITpBootFromRam 0x01 +#define PCITpBootFromLink 0x00 + +#define RIO_PCI_VENDOR 0x11CB +#define RIO_PCI_DEVICE 0x8000 +#define RIO_PCI_BASE_CLASS 0x02 +#define RIO_PCI_SUB_CLASS 0x80 +#define RIO_PCI_PROG_IFACE 0x00 + +#define RIO_PCI_RID 0x0008 +#define RIO_PCI_BADR0 0x0010 +#define RIO_PCI_INTLN 0x003C +#define RIO_PCI_INTPIN 0x003D + +#define RIO_PCI_MEM_SIZE 65536 + +#define RIO_PCI_TURBO_TP 0x80 +#define RIO_PCI_FAST_LINKS 0x40 +#define RIO_PCI_INT_ENABLE 0x04 +#define RIO_PCI_TP_BUS_ENABLE 0x02 +#define RIO_PCI_BOOT_FROM_RAM 0x01 + +#define RIO_PCI_DEFAULT_MODE 0x05 + +#endif /* __rio_pci_h__ */ diff --git a/drivers/staging/generic_serial/rio/phb.h b/drivers/staging/generic_serial/rio/phb.h new file mode 100644 index 0000000..a4c48ae --- /dev/null +++ b/drivers/staging/generic_serial/rio/phb.h @@ -0,0 +1,142 @@ +/**************************************************************************** + ******* ******* + ******* P H B H E A D E R ******* + ******* ******* + **************************************************************************** + + Author : Ian Nandhra, Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _phb_h +#define _phb_h 1 + +/************************************************* + * Handshake asserted. Deasserted by the LTT(s) + ************************************************/ +#define PHB_HANDSHAKE_SET ((ushort) 0x001) /* Set by LRT */ + +#define PHB_HANDSHAKE_RESET ((ushort) 0x002) /* Set by ISR / driver */ + +#define PHB_HANDSHAKE_FLAGS (PHB_HANDSHAKE_RESET | PHB_HANDSHAKE_SET) + /* Reset by ltt */ + + +/************************************************* + * Maximum number of PHB's + ************************************************/ +#define MAX_PHB ((ushort) 128) /* range 0-127 */ + +/************************************************* + * Defines for the mode fields + ************************************************/ +#define TXPKT_INCOMPLETE 0x0001 /* Previous tx packet not completed */ +#define TXINTR_ENABLED 0x0002 /* Tx interrupt is enabled */ +#define TX_TAB3 0x0004 /* TAB3 mode */ +#define TX_OCRNL 0x0008 /* OCRNL mode */ +#define TX_ONLCR 0x0010 /* ONLCR mode */ +#define TX_SENDSPACES 0x0020 /* Send n spaces command needs + completing */ +#define TX_SENDNULL 0x0040 /* Escaping NULL needs completing */ +#define TX_SENDLF 0x0080 /* LF -> CR LF needs completing */ +#define TX_PARALLELBUG 0x0100 /* CD1400 LF -> CR LF bug on parallel + port */ +#define TX_HANGOVER (TX_SENDSPACES | TX_SENDLF | TX_SENDNULL) +#define TX_DTRFLOW 0x0200 /* DTR tx flow control */ +#define TX_DTRFLOWED 0x0400 /* DTR is low - don't allow more data + into the FIFO */ +#define TX_DATAINFIFO 0x0800 /* There is data in the FIFO */ +#define TX_BUSY 0x1000 /* Data in FIFO, shift or holding regs */ + +#define RX_SPARE 0x0001 /* SPARE */ +#define RXINTR_ENABLED 0x0002 /* Rx interrupt enabled */ +#define RX_ICRNL 0x0008 /* ICRNL mode */ +#define RX_INLCR 0x0010 /* INLCR mode */ +#define RX_IGNCR 0x0020 /* IGNCR mode */ +#define RX_CTSFLOW 0x0040 /* CTSFLOW enabled */ +#define RX_IXOFF 0x0080 /* IXOFF enabled */ +#define RX_CTSFLOWED 0x0100 /* CTSFLOW and CTS dropped */ +#define RX_IXOFFED 0x0200 /* IXOFF and xoff sent */ +#define RX_BUFFERED 0x0400 /* Try and pass on complete packets */ + +#define PORT_ISOPEN 0x0001 /* Port open? */ +#define PORT_HUPCL 0x0002 /* Hangup on close? */ +#define PORT_MOPENPEND 0x0004 /* Modem open pending */ +#define PORT_ISPARALLEL 0x0008 /* Parallel port */ +#define PORT_BREAK 0x0010 /* Port on break */ +#define PORT_STATUSPEND 0x0020 /* Status packet pending */ +#define PORT_BREAKPEND 0x0040 /* Break packet pending */ +#define PORT_MODEMPEND 0x0080 /* Modem status packet pending */ +#define PORT_PARALLELBUG 0x0100 /* CD1400 LF -> CR LF bug on parallel + port */ +#define PORT_FULLMODEM 0x0200 /* Full modem signals */ +#define PORT_RJ45 0x0400 /* RJ45 connector - no RI signal */ +#define PORT_RESTRICTED 0x0600 /* Restricted connector - no RI / DTR */ + +#define PORT_MODEMBITS 0x0600 /* Mask for modem fields */ + +#define PORT_WCLOSE 0x0800 /* Waiting for close */ +#define PORT_HANDSHAKEFIX 0x1000 /* Port has H/W flow control fix */ +#define PORT_WASPCLOSED 0x2000 /* Port closed with PCLOSE */ +#define DUMPMODE 0x4000 /* Dump RTA mem */ +#define READ_REG 0x8000 /* Read CD1400 register */ + + + +/************************************************************************** + * PHB Structure + * A few words. + * + * Normally Packets are added to the end of the list and removed from + * the start. The pointer tx_add points to a SPACE to put a Packet. + * The pointer tx_remove points to the next Packet to remove + *************************************************************************/ + +struct PHB { + u8 source; + u8 handshake; + u8 status; + u16 timeout; /* Maximum of 1.9 seconds */ + u8 link; /* Send down this link */ + u8 destination; + u16 tx_start; + u16 tx_end; + u16 tx_add; + u16 tx_remove; + + u16 rx_start; + u16 rx_end; + u16 rx_add; + u16 rx_remove; + +}; + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/pkt.h b/drivers/staging/generic_serial/rio/pkt.h new file mode 100644 index 0000000..a945816 --- /dev/null +++ b/drivers/staging/generic_serial/rio/pkt.h @@ -0,0 +1,77 @@ +/**************************************************************************** + ******* ******* + ******* P A C K E T H E A D E R F I L E + ******* ******* + **************************************************************************** + + Author : Ian Nandhra / Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _pkt_h +#define _pkt_h 1 + +#define PKT_CMD_BIT ((ushort) 0x080) +#define PKT_CMD_DATA ((ushort) 0x080) + +#define PKT_ACK ((ushort) 0x040) + +#define PKT_TGL ((ushort) 0x020) + +#define PKT_LEN_MASK ((ushort) 0x07f) + +#define DATA_WNDW ((ushort) 0x10) +#define PKT_TTL_MASK ((ushort) 0x0f) + +#define PKT_MAX_DATA_LEN 72 + +#define PKT_LENGTH sizeof(struct PKT) +#define SYNC_PKT_LENGTH (PKT_LENGTH + 4) + +#define CONTROL_PKT_LEN_MASK PKT_LEN_MASK +#define CONTROL_PKT_CMD_BIT PKT_CMD_BIT +#define CONTROL_PKT_ACK (PKT_ACK << 8) +#define CONTROL_PKT_TGL (PKT_TGL << 8) +#define CONTROL_PKT_TTL_MASK (PKT_TTL_MASK << 8) +#define CONTROL_DATA_WNDW (DATA_WNDW << 8) + +struct PKT { + u8 dest_unit; /* Destination Unit Id */ + u8 dest_port; /* Destination POrt */ + u8 src_unit; /* Source Unit Id */ + u8 src_port; /* Source POrt */ + u8 len; + u8 control; + u8 data[PKT_MAX_DATA_LEN]; + /* Actual data :-) */ + u16 csum; /* C-SUM */ +}; +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/port.h b/drivers/staging/generic_serial/rio/port.h new file mode 100644 index 0000000..49cf6d1 --- /dev/null +++ b/drivers/staging/generic_serial/rio/port.h @@ -0,0 +1,179 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : port.h +** SID : 1.3 +** Last Modified : 11/6/98 11:34:12 +** Retrieved : 11/6/98 11:34:21 +** +** ident @(#)port.h 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_port_h__ +#define __rio_port_h__ + +/* +** Port data structure +*/ +struct Port { + struct gs_port gs; + int PortNum; /* RIO port no., 0-511 */ + struct Host *HostP; + void __iomem *Caddr; + unsigned short HostPort; /* Port number on host card */ + unsigned char RupNum; /* Number of RUP for port */ + unsigned char ID2; /* Second ID of RTA for port */ + unsigned long State; /* FLAGS for open & xopen */ +#define RIO_LOPEN 0x00001 /* Local open */ +#define RIO_MOPEN 0x00002 /* Modem open */ +#define RIO_WOPEN 0x00004 /* Waiting for open */ +#define RIO_CLOSING 0x00008 /* The port is being close */ +#define RIO_XPBUSY 0x00010 /* Transparent printer busy */ +#define RIO_BREAKING 0x00020 /* Break in progress */ +#define RIO_DIRECT 0x00040 /* Doing Direct output */ +#define RIO_EXCLUSIVE 0x00080 /* Stream open for exclusive use */ +#define RIO_NDELAY 0x00100 /* Stream is open FNDELAY */ +#define RIO_CARR_ON 0x00200 /* Stream has carrier present */ +#define RIO_XPWANTR 0x00400 /* Stream wanted by Xprint */ +#define RIO_RBLK 0x00800 /* Stream is read-blocked */ +#define RIO_BUSY 0x01000 /* Stream is BUSY for write */ +#define RIO_TIMEOUT 0x02000 /* Stream timeout in progress */ +#define RIO_TXSTOP 0x04000 /* Stream output is stopped */ +#define RIO_WAITFLUSH 0x08000 /* Stream waiting for flush */ +#define RIO_DYNOROD 0x10000 /* Drain failed */ +#define RIO_DELETED 0x20000 /* RTA has been deleted */ +#define RIO_ISSCANCODE 0x40000 /* This line is in scancode mode */ +#define RIO_USING_EUC 0x100000 /* Using extended Unix chars */ +#define RIO_CAN_COOK 0x200000 /* This line can do cooking */ +#define RIO_TRIAD_MODE 0x400000 /* Enable TRIAD special ops. */ +#define RIO_TRIAD_BLOCK 0x800000 /* Next read will block */ +#define RIO_TRIAD_FUNC 0x1000000 /* Seen a function key coming in */ +#define RIO_THROTTLE_RX 0x2000000 /* RX needs to be throttled. */ + + unsigned long Config; /* FLAGS for NOREAD.... */ +#define RIO_NOREAD 0x0001 /* Are not allowed to read port */ +#define RIO_NOWRITE 0x0002 /* Are not allowed to write port */ +#define RIO_NOXPRINT 0x0004 /* Are not allowed to xprint port */ +#define RIO_NOMASK 0x0007 /* All not allowed things */ +#define RIO_IXANY 0x0008 /* Port is allowed ixany */ +#define RIO_MODEM 0x0010 /* Stream is a modem device */ +#define RIO_IXON 0x0020 /* Port is allowed ixon */ +#define RIO_WAITDRAIN 0x0040 /* Wait for port to completely drain */ +#define RIO_MAP_50_TO_50 0x0080 /* Map 50 baud to 50 baud */ +#define RIO_MAP_110_TO_110 0x0100 /* Map 110 baud to 110 baud */ + +/* +** 15.10.1998 ARG - ESIL 0761 prt fix +** As LynxOS does not appear to support Hardware Flow Control ..... +** Define our own flow control flags in 'Config'. +*/ +#define RIO_CTSFLOW 0x0200 /* RIO's own CTSFLOW flag */ +#define RIO_RTSFLOW 0x0400 /* RIO's own RTSFLOW flag */ + + + struct PHB __iomem *PhbP; /* pointer to PHB for port */ + u16 __iomem *TxAdd; /* Add packets here */ + u16 __iomem *TxStart; /* Start of add array */ + u16 __iomem *TxEnd; /* End of add array */ + u16 __iomem *RxRemove; /* Remove packets here */ + u16 __iomem *RxStart; /* Start of remove array */ + u16 __iomem *RxEnd; /* End of remove array */ + unsigned int RtaUniqueNum; /* Unique number of RTA */ + unsigned short PortState; /* status of port */ + unsigned short ModemState; /* status of modem lines */ + unsigned long ModemLines; /* Modem bits sent to RTA */ + unsigned char CookMode; /* who expands CR/LF? */ + unsigned char ParamSem; /* Prevent write during param */ + unsigned char Mapped; /* if port mapped onto host */ + unsigned char SecondBlock; /* if port belongs to 2nd block + of 16 port RTA */ + unsigned char InUse; /* how many pre-emptive cmds */ + unsigned char Lock; /* if params locked */ + unsigned char Store; /* if params stored across closes */ + unsigned char FirstOpen; /* TRUE if first time port opened */ + unsigned char FlushCmdBodge; /* if doing a (non)flush */ + unsigned char MagicFlags; /* require intr processing */ +#define MAGIC_FLUSH 0x01 /* mirror of WflushFlag */ +#define MAGIC_REBOOT 0x02 /* RTA re-booted, re-open ports */ +#define MORE_OUTPUT_EYGOR 0x04 /* riotproc failed to empty clists */ + unsigned char WflushFlag; /* 1 How many WFLUSHs active */ +/* +** Transparent print stuff +*/ + struct Xprint { +#ifndef MAX_XP_CTRL_LEN +#define MAX_XP_CTRL_LEN 16 /* ALSO IN DAEMON.H */ +#endif + unsigned int XpCps; + char XpOn[MAX_XP_CTRL_LEN]; + char XpOff[MAX_XP_CTRL_LEN]; + unsigned short XpLen; /* strlen(XpOn)+strlen(XpOff) */ + unsigned char XpActive; + unsigned char XpLastTickOk; /* TRUE if we can process */ +#define XP_OPEN 00001 +#define XP_RUNABLE 00002 + struct ttystatics *XttyP; + } Xprint; + unsigned char RxDataStart; + unsigned char Cor2Copy; /* copy of COR2 */ + char *Name; /* points to the Rta's name */ + char *TxRingBuffer; + unsigned short TxBufferIn; /* New data arrives here */ + unsigned short TxBufferOut; /* Intr removes data here */ + unsigned short OldTxBufferOut; /* Indicates if draining */ + int TimeoutId; /* Timeout ID */ + unsigned int Debug; + unsigned char WaitUntilBooted; /* True if open should block */ + unsigned int statsGather; /* True if gathering stats */ + unsigned long txchars; /* Chars transmitted */ + unsigned long rxchars; /* Chars received */ + unsigned long opens; /* port open count */ + unsigned long closes; /* port close count */ + unsigned long ioctls; /* ioctl count */ + unsigned char LastRxTgl; /* Last state of rx toggle bit */ + spinlock_t portSem; /* Lock using this sem */ + int MonitorTstate; /* Monitoring ? */ + int timeout_id; /* For calling 100 ms delays */ + int timeout_sem; /* For calling 100 ms delays */ + int firstOpen; /* First time open ? */ + char *p; /* save the global struc here .. */ +}; + +struct ModuleInfo { + char *Name; + unsigned int Flags[4]; /* one per port on a module */ +}; + +/* +** This struct is required because trying to grab an entire Port structure +** runs into problems with differing struct sizes between driver and config. +*/ +struct PortParams { + unsigned int Port; + unsigned long Config; + unsigned long State; + struct ttystatics *TtyP; +}; + +#endif diff --git a/drivers/staging/generic_serial/rio/protsts.h b/drivers/staging/generic_serial/rio/protsts.h new file mode 100644 index 0000000..8ab7940 --- /dev/null +++ b/drivers/staging/generic_serial/rio/protsts.h @@ -0,0 +1,110 @@ +/**************************************************************************** + ******* ******* + ******* P R O T O C O L S T A T U S S T R U C T U R E ******* + ******* ******* + **************************************************************************** + + Author : Ian Nandhra / Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _protsts_h +#define _protsts_h 1 + +/************************************************* + * ACK bit. Last Packet received OK. Set by + * rxpkt to indicate that the Packet has been + * received OK and that the LTT must set the ACK + * bit in the next outward bound Packet + * and re-set by LTT's after xmit. + * + * Gets shoved into rx_status + ************************************************/ +#define PHB_RX_LAST_PKT_ACKED ((ushort) 0x080) + +/******************************************************* + * The Rx TOGGLE bit. + * Stuffed into rx_status by RXPKT + ******************************************************/ +#define PHB_RX_DATA_WNDW ((ushort) 0x040) + +/******************************************************* + * The Rx TOGGLE bit. Matches the setting in PKT.H + * Stuffed into rx_status + ******************************************************/ +#define PHB_RX_TGL ((ushort) 0x2000) + + +/************************************************* + * This bit is set by the LRT to indicate that + * an ACK (packet) must be returned. + * + * Gets shoved into tx_status + ************************************************/ +#define PHB_TX_SEND_PKT_ACK ((ushort) 0x08) + +/************************************************* + * Set by LTT to indicate that an ACK is required + *************************************************/ +#define PHB_TX_ACK_RQRD ((ushort) 0x01) + + +/******************************************************* + * The Tx TOGGLE bit. + * Stuffed into tx_status by RXPKT from the PKT WndW + * field. Looked by the LTT when the NEXT Packet + * is going to be sent. + ******************************************************/ +#define PHB_TX_DATA_WNDW ((ushort) 0x04) + + +/******************************************************* + * The Tx TOGGLE bit. Matches the setting in PKT.H + * Stuffed into tx_status + ******************************************************/ +#define PHB_TX_TGL ((ushort) 0x02) + +/******************************************************* + * Request intr bit. Set when the queue has gone quiet + * and the PHB has requested an interrupt. + ******************************************************/ +#define PHB_TX_INTR ((ushort) 0x100) + +/******************************************************* + * SET if the PHB cannot send any more data down the + * Link + ******************************************************/ +#define PHB_TX_HANDSHAKE ((ushort) 0x010) + + +#define RUP_SEND_WNDW ((ushort) 0x08) ; + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/rio.h b/drivers/staging/generic_serial/rio/rio.h new file mode 100644 index 0000000..1bf3622 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rio.h @@ -0,0 +1,208 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 1998 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rio.h +** SID : 1.3 +** Last Modified : 11/6/98 11:34:13 +** Retrieved : 11/6/98 11:34:22 +** +** ident @(#)rio.h 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_rio_h__ +#define __rio_rio_h__ + +/* +** Maximum numbers of things +*/ +#define RIO_SLOTS 4 /* number of configuration slots */ +#define RIO_HOSTS 4 /* number of hosts that can be found */ +#define PORTS_PER_HOST 128 /* number of ports per host */ +#define LINKS_PER_UNIT 4 /* number of links from a host */ +#define RIO_PORTS (PORTS_PER_HOST * RIO_HOSTS) /* max. no. of ports */ +#define RTAS_PER_HOST (MAX_RUP) /* number of RTAs per host */ +#define PORTS_PER_RTA (PORTS_PER_HOST/RTAS_PER_HOST) /* ports on a rta */ +#define PORTS_PER_MODULE 4 /* number of ports on a plug-in module */ + /* number of modules on an RTA */ +#define MODULES_PER_RTA (PORTS_PER_RTA/PORTS_PER_MODULE) +#define MAX_PRODUCT 16 /* numbr of different product codes */ +#define MAX_MODULE_TYPES 16 /* number of different types of module */ + +#define RIO_CONTROL_DEV 128 /* minor number of host/control device */ +#define RIO_INVALID_MAJOR 0 /* test first host card's major no for validity */ + +/* +** number of RTAs that can be bound to a master +*/ +#define MAX_RTA_BINDINGS (MAX_RUP * RIO_HOSTS) + +/* +** Unit types +*/ +#define PC_RTA16 0x90000000 +#define PC_RTA8 0xe0000000 +#define TYPE_HOST 0 +#define TYPE_RTA8 1 +#define TYPE_RTA16 2 + +/* +** Flag values returned by functions +*/ + +#define RIO_FAIL -1 + +/* +** SysPort value for something that hasn't any ports +*/ +#define NO_PORT 0xFFFFFFFF + +/* +** Unit ID Of all hosts +*/ +#define HOST_ID 0 + +/* +** Break bytes into nybles +*/ +#define LONYBLE(X) ((X) & 0xF) +#define HINYBLE(X) (((X)>>4) & 0xF) + +/* +** Flag values passed into some functions +*/ +#define DONT_SLEEP 0 +#define OK_TO_SLEEP 1 + +#define DONT_PRINT 1 +#define DO_PRINT 0 + +#define PRINT_TO_LOG_CONS 0 +#define PRINT_TO_CONS 1 +#define PRINT_TO_LOG 2 + +/* +** Timeout has trouble with times of less than 3 ticks... +*/ +#define MIN_TIMEOUT 3 + +/* +** Generally useful constants +*/ + +#define HUNDRED_MS ((HZ/10)?(HZ/10):1) +#define ONE_MEG 0x100000 +#define SIXTY_FOUR_K 0x10000 + +#define RIO_AT_MEM_SIZE SIXTY_FOUR_K +#define RIO_EISA_MEM_SIZE SIXTY_FOUR_K +#define RIO_MCA_MEM_SIZE SIXTY_FOUR_K + +#define COOK_WELL 0 +#define COOK_MEDIUM 1 +#define COOK_RAW 2 + +/* +** Pointer manipulation stuff +** RIO_PTR takes hostp->Caddr and the offset into the DP RAM area +** and produces a UNIX caddr_t (pointer) to the object +** RIO_OBJ takes hostp->Caddr and a UNIX pointer to an object and +** returns the offset into the DP RAM area. +*/ +#define RIO_PTR(C,O) (((unsigned char __iomem *)(C))+(0xFFFF&(O))) +#define RIO_OFF(C,O) ((unsigned char __iomem *)(O)-(unsigned char __iomem *)(C)) + +/* +** How to convert from various different device number formats: +** DEV is a dev number, as passed to open, close etc - NOT a minor +** number! +**/ + +#define RIO_MODEM_MASK 0x1FF +#define RIO_MODEM_BIT 0x200 +#define RIO_UNMODEM(DEV) (MINOR(DEV) & RIO_MODEM_MASK) +#define RIO_ISMODEM(DEV) (MINOR(DEV) & RIO_MODEM_BIT) +#define RIO_PORT(DEV,FIRST_MAJ) ( (MAJOR(DEV) - FIRST_MAJ) * PORTS_PER_HOST) \ + + MINOR(DEV) +#define CSUM(pkt_ptr) (((u16 *)(pkt_ptr))[0] + ((u16 *)(pkt_ptr))[1] + \ + ((u16 *)(pkt_ptr))[2] + ((u16 *)(pkt_ptr))[3] + \ + ((u16 *)(pkt_ptr))[4] + ((u16 *)(pkt_ptr))[5] + \ + ((u16 *)(pkt_ptr))[6] + ((u16 *)(pkt_ptr))[7] + \ + ((u16 *)(pkt_ptr))[8] + ((u16 *)(pkt_ptr))[9] ) + +#define RIO_LINK_ENABLE 0x80FF /* FF is a hack, mainly for Mips, to */ + /* prevent a really stupid race condition. */ + +#define NOT_INITIALISED 0 +#define INITIALISED 1 + +#define NOT_POLLING 0 +#define POLLING 1 + +#define NOT_CHANGED 0 +#define CHANGED 1 + +#define NOT_INUSE 0 + +#define DISCONNECT 0 +#define CONNECT 1 + +/* ------ Control Codes ------ */ + +#define CONTROL '^' +#define IFOAD ( CONTROL + 1 ) +#define IDENTIFY ( CONTROL + 2 ) +#define ZOMBIE ( CONTROL + 3 ) +#define UFOAD ( CONTROL + 4 ) +#define IWAIT ( CONTROL + 5 ) + +#define IFOAD_MAGIC 0xF0AD /* of course */ +#define ZOMBIE_MAGIC (~0xDEAD) /* not dead -> zombie */ +#define UFOAD_MAGIC 0xD1E /* kill-your-neighbour */ +#define IWAIT_MAGIC 0xB1DE /* Bide your time */ + +/* ------ Error Codes ------ */ + +#define E_NO_ERROR ((ushort) 0) + +/* ------ Free Lists ------ */ + +struct rio_free_list { + u16 next; + u16 prev; +}; + +/* NULL for card side linked lists */ +#define TPNULL ((ushort)(0x8000)) +/* We can add another packet to a transmit queue if the packet pointer pointed + * to by the TxAdd pointer has PKT_IN_USE clear in its address. */ +#define PKT_IN_USE 0x1 + +/* ------ Topology ------ */ + +struct Top { + u8 Unit; + u8 Link; +}; + +#endif /* __rio_h__ */ diff --git a/drivers/staging/generic_serial/rio/rio_linux.c b/drivers/staging/generic_serial/rio/rio_linux.c new file mode 100644 index 0000000..5e33293 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rio_linux.c @@ -0,0 +1,1204 @@ + +/* rio_linux.c -- Linux driver for the Specialix RIO series cards. + * + * + * (C) 1999 R.E.Wolff@BitWizard.nl + * + * Specialix pays for the development and support of this driver. + * Please DO contact support@specialix.co.uk if you require + * support. But please read the documentation (rio.txt) first. + * + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * */ + +#include <linux/module.h> +#include <linux/kdev_t.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/miscdevice.h> +#include <linux/init.h> + +#include <linux/generic_serial.h> +#include <asm/uaccess.h> + +#include "linux_compat.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "param.h" +#include "protsts.h" +#include "rioboard.h" + + +#include "rio_linux.h" + +/* I don't think that this driver can handle more than 512 ports on +one machine. Specialix specifies max 4 boards in one machine. I don't +know why. If you want to try anyway you'll have to increase the number +of boards in rio.h. You'll have to allocate more majors if you need +more than 512 ports.... */ + +#ifndef RIO_NORMAL_MAJOR0 +/* This allows overriding on the compiler commandline, or in a "major.h" + include or something like that */ +#define RIO_NORMAL_MAJOR0 154 +#define RIO_NORMAL_MAJOR1 156 +#endif + +#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 +#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000 +#endif + +#ifndef RIO_WINDOW_LEN +#define RIO_WINDOW_LEN 0x10000 +#endif + + +/* Configurable options: + (Don't be too sure that it'll work if you toggle them) */ + +/* Am I paranoid or not ? ;-) */ +#undef RIO_PARANOIA_CHECK + + +/* 20 -> 2000 per second. The card should rate-limit interrupts at 1000 + Hz, but it is user configurable. I don't recommend going above 1000 + Hz. The interrupt ratelimit might trigger if the interrupt is + shared with a very active other device. + undef this if you want to disable the check.... +*/ +#define IRQ_RATE_LIMIT 200 + + +/* These constants are derived from SCO Source */ +static DEFINE_MUTEX(rio_fw_mutex); +static struct Conf + RIOConf = { + /* locator */ "RIO Config here", + /* startuptime */ HZ * 2, + /* how long to wait for card to run */ + /* slowcook */ 0, + /* TRUE -> always use line disc. */ + /* intrpolltime */ 1, + /* The frequency of OUR polls */ + /* breakinterval */ 25, + /* x10 mS XXX: units seem to be 1ms not 10! -- REW */ + /* timer */ 10, + /* mS */ + /* RtaLoadBase */ 0x7000, + /* HostLoadBase */ 0x7C00, + /* XpHz */ 5, + /* number of Xprint hits per second */ + /* XpCps */ 120, + /* Xprint characters per second */ + /* XpOn */ "\033d#", + /* start Xprint for a wyse 60 */ + /* XpOff */ "\024", + /* end Xprint for a wyse 60 */ + /* MaxXpCps */ 2000, + /* highest Xprint speed */ + /* MinXpCps */ 10, + /* slowest Xprint speed */ + /* SpinCmds */ 1, + /* non-zero for mega fast boots */ + /* First Addr */ 0x0A0000, + /* First address to look at */ + /* Last Addr */ 0xFF0000, + /* Last address looked at */ + /* BufferSize */ 1024, + /* Bytes per port of buffering */ + /* LowWater */ 256, + /* how much data left before wakeup */ + /* LineLength */ 80, + /* how wide is the console? */ + /* CmdTimeout */ HZ, + /* how long a close command may take */ +}; + + + + +/* Function prototypes */ + +static void rio_disable_tx_interrupts(void *ptr); +static void rio_enable_tx_interrupts(void *ptr); +static void rio_disable_rx_interrupts(void *ptr); +static void rio_enable_rx_interrupts(void *ptr); +static int rio_carrier_raised(struct tty_port *port); +static void rio_shutdown_port(void *ptr); +static int rio_set_real_termios(void *ptr); +static void rio_hungup(void *ptr); +static void rio_close(void *ptr); +static int rio_chars_in_buffer(void *ptr); +static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int rio_init_drivers(void); + +static void my_hd(void *addr, int len); + +static struct tty_driver *rio_driver, *rio_driver2; + +/* The name "p" is a bit non-descript. But that's what the rio-lynxos +sources use all over the place. */ +struct rio_info *p; + +int rio_debug; + + +/* You can have the driver poll your card. + - Set rio_poll to 1 to poll every timer tick (10ms on Intel). + This is used when the card cannot use an interrupt for some reason. +*/ +static int rio_poll = 1; + + +/* These are the only open spaces in my computer. Yours may have more + or less.... */ +static int rio_probe_addrs[] = { 0xc0000, 0xd0000, 0xe0000 }; + +#define NR_RIO_ADDRS ARRAY_SIZE(rio_probe_addrs) + + +/* Set the mask to all-ones. This alas, only supports 32 interrupts. + Some architectures may need more. -- Changed to LONG to + support up to 64 bits on 64bit architectures. -- REW 20/06/99 */ +static long rio_irqmask = -1; + +MODULE_AUTHOR("Rogier Wolff <R.E.Wolff@bitwizard.nl>, Patrick van de Lageweg <patrick@bitwizard.nl>"); +MODULE_DESCRIPTION("RIO driver"); +MODULE_LICENSE("GPL"); +module_param(rio_poll, int, 0); +module_param(rio_debug, int, 0644); +module_param(rio_irqmask, long, 0); + +static struct real_driver rio_real_driver = { + rio_disable_tx_interrupts, + rio_enable_tx_interrupts, + rio_disable_rx_interrupts, + rio_enable_rx_interrupts, + rio_shutdown_port, + rio_set_real_termios, + rio_chars_in_buffer, + rio_close, + rio_hungup, + NULL +}; + +/* + * Firmware loader driver specific routines + * + */ + +static const struct file_operations rio_fw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rio_fw_ioctl, + .llseek = noop_llseek, +}; + +static struct miscdevice rio_fw_device = { + RIOCTL_MISC_MINOR, "rioctl", &rio_fw_fops +}; + + + + + +#ifdef RIO_PARANOIA_CHECK + +/* This doesn't work. Who's paranoid around here? Not me! */ + +static inline int rio_paranoia_check(struct rio_port const *port, char *name, const char *routine) +{ + + static const char *badmagic = KERN_ERR "rio: Warning: bad rio port magic number for device %s in %s\n"; + static const char *badinfo = KERN_ERR "rio: Warning: null rio port for device %s in %s\n"; + + if (!port) { + printk(badinfo, name, routine); + return 1; + } + if (port->magic != RIO_MAGIC) { + printk(badmagic, name, routine); + return 1; + } + + return 0; +} +#else +#define rio_paranoia_check(a,b,c) 0 +#endif + + +#ifdef DEBUG +static void my_hd(void *ad, int len) +{ + int i, j, ch; + unsigned char *addr = ad; + + for (i = 0; i < len; i += 16) { + rio_dprintk(RIO_DEBUG_PARAM, "%08lx ", (unsigned long) addr + i); + for (j = 0; j < 16; j++) { + rio_dprintk(RIO_DEBUG_PARAM, "%02x %s", addr[j + i], (j == 7) ? " " : ""); + } + for (j = 0; j < 16; j++) { + ch = addr[j + i]; + rio_dprintk(RIO_DEBUG_PARAM, "%c", (ch < 0x20) ? '.' : ((ch > 0x7f) ? '.' : ch)); + } + rio_dprintk(RIO_DEBUG_PARAM, "\n"); + } +} +#else +#define my_hd(ad,len) do{/* nothing*/ } while (0) +#endif + + +/* Delay a number of jiffies, allowing a signal to interrupt */ +int RIODelay(struct Port *PortP, int njiffies) +{ + func_enter(); + + rio_dprintk(RIO_DEBUG_DELAY, "delaying %d jiffies\n", njiffies); + msleep_interruptible(jiffies_to_msecs(njiffies)); + func_exit(); + + if (signal_pending(current)) + return RIO_FAIL; + else + return !RIO_FAIL; +} + + +/* Delay a number of jiffies, disallowing a signal to interrupt */ +int RIODelay_ni(struct Port *PortP, int njiffies) +{ + func_enter(); + + rio_dprintk(RIO_DEBUG_DELAY, "delaying %d jiffies (ni)\n", njiffies); + msleep(jiffies_to_msecs(njiffies)); + func_exit(); + return !RIO_FAIL; +} + +void rio_copy_to_card(void *from, void __iomem *to, int len) +{ + rio_copy_toio(to, from, len); +} + +int rio_minor(struct tty_struct *tty) +{ + return tty->index + ((tty->driver == rio_driver) ? 0 : 256); +} + +static int rio_set_real_termios(void *ptr) +{ + return RIOParam((struct Port *) ptr, RIOC_CONFIG, 1, 1); +} + + +static void rio_reset_interrupt(struct Host *HostP) +{ + func_enter(); + + switch (HostP->Type) { + case RIO_AT: + case RIO_MCA: + case RIO_PCI: + writeb(0xFF, &HostP->ResetInt); + } + + func_exit(); +} + + +static irqreturn_t rio_interrupt(int irq, void *ptr) +{ + struct Host *HostP; + func_enter(); + + HostP = ptr; /* &p->RIOHosts[(long)ptr]; */ + rio_dprintk(RIO_DEBUG_IFLOW, "rio: enter rio_interrupt (%d/%d)\n", irq, HostP->Ivec); + + /* AAargh! The order in which to do these things is essential and + not trivial. + + - hardware twiddling goes before "recursive". Otherwise when we + poll the card, and a recursive interrupt happens, we won't + ack the card, so it might keep on interrupting us. (especially + level sensitive interrupt systems like PCI). + + - Rate limit goes before hardware twiddling. Otherwise we won't + catch a card that has gone bonkers. + + - The "initialized" test goes after the hardware twiddling. Otherwise + the card will stick us in the interrupt routine again. + + - The initialized test goes before recursive. + */ + + rio_dprintk(RIO_DEBUG_IFLOW, "rio: We've have noticed the interrupt\n"); + if (HostP->Ivec == irq) { + /* Tell the card we've noticed the interrupt. */ + rio_reset_interrupt(HostP); + } + + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) + return IRQ_HANDLED; + + if (test_and_set_bit(RIO_BOARD_INTR_LOCK, &HostP->locks)) { + printk(KERN_ERR "Recursive interrupt! (host %p/irq%d)\n", ptr, HostP->Ivec); + return IRQ_HANDLED; + } + + RIOServiceHost(p, HostP); + + rio_dprintk(RIO_DEBUG_IFLOW, "riointr() doing host %p type %d\n", ptr, HostP->Type); + + clear_bit(RIO_BOARD_INTR_LOCK, &HostP->locks); + rio_dprintk(RIO_DEBUG_IFLOW, "rio: exit rio_interrupt (%d/%d)\n", irq, HostP->Ivec); + func_exit(); + return IRQ_HANDLED; +} + + +static void rio_pollfunc(unsigned long data) +{ + func_enter(); + + rio_interrupt(0, &p->RIOHosts[data]); + mod_timer(&p->RIOHosts[data].timer, jiffies + rio_poll); + + func_exit(); +} + + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the generic_serial driver * + * ********************************************************************** */ + +/* Ehhm. I don't know how to fiddle with interrupts on the Specialix + cards. .... Hmm. Ok I figured it out. You don't. -- REW */ + +static void rio_disable_tx_interrupts(void *ptr) +{ + func_enter(); + + /* port->gs.port.flags &= ~GS_TX_INTEN; */ + + func_exit(); +} + + +static void rio_enable_tx_interrupts(void *ptr) +{ + struct Port *PortP = ptr; + /* int hn; */ + + func_enter(); + + /* hn = PortP->HostP - p->RIOHosts; + + rio_dprintk (RIO_DEBUG_TTY, "Pushing host %d\n", hn); + rio_interrupt (-1,(void *) hn, NULL); */ + + RIOTxEnable((char *) PortP); + + /* + * In general we cannot count on "tx empty" interrupts, although + * the interrupt routine seems to be able to tell the difference. + */ + PortP->gs.port.flags &= ~GS_TX_INTEN; + + func_exit(); +} + + +static void rio_disable_rx_interrupts(void *ptr) +{ + func_enter(); + func_exit(); +} + +static void rio_enable_rx_interrupts(void *ptr) +{ + /* struct rio_port *port = ptr; */ + func_enter(); + func_exit(); +} + + +/* Jeez. Isn't this simple? */ +static int rio_carrier_raised(struct tty_port *port) +{ + struct Port *PortP = container_of(port, struct Port, gs.port); + int rv; + + func_enter(); + rv = (PortP->ModemState & RIOC_MSVR1_CD) != 0; + + rio_dprintk(RIO_DEBUG_INIT, "Getting CD status: %d\n", rv); + + func_exit(); + return rv; +} + + +/* Jeez. Isn't this simple? Actually, we can sync with the actual port + by just pushing stuff into the queue going to the port... */ +static int rio_chars_in_buffer(void *ptr) +{ + func_enter(); + + func_exit(); + return 0; +} + + +/* Nothing special here... */ +static void rio_shutdown_port(void *ptr) +{ + struct Port *PortP; + + func_enter(); + + PortP = (struct Port *) ptr; + PortP->gs.port.tty = NULL; + 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 rio_hungup(void *ptr) +{ + struct Port *PortP; + + func_enter(); + + PortP = (struct Port *) ptr; + PortP->gs.port.tty = NULL; + + func_exit(); +} + + +/* The standard serial_close would become shorter if you'd wrap it like + this. + rs_close (...){save_flags;cli;real_close();dec_use_count;restore_flags;} + */ +static void rio_close(void *ptr) +{ + struct Port *PortP; + + func_enter(); + + PortP = (struct Port *) ptr; + + riotclose(ptr); + + if (PortP->gs.port.count) { + printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.port.count); + PortP->gs.port.count = 0; + } + + PortP->gs.port.tty = NULL; + func_exit(); +} + + + +static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int rc = 0; + func_enter(); + + /* The "dev" argument isn't used. */ + mutex_lock(&rio_fw_mutex); + rc = riocontrol(p, 0, cmd, arg, capable(CAP_SYS_ADMIN)); + mutex_unlock(&rio_fw_mutex); + + func_exit(); + return rc; +} + +extern int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg); + +static int rio_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc; + struct Port *PortP; + int ival; + + func_enter(); + + PortP = (struct Port *) tty->driver_data; + + rc = 0; + switch (cmd) { + case TIOCSSOFTCAR: + if ((rc = get_user(ival, (unsigned __user *) argp)) == 0) { + tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); + } + break; + case TIOCGSERIAL: + rc = -EFAULT; + if (access_ok(VERIFY_WRITE, argp, sizeof(struct serial_struct))) + rc = gs_getserial(&PortP->gs, argp); + break; + case TCSBRK: + if (PortP->State & RIO_DELETED) { + rio_dprintk(RIO_DEBUG_TTY, "BREAK on deleted RTA\n"); + rc = -EIO; + } else { + if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2, 250) == + RIO_FAIL) { + rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); + rc = -EIO; + } + } + break; + case TCSBRKP: + if (PortP->State & RIO_DELETED) { + rio_dprintk(RIO_DEBUG_TTY, "BREAK on deleted RTA\n"); + rc = -EIO; + } else { + int l; + l = arg ? arg * 100 : 250; + if (l > 255) + l = 255; + if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2, + arg ? arg * 100 : 250) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); + rc = -EIO; + } + } + break; + case TIOCSSERIAL: + rc = -EFAULT; + if (access_ok(VERIFY_READ, argp, sizeof(struct serial_struct))) + rc = gs_setserial(&PortP->gs, argp); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + func_exit(); + return rc; +} + + +/* The throttle/unthrottle scheme for the Specialix card is different + * from other drivers and deserves some explanation. + * The Specialix hardware takes care of XON/XOFF + * and CTS/RTS flow control itself. This means that all we have to + * do when signalled by the upper tty layer to throttle/unthrottle is + * to make a note of it here. When we come to read characters from the + * rx buffers on the card (rio_receive_chars()) we look to see if the + * upper layer can accept more (as noted here in rio_rx_throt[]). + * If it can't we simply don't remove chars from the cards buffer. + * When the tty layer can accept chars, we again note that here and when + * rio_receive_chars() is called it will remove them from the cards buffer. + * The card will notice that a ports buffer has drained below some low + * water mark and will unflow control the line itself, using whatever + * flow control scheme is in use for that port. -- Simon Allen + */ + +static void rio_throttle(struct tty_struct *tty) +{ + struct Port *port = (struct Port *) tty->driver_data; + + func_enter(); + /* If the port is using any type of input flow + * control then throttle the port. + */ + + if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) { + port->State |= RIO_THROTTLE_RX; + } + + func_exit(); +} + + +static void rio_unthrottle(struct tty_struct *tty) +{ + struct Port *port = (struct Port *) tty->driver_data; + + func_enter(); + /* Always unthrottle even if flow control is not enabled on + * this port in case we disabled flow control while the port + * was throttled + */ + + port->State &= ~RIO_THROTTLE_RX; + + func_exit(); + return; +} + + + + + +/* ********************************************************************** * + * Here are the initialization routines. * + * ********************************************************************** */ + + +static struct vpd_prom *get_VPD_PROM(struct Host *hp) +{ + static struct vpd_prom vpdp; + char *p; + int i; + + func_enter(); + rio_dprintk(RIO_DEBUG_PROBE, "Going to verify vpd prom at %p.\n", hp->Caddr + RIO_VPD_ROM); + + p = (char *) &vpdp; + for (i = 0; i < sizeof(struct vpd_prom); i++) + *p++ = readb(hp->Caddr + RIO_VPD_ROM + i * 2); + /* read_rio_byte (hp, RIO_VPD_ROM + i*2); */ + + /* Terminate the identifier string. + *** requires one extra byte in struct vpd_prom *** */ + *p++ = 0; + + if (rio_debug & RIO_DEBUG_PROBE) + my_hd((char *) &vpdp, 0x20); + + func_exit(); + + return &vpdp; +} + +static const struct tty_operations rio_ops = { + .open = riotopen, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .flush_buffer = gs_flush_buffer, + .ioctl = rio_ioctl, + .throttle = rio_throttle, + .unthrottle = rio_unthrottle, + .set_termios = gs_set_termios, + .stop = gs_stop, + .start = gs_start, + .hangup = gs_hangup, +}; + +static int rio_init_drivers(void) +{ + int error = -ENOMEM; + + rio_driver = alloc_tty_driver(256); + if (!rio_driver) + goto out; + rio_driver2 = alloc_tty_driver(256); + if (!rio_driver2) + goto out1; + + func_enter(); + + rio_driver->owner = THIS_MODULE; + rio_driver->driver_name = "specialix_rio"; + rio_driver->name = "ttySR"; + rio_driver->major = RIO_NORMAL_MAJOR0; + rio_driver->type = TTY_DRIVER_TYPE_SERIAL; + rio_driver->subtype = SERIAL_TYPE_NORMAL; + rio_driver->init_termios = tty_std_termios; + rio_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rio_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(rio_driver, &rio_ops); + + rio_driver2->owner = THIS_MODULE; + rio_driver2->driver_name = "specialix_rio"; + rio_driver2->name = "ttySR"; + rio_driver2->major = RIO_NORMAL_MAJOR1; + rio_driver2->type = TTY_DRIVER_TYPE_SERIAL; + rio_driver2->subtype = SERIAL_TYPE_NORMAL; + rio_driver2->init_termios = tty_std_termios; + rio_driver2->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rio_driver2->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(rio_driver2, &rio_ops); + + rio_dprintk(RIO_DEBUG_INIT, "set_termios = %p\n", gs_set_termios); + + if ((error = tty_register_driver(rio_driver))) + goto out2; + if ((error = tty_register_driver(rio_driver2))) + goto out3; + func_exit(); + return 0; + out3: + tty_unregister_driver(rio_driver); + out2: + put_tty_driver(rio_driver2); + out1: + put_tty_driver(rio_driver); + out: + printk(KERN_ERR "rio: Couldn't register a rio driver, error = %d\n", error); + return 1; +} + +static const struct tty_port_operations rio_port_ops = { + .carrier_raised = rio_carrier_raised, +}; + +static int rio_init_datastructures(void) +{ + int i; + struct Port *port; + func_enter(); + + /* Many drivers statically allocate the maximum number of ports + There is no reason not to allocate them dynamically. Is there? -- REW */ + /* However, the RIO driver allows users to configure their first + RTA as the ports numbered 504-511. We therefore need to allocate + the whole range. :-( -- REW */ + +#define RI_SZ sizeof(struct rio_info) +#define HOST_SZ sizeof(struct Host) +#define PORT_SZ sizeof(struct Port *) +#define TMIO_SZ sizeof(struct termios *) + rio_dprintk(RIO_DEBUG_INIT, "getting : %Zd %Zd %Zd %Zd %Zd bytes\n", RI_SZ, RIO_HOSTS * HOST_SZ, RIO_PORTS * PORT_SZ, RIO_PORTS * TMIO_SZ, RIO_PORTS * TMIO_SZ); + + if (!(p = kzalloc(RI_SZ, GFP_KERNEL))) + goto free0; + if (!(p->RIOHosts = kzalloc(RIO_HOSTS * HOST_SZ, GFP_KERNEL))) + goto free1; + if (!(p->RIOPortp = kzalloc(RIO_PORTS * PORT_SZ, GFP_KERNEL))) + goto free2; + p->RIOConf = RIOConf; + rio_dprintk(RIO_DEBUG_INIT, "Got : %p %p %p\n", p, p->RIOHosts, p->RIOPortp); + +#if 1 + for (i = 0; i < RIO_PORTS; i++) { + port = p->RIOPortp[i] = kzalloc(sizeof(struct Port), GFP_KERNEL); + if (!port) { + goto free6; + } + rio_dprintk(RIO_DEBUG_INIT, "initing port %d (%d)\n", i, port->Mapped); + tty_port_init(&port->gs.port); + port->gs.port.ops = &rio_port_ops; + port->PortNum = i; + port->gs.magic = RIO_MAGIC; + port->gs.close_delay = HZ / 2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &rio_real_driver; + spin_lock_init(&port->portSem); + } +#else + /* We could postpone initializing them to when they are configured. */ +#endif + + + + if (rio_debug & RIO_DEBUG_INIT) { + my_hd(&rio_real_driver, sizeof(rio_real_driver)); + } + + + func_exit(); + return 0; + + free6:for (i--; i >= 0; i--) + kfree(p->RIOPortp[i]); +/*free5: + free4: + free3:*/ kfree(p->RIOPortp); + free2:kfree(p->RIOHosts); + free1: + rio_dprintk(RIO_DEBUG_INIT, "Not enough memory! %p %p %p\n", p, p->RIOHosts, p->RIOPortp); + kfree(p); + free0: + return -ENOMEM; +} + +static void __exit rio_release_drivers(void) +{ + func_enter(); + tty_unregister_driver(rio_driver2); + tty_unregister_driver(rio_driver); + put_tty_driver(rio_driver2); + put_tty_driver(rio_driver); + func_exit(); +} + + +#ifdef CONFIG_PCI + /* This was written for SX, but applies to RIO too... + (including bugs....) + + There is another bit besides Bit 17. Turning that bit off + (on boards shipped with the fix in the eeprom) results in a + hang on the next access to the card. + */ + + /******************************************************** + * Setting bit 17 in the CNTRL register of the PLX 9050 * + * chip forces a retry on writes while a read is pending.* + * This is to prevent the card locking up on Intel Xeon * + * multiprocessor systems with the NX chipset. -- NV * + ********************************************************/ + +/* Newer cards are produced with this bit set from the configuration + EEprom. As the bit is read/write for the CPU, we can fix it here, + if we detect that it isn't set correctly. -- REW */ + +static void fix_rio_pci(struct pci_dev *pdev) +{ + unsigned long hwbase; + unsigned char __iomem *rebase; + unsigned int t; + +#define CNTRL_REG_OFFSET 0x50 +#define CNTRL_REG_GOODVALUE 0x18260000 + + hwbase = pci_resource_start(pdev, 0); + rebase = ioremap(hwbase, 0x80); + t = readl(rebase + CNTRL_REG_OFFSET); + if (t != CNTRL_REG_GOODVALUE) { + printk(KERN_DEBUG "rio: performing cntrl reg fix: %08x -> %08x\n", t, CNTRL_REG_GOODVALUE); + writel(CNTRL_REG_GOODVALUE, rebase + CNTRL_REG_OFFSET); + } + iounmap(rebase); +} +#endif + + +static int __init rio_init(void) +{ + int found = 0; + int i; + struct Host *hp; + int retval; + struct vpd_prom *vpdp; + int okboard; + +#ifdef CONFIG_PCI + struct pci_dev *pdev = NULL; + unsigned short tshort; +#endif + + func_enter(); + rio_dprintk(RIO_DEBUG_INIT, "Initing rio module... (rio_debug=%d)\n", rio_debug); + + if (abs((long) (&rio_debug) - rio_debug) < 0x10000) { + printk(KERN_WARNING "rio: rio_debug is an address, instead of a value. " "Assuming -1. Was %x/%p.\n", rio_debug, &rio_debug); + rio_debug = -1; + } + + if (misc_register(&rio_fw_device) < 0) { + printk(KERN_ERR "RIO: Unable to register firmware loader driver.\n"); + return -EIO; + } + + retval = rio_init_datastructures(); + if (retval < 0) { + misc_deregister(&rio_fw_device); + return retval; + } +#ifdef CONFIG_PCI + /* First look for the JET devices: */ + while ((pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, pdev))) { + u32 tint; + + if (pci_enable_device(pdev)) + continue; + + /* Specialix has a whole bunch of cards with + 0x2000 as the device ID. They say its because + the standard requires it. Stupid standard. */ + /* It seems that reading a word doesn't work reliably on 2.0. + Also, reading a non-aligned dword doesn't work. So we read the + whole dword at 0x2c and extract the word at 0x2e (SUBSYSTEM_ID) + ourselves */ + pci_read_config_dword(pdev, 0x2c, &tint); + tshort = (tint >> 16) & 0xffff; + rio_dprintk(RIO_DEBUG_PROBE, "Got a specialix card: %x.\n", tint); + if (tshort != 0x0100) { + rio_dprintk(RIO_DEBUG_PROBE, "But it's not a RIO card (%d)...\n", tshort); + continue; + } + rio_dprintk(RIO_DEBUG_PROBE, "cp1\n"); + + hp = &p->RIOHosts[p->RIONumHosts]; + hp->PaddrP = pci_resource_start(pdev, 2); + hp->Ivec = pdev->irq; + if (((1 << hp->Ivec) & rio_irqmask) == 0) + hp->Ivec = 0; + hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam __iomem *) hp->Caddr; + hp->Type = RIO_PCI; + hp->Copy = rio_copy_to_card; + hp->Mode = RIO_PCI_BOOT_FROM_RAM; + spin_lock_init(&hp->HostLock); + rio_reset_interrupt(hp); + rio_start_card_running(hp); + + rio_dprintk(RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *) p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr); + if (RIOBoardTest(p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr, RIO_PCI, 0) == 0) { + rio_dprintk(RIO_DEBUG_INIT, "Done RIOBoardTest\n"); + writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt); + p->RIOHosts[p->RIONumHosts].UniqueNum = + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0]) & 0xFF) << 0) | + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1]) & 0xFF) << 8) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2]) & 0xFF) << 16) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3]) & 0xFF) << 24); + rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum); + + fix_rio_pci(pdev); + + p->RIOHosts[p->RIONumHosts].pdev = pdev; + pci_dev_get(pdev); + + p->RIOLastPCISearch = 0; + p->RIONumHosts++; + found++; + } else { + iounmap(p->RIOHosts[p->RIONumHosts].Caddr); + p->RIOHosts[p->RIONumHosts].Caddr = NULL; + } + } + + /* Then look for the older PCI card.... : */ + + /* These older PCI cards have problems (only byte-mode access is + supported), which makes them a bit awkward to support. + They also have problems sharing interrupts. Be careful. + (The driver now refuses to share interrupts for these + cards. This should be sufficient). + */ + + /* Then look for the older RIO/PCI devices: */ + while ((pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_RIO, pdev))) { + if (pci_enable_device(pdev)) + continue; + +#ifdef CONFIG_RIO_OLDPCI + hp = &p->RIOHosts[p->RIONumHosts]; + hp->PaddrP = pci_resource_start(pdev, 0); + hp->Ivec = pdev->irq; + if (((1 << hp->Ivec) & rio_irqmask) == 0) + hp->Ivec = 0; + hp->Ivec |= 0x8000; /* Mark as non-sharable */ + hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam __iomem *) hp->Caddr; + hp->Type = RIO_PCI; + hp->Copy = rio_copy_to_card; + hp->Mode = RIO_PCI_BOOT_FROM_RAM; + spin_lock_init(&hp->HostLock); + + rio_dprintk(RIO_DEBUG_PROBE, "Ivec: %x\n", hp->Ivec); + rio_dprintk(RIO_DEBUG_PROBE, "Mode: %x\n", hp->Mode); + + rio_reset_interrupt(hp); + rio_start_card_running(hp); + rio_dprintk(RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *) p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr); + if (RIOBoardTest(p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr, RIO_PCI, 0) == 0) { + writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt); + p->RIOHosts[p->RIONumHosts].UniqueNum = + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0]) & 0xFF) << 0) | + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1]) & 0xFF) << 8) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2]) & 0xFF) << 16) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3]) & 0xFF) << 24); + rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum); + + p->RIOHosts[p->RIONumHosts].pdev = pdev; + pci_dev_get(pdev); + + p->RIOLastPCISearch = 0; + p->RIONumHosts++; + found++; + } else { + iounmap(p->RIOHosts[p->RIONumHosts].Caddr); + p->RIOHosts[p->RIONumHosts].Caddr = NULL; + } +#else + printk(KERN_ERR "Found an older RIO PCI card, but the driver is not " "compiled to support it.\n"); +#endif + } +#endif /* PCI */ + + /* Now probe for ISA cards... */ + for (i = 0; i < NR_RIO_ADDRS; i++) { + hp = &p->RIOHosts[p->RIONumHosts]; + hp->PaddrP = rio_probe_addrs[i]; + /* There was something about the IRQs of these cards. 'Forget what.--REW */ + hp->Ivec = 0; + hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam __iomem *) hp->Caddr; + hp->Type = RIO_AT; + hp->Copy = rio_copy_to_card; /* AT card PCI???? - PVDL + * -- YES! this is now a normal copy. Only the + * old PCI card uses the special PCI copy. + * Moreover, the ISA card will work with the + * special PCI copy anyway. -- REW */ + hp->Mode = 0; + spin_lock_init(&hp->HostLock); + + vpdp = get_VPD_PROM(hp); + rio_dprintk(RIO_DEBUG_PROBE, "Got VPD ROM\n"); + okboard = 0; + if ((strncmp(vpdp->identifier, RIO_ISA_IDENT, 16) == 0) || (strncmp(vpdp->identifier, RIO_ISA2_IDENT, 16) == 0) || (strncmp(vpdp->identifier, RIO_ISA3_IDENT, 16) == 0)) { + /* Board is present... */ + if (RIOBoardTest(hp->PaddrP, hp->Caddr, RIO_AT, 0) == 0) { + /* ... and feeling fine!!!! */ + rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum); + if (RIOAssignAT(p, hp->PaddrP, hp->Caddr, 0)) { + rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, host%d uniqid = %x.\n", p->RIONumHosts, p->RIOHosts[p->RIONumHosts - 1].UniqueNum); + okboard++; + found++; + } + } + + if (!okboard) { + iounmap(hp->Caddr); + hp->Caddr = NULL; + } + } + } + + + for (i = 0; i < p->RIONumHosts; i++) { + hp = &p->RIOHosts[i]; + if (hp->Ivec) { + int mode = IRQF_SHARED; + if (hp->Ivec & 0x8000) { + mode = 0; + hp->Ivec &= 0x7fff; + } + rio_dprintk(RIO_DEBUG_INIT, "Requesting interrupt hp: %p rio_interrupt: %d Mode: %x\n", hp, hp->Ivec, hp->Mode); + retval = request_irq(hp->Ivec, rio_interrupt, mode, "rio", hp); + rio_dprintk(RIO_DEBUG_INIT, "Return value from request_irq: %d\n", retval); + if (retval) { + printk(KERN_ERR "rio: Cannot allocate irq %d.\n", hp->Ivec); + hp->Ivec = 0; + } + rio_dprintk(RIO_DEBUG_INIT, "Got irq %d.\n", hp->Ivec); + if (hp->Ivec != 0) { + rio_dprintk(RIO_DEBUG_INIT, "Enabling interrupts on rio card.\n"); + hp->Mode |= RIO_PCI_INT_ENABLE; + } else + hp->Mode &= ~RIO_PCI_INT_ENABLE; + rio_dprintk(RIO_DEBUG_INIT, "New Mode: %x\n", hp->Mode); + rio_start_card_running(hp); + } + /* Init the timer "always" to make sure that it can safely be + deleted when we unload... */ + + setup_timer(&hp->timer, rio_pollfunc, i); + if (!hp->Ivec) { + rio_dprintk(RIO_DEBUG_INIT, "Starting polling at %dj intervals.\n", rio_poll); + mod_timer(&hp->timer, jiffies + rio_poll); + } + } + + if (found) { + rio_dprintk(RIO_DEBUG_INIT, "rio: total of %d boards detected.\n", found); + rio_init_drivers(); + } else { + /* deregister the misc device we created earlier */ + misc_deregister(&rio_fw_device); + } + + func_exit(); + return found ? 0 : -EIO; +} + + +static void __exit rio_exit(void) +{ + int i; + struct Host *hp; + + func_enter(); + + for (i = 0, hp = p->RIOHosts; i < p->RIONumHosts; i++, hp++) { + RIOHostReset(hp->Type, hp->CardP, hp->Slot); + if (hp->Ivec) { + free_irq(hp->Ivec, hp); + rio_dprintk(RIO_DEBUG_INIT, "freed irq %d.\n", hp->Ivec); + } + /* It is safe/allowed to del_timer a non-active timer */ + del_timer_sync(&hp->timer); + if (hp->Caddr) + iounmap(hp->Caddr); + if (hp->Type == RIO_PCI) + pci_dev_put(hp->pdev); + } + + if (misc_deregister(&rio_fw_device) < 0) { + printk(KERN_INFO "rio: couldn't deregister control-device\n"); + } + + + rio_dprintk(RIO_DEBUG_CLEANUP, "Cleaning up drivers\n"); + + rio_release_drivers(); + + /* Release dynamically allocated memory */ + kfree(p->RIOPortp); + kfree(p->RIOHosts); + kfree(p); + + func_exit(); +} + +module_init(rio_init); +module_exit(rio_exit); diff --git a/drivers/staging/generic_serial/rio/rio_linux.h b/drivers/staging/generic_serial/rio/rio_linux.h new file mode 100644 index 0000000..7f26cd7 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rio_linux.h @@ -0,0 +1,197 @@ + +/* + * rio_linux.h + * + * Copyright (C) 1998,1999,2000 R.E.Wolff@BitWizard.nl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * RIO serial driver. + * + * Version 1.0 -- July, 1999. + * + */ + +#define RIO_NBOARDS 4 +#define RIO_PORTSPERBOARD 128 +#define RIO_NPORTS (RIO_NBOARDS * RIO_PORTSPERBOARD) + +#define MODEM_SUPPORT + +#ifdef __KERNEL__ + +#define RIO_MAGIC 0x12345678 + + +struct vpd_prom { + unsigned short id; + char hwrev; + char hwass; + int uniqid; + char myear; + char mweek; + char hw_feature[5]; + char oem_id; + char identifier[16]; +}; + + +#define RIO_DEBUG_ALL 0xffffffff + +#define O_OTHER(tty) \ + ((O_OLCUC(tty)) ||\ + (O_ONLCR(tty)) ||\ + (O_OCRNL(tty)) ||\ + (O_ONOCR(tty)) ||\ + (O_ONLRET(tty)) ||\ + (O_OFILL(tty)) ||\ + (O_OFDEL(tty)) ||\ + (O_NLDLY(tty)) ||\ + (O_CRDLY(tty)) ||\ + (O_TABDLY(tty)) ||\ + (O_BSDLY(tty)) ||\ + (O_VTDLY(tty)) ||\ + (O_FFDLY(tty))) + +/* Same for input. */ +#define I_OTHER(tty) \ + ((I_INLCR(tty)) ||\ + (I_IGNCR(tty)) ||\ + (I_ICRNL(tty)) ||\ + (I_IUCLC(tty)) ||\ + (L_ISIG(tty))) + + +#endif /* __KERNEL__ */ + + +#define RIO_BOARD_INTR_LOCK 1 + + +#ifndef RIOCTL_MISC_MINOR +/* Allow others to gather this into "major.h" or something like that */ +#define RIOCTL_MISC_MINOR 169 +#endif + + +/* Allow us to debug "in the field" without requiring clients to + recompile.... */ +#if 1 +#define rio_spin_lock_irqsave(sem, flags) do { \ + rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlockirqsave: %p %s:%d\n", \ + sem, __FILE__, __LINE__);\ + spin_lock_irqsave(sem, flags);\ + } while (0) + +#define rio_spin_unlock_irqrestore(sem, flags) do { \ + rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlockirqrestore: %p %s:%d\n",\ + sem, __FILE__, __LINE__);\ + spin_unlock_irqrestore(sem, flags);\ + } while (0) + +#define rio_spin_lock(sem) do { \ + rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlock: %p %s:%d\n",\ + sem, __FILE__, __LINE__);\ + spin_lock(sem);\ + } while (0) + +#define rio_spin_unlock(sem) do { \ + rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlock: %p %s:%d\n",\ + sem, __FILE__, __LINE__);\ + spin_unlock(sem);\ + } while (0) +#else +#define rio_spin_lock_irqsave(sem, flags) \ + spin_lock_irqsave(sem, flags) + +#define rio_spin_unlock_irqrestore(sem, flags) \ + spin_unlock_irqrestore(sem, flags) + +#define rio_spin_lock(sem) \ + spin_lock(sem) + +#define rio_spin_unlock(sem) \ + spin_unlock(sem) + +#endif + + + +#ifdef CONFIG_RIO_OLDPCI +static inline void __iomem *rio_memcpy_toio(void __iomem *dummy, void __iomem *dest, void *source, int n) +{ + char __iomem *dst = dest; + char *src = source; + + while (n--) { + writeb(*src++, dst++); + (void) readb(dummy); + } + + return dest; +} + +static inline void __iomem *rio_copy_toio(void __iomem *dest, void *source, int n) +{ + char __iomem *dst = dest; + char *src = source; + + while (n--) + writeb(*src++, dst++); + + return dest; +} + + +static inline void *rio_memcpy_fromio(void *dest, void __iomem *source, int n) +{ + char *dst = dest; + char __iomem *src = source; + + while (n--) + *dst++ = readb(src++); + + return dest; +} + +#else +#define rio_memcpy_toio(dummy,dest,source,n) memcpy_toio(dest, source, n) +#define rio_copy_toio memcpy_toio +#define rio_memcpy_fromio memcpy_fromio +#endif + +#define DEBUG 1 + + +/* + 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... +*/ + +#ifdef DEBUG +#define rio_dprintk(f, str...) do { if (rio_debug & f) printk (str);} while (0) +#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s\n", __func__) +#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit %s\n", __func__) +#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s (port %d)\n",__func__, port->line) +#else +#define rio_dprintk(f, str...) /* nothing */ +#define func_enter() +#define func_exit() +#define func_enter2() +#endif diff --git a/drivers/staging/generic_serial/rio/rioboard.h b/drivers/staging/generic_serial/rio/rioboard.h new file mode 100644 index 0000000..2522300 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioboard.h @@ -0,0 +1,275 @@ +/************************************************************************/ +/* */ +/* Title : RIO Host Card Hardware Definitions */ +/* */ +/* Author : N.P.Vassallo */ +/* */ +/* Creation : 26th April 1999 */ +/* */ +/* Version : 1.0.0 */ +/* */ +/* Copyright : (c) Specialix International Ltd. 1999 * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * */ +/* Description : Prototypes, structures and definitions */ +/* describing the RIO board hardware */ +/* */ +/************************************************************************/ + +#ifndef _rioboard_h /* If RIOBOARD.H not already defined */ +#define _rioboard_h 1 + +/***************************************************************************** +*********************** *********************** +*********************** Hardware Control Registers *********************** +*********************** *********************** +*****************************************************************************/ + +/* Hardware Registers... */ + +#define RIO_REG_BASE 0x7C00 /* Base of control registers */ + +#define RIO_CONFIG RIO_REG_BASE + 0x0000 /* WRITE: Configuration Register */ +#define RIO_INTSET RIO_REG_BASE + 0x0080 /* WRITE: Interrupt Set */ +#define RIO_RESET RIO_REG_BASE + 0x0100 /* WRITE: Host Reset */ +#define RIO_INTRESET RIO_REG_BASE + 0x0180 /* WRITE: Interrupt Reset */ + +#define RIO_VPD_ROM RIO_REG_BASE + 0x0000 /* READ: Vital Product Data ROM */ +#define RIO_INTSTAT RIO_REG_BASE + 0x0080 /* READ: Interrupt Status (Jet boards only) */ +#define RIO_RESETSTAT RIO_REG_BASE + 0x0100 /* READ: Reset Status (Jet boards only) */ + +/* RIO_VPD_ROM definitions... */ +#define VPD_SLX_ID1 0x00 /* READ: Specialix Identifier #1 */ +#define VPD_SLX_ID2 0x01 /* READ: Specialix Identifier #2 */ +#define VPD_HW_REV 0x02 /* READ: Hardware Revision */ +#define VPD_HW_ASSEM 0x03 /* READ: Hardware Assembly Level */ +#define VPD_UNIQUEID4 0x04 /* READ: Unique Identifier #4 */ +#define VPD_UNIQUEID3 0x05 /* READ: Unique Identifier #3 */ +#define VPD_UNIQUEID2 0x06 /* READ: Unique Identifier #2 */ +#define VPD_UNIQUEID1 0x07 /* READ: Unique Identifier #1 */ +#define VPD_MANU_YEAR 0x08 /* READ: Year Of Manufacture (0 = 1970) */ +#define VPD_MANU_WEEK 0x09 /* READ: Week Of Manufacture (0 = week 1 Jan) */ +#define VPD_HWFEATURE1 0x0A /* READ: Hardware Feature Byte 1 */ +#define VPD_HWFEATURE2 0x0B /* READ: Hardware Feature Byte 2 */ +#define VPD_HWFEATURE3 0x0C /* READ: Hardware Feature Byte 3 */ +#define VPD_HWFEATURE4 0x0D /* READ: Hardware Feature Byte 4 */ +#define VPD_HWFEATURE5 0x0E /* READ: Hardware Feature Byte 5 */ +#define VPD_OEMID 0x0F /* READ: OEM Identifier */ +#define VPD_IDENT 0x10 /* READ: Identifier string (16 bytes) */ +#define VPD_IDENT_LEN 0x10 + +/* VPD ROM Definitions... */ +#define SLX_ID1 0x4D +#define SLX_ID2 0x98 + +#define PRODUCT_ID(a) ((a>>4)&0xF) /* Use to obtain Product ID from VPD_UNIQUEID1 */ + +#define ID_SX_ISA 0x2 +#define ID_RIO_EISA 0x3 +#define ID_SX_PCI 0x5 +#define ID_SX_EISA 0x7 +#define ID_RIO_RTA16 0x9 +#define ID_RIO_ISA 0xA +#define ID_RIO_MCA 0xB +#define ID_RIO_SBUS 0xC +#define ID_RIO_PCI 0xD +#define ID_RIO_RTA8 0xE + +/* Transputer bootstrap definitions... */ + +#define BOOTLOADADDR (0x8000 - 6) +#define BOOTINDICATE (0x8000 - 2) + +/* Firmware load position... */ + +#define FIRMWARELOADADDR 0x7C00 /* Firmware is loaded _before_ this address */ + +/***************************************************************************** +***************************** ***************************** +***************************** RIO (Rev1) ISA ***************************** +***************************** ***************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_ISA_IDENT "JBJGPGGHINSMJPJR" + +#define RIO_ISA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_ISA_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_ISA_CFG_IRQMASK 0x30 /* Interrupt mask */ +#define RIO_ISA_CFG_IRQ12 0x10 /* Interrupt Level 12 */ +#define RIO_ISA_CFG_IRQ11 0x20 /* Interrupt Level 11 */ +#define RIO_ISA_CFG_IRQ9 0x30 /* Interrupt Level 9 */ +#define RIO_ISA_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */ +#define RIO_ISA_CFG_WAITSTATE0 0x80 /* 0 waitstates, else 1 */ + +/***************************************************************************** +***************************** ***************************** +***************************** RIO (Rev2) ISA ***************************** +***************************** ***************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_ISA2_IDENT "JBJGPGGHINSMJPJR" + +#define RIO_ISA2_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_ISA2_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_ISA2_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */ +#define RIO_ISA2_CFG_16BIT 0x08 /* 16bit mode, else 8bit */ +#define RIO_ISA2_CFG_IRQMASK 0x30 /* Interrupt mask */ +#define RIO_ISA2_CFG_IRQ15 0x00 /* Interrupt Level 15 */ +#define RIO_ISA2_CFG_IRQ12 0x10 /* Interrupt Level 12 */ +#define RIO_ISA2_CFG_IRQ11 0x20 /* Interrupt Level 11 */ +#define RIO_ISA2_CFG_IRQ9 0x30 /* Interrupt Level 9 */ +#define RIO_ISA2_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */ +#define RIO_ISA2_CFG_WAITSTATE0 0x80 /* 0 waitstates, else 1 */ + +/***************************************************************************** +***************************** ****************************** +***************************** RIO (Jet) ISA ****************************** +***************************** ****************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_ISA3_IDENT "JET HOST BY KEV#" + +#define RIO_ISA3_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_ISA3_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */ +#define RIO_ISA32_CFG_IRQMASK 0xF30 /* Interrupt mask */ +#define RIO_ISA3_CFG_IRQ15 0xF0 /* Interrupt Level 15 */ +#define RIO_ISA3_CFG_IRQ12 0xC0 /* Interrupt Level 12 */ +#define RIO_ISA3_CFG_IRQ11 0xB0 /* Interrupt Level 11 */ +#define RIO_ISA3_CFG_IRQ10 0xA0 /* Interrupt Level 10 */ +#define RIO_ISA3_CFG_IRQ9 0x90 /* Interrupt Level 9 */ + +/***************************************************************************** +********************************* ******************************** +********************************* RIO MCA ******************************** +********************************* ******************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_MCA_IDENT "JBJGPGGHINSMJPJR" + +#define RIO_MCA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_MCA_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_MCA_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */ + +/***************************************************************************** +******************************** ******************************** +******************************** RIO EISA ******************************** +******************************** ******************************** +*****************************************************************************/ + +/* EISA Configuration Space Definitions... */ +#define EISA_PRODUCT_ID1 0xC80 +#define EISA_PRODUCT_ID2 0xC81 +#define EISA_PRODUCT_NUMBER 0xC82 +#define EISA_REVISION_NUMBER 0xC83 +#define EISA_CARD_ENABLE 0xC84 +#define EISA_VPD_UNIQUEID4 0xC88 /* READ: Unique Identifier #4 */ +#define EISA_VPD_UNIQUEID3 0xC8A /* READ: Unique Identifier #3 */ +#define EISA_VPD_UNIQUEID2 0xC90 /* READ: Unique Identifier #2 */ +#define EISA_VPD_UNIQUEID1 0xC92 /* READ: Unique Identifier #1 */ +#define EISA_VPD_MANU_YEAR 0xC98 /* READ: Year Of Manufacture (0 = 1970) */ +#define EISA_VPD_MANU_WEEK 0xC9A /* READ: Week Of Manufacture (0 = week 1 Jan) */ +#define EISA_MEM_ADDR_23_16 0xC00 +#define EISA_MEM_ADDR_31_24 0xC01 +#define EISA_RIO_CONFIG 0xC02 /* WRITE: Configuration Register */ +#define EISA_RIO_INTSET 0xC03 /* WRITE: Interrupt Set */ +#define EISA_RIO_INTRESET 0xC03 /* READ: Interrupt Reset */ + +/* Control Register Definitions... */ +#define RIO_EISA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_EISA_CFG_LINK20 0x02 /* 20Mbps link, else 10Mbps */ +#define RIO_EISA_CFG_BUSENABLE 0x04 /* Enable processor bus */ +#define RIO_EISA_CFG_PROCRUN 0x08 /* Processor running, else reset */ +#define RIO_EISA_CFG_IRQMASK 0xF0 /* Interrupt mask */ +#define RIO_EISA_CFG_IRQ15 0xF0 /* Interrupt Level 15 */ +#define RIO_EISA_CFG_IRQ14 0xE0 /* Interrupt Level 14 */ +#define RIO_EISA_CFG_IRQ12 0xC0 /* Interrupt Level 12 */ +#define RIO_EISA_CFG_IRQ11 0xB0 /* Interrupt Level 11 */ +#define RIO_EISA_CFG_IRQ10 0xA0 /* Interrupt Level 10 */ +#define RIO_EISA_CFG_IRQ9 0x90 /* Interrupt Level 9 */ +#define RIO_EISA_CFG_IRQ7 0x70 /* Interrupt Level 7 */ +#define RIO_EISA_CFG_IRQ6 0x60 /* Interrupt Level 6 */ +#define RIO_EISA_CFG_IRQ5 0x50 /* Interrupt Level 5 */ +#define RIO_EISA_CFG_IRQ4 0x40 /* Interrupt Level 4 */ +#define RIO_EISA_CFG_IRQ3 0x30 /* Interrupt Level 3 */ + +/***************************************************************************** +******************************** ******************************** +******************************** RIO SBus ******************************** +******************************** ******************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_SBUS_IDENT "JBPGK#\0\0\0\0\0\0\0\0\0\0" + +#define RIO_SBUS_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_SBUS_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_SBUS_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */ +#define RIO_SBUS_CFG_IRQMASK 0x38 /* Interrupt mask */ +#define RIO_SBUS_CFG_IRQNONE 0x00 /* No Interrupt */ +#define RIO_SBUS_CFG_IRQ7 0x38 /* Interrupt Level 7 */ +#define RIO_SBUS_CFG_IRQ6 0x30 /* Interrupt Level 6 */ +#define RIO_SBUS_CFG_IRQ5 0x28 /* Interrupt Level 5 */ +#define RIO_SBUS_CFG_IRQ4 0x20 /* Interrupt Level 4 */ +#define RIO_SBUS_CFG_IRQ3 0x18 /* Interrupt Level 3 */ +#define RIO_SBUS_CFG_IRQ2 0x10 /* Interrupt Level 2 */ +#define RIO_SBUS_CFG_IRQ1 0x08 /* Interrupt Level 1 */ +#define RIO_SBUS_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */ +#define RIO_SBUS_CFG_PROC25 0x80 /* 25Mhz processor clock, else 20Mhz */ + +/***************************************************************************** +********************************* ******************************** +********************************* RIO PCI ******************************** +********************************* ******************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_PCI_IDENT "ECDDPGJGJHJRGSK#" + +#define RIO_PCI_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */ +#define RIO_PCI_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_PCI_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */ +#define RIO_PCI_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */ +#define RIO_PCI_CFG_PROC25 0x80 /* 25Mhz processor clock, else 20Mhz */ + +/* PCI Definitions... */ +#define SPX_VENDOR_ID 0x11CB /* Assigned by the PCI SIG */ +#define SPX_DEVICE_ID 0x8000 /* RIO bridge boards */ +#define SPX_PLXDEVICE_ID 0x2000 /* PLX bridge boards */ +#define SPX_SUB_VENDOR_ID SPX_VENDOR_ID /* Same as vendor id */ +#define RIO_SUB_SYS_ID 0x0800 /* RIO PCI board */ + +/***************************************************************************** +***************************** ****************************** +***************************** RIO (Jet) PCI ****************************** +***************************** ****************************** +*****************************************************************************/ + +/* Control Register Definitions... */ +#define RIO_PCI2_IDENT "JET HOST BY KEV#" + +#define RIO_PCI2_CFG_BUSENABLE 0x02 /* Enable processor bus */ +#define RIO_PCI2_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */ + +/* PCI Definitions... */ +#define RIO2_SUB_SYS_ID 0x0100 /* RIO (Jet) PCI board */ + +#endif /*_rioboard_h */ + +/* End of RIOBOARD.H */ diff --git a/drivers/staging/generic_serial/rio/rioboot.c b/drivers/staging/generic_serial/rio/rioboot.c new file mode 100644 index 0000000..d956dd3 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioboot.c @@ -0,0 +1,1113 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioboot.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:36 +** Retrieved : 11/6/98 10:33:48 +** +** ident @(#)rioboot.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/termios.h> +#include <linux/serial.h> +#include <linux/vmalloc.h> +#include <linux/generic_serial.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" + +static int RIOBootComplete(struct rio_info *p, struct Host *HostP, unsigned int Rup, struct PktCmd __iomem *PktCmdP); + +static const unsigned char RIOAtVec2Ctrl[] = { + /* 0 */ INTERRUPT_DISABLE, + /* 1 */ INTERRUPT_DISABLE, + /* 2 */ INTERRUPT_DISABLE, + /* 3 */ INTERRUPT_DISABLE, + /* 4 */ INTERRUPT_DISABLE, + /* 5 */ INTERRUPT_DISABLE, + /* 6 */ INTERRUPT_DISABLE, + /* 7 */ INTERRUPT_DISABLE, + /* 8 */ INTERRUPT_DISABLE, + /* 9 */ IRQ_9 | INTERRUPT_ENABLE, + /* 10 */ INTERRUPT_DISABLE, + /* 11 */ IRQ_11 | INTERRUPT_ENABLE, + /* 12 */ IRQ_12 | INTERRUPT_ENABLE, + /* 13 */ INTERRUPT_DISABLE, + /* 14 */ INTERRUPT_DISABLE, + /* 15 */ IRQ_15 | INTERRUPT_ENABLE +}; + +/** + * RIOBootCodeRTA - Load RTA boot code + * @p: RIO to load + * @rbp: Download descriptor + * + * Called when the user process initiates booting of the card firmware. + * Lads the firmware + */ + +int RIOBootCodeRTA(struct rio_info *p, struct DownLoad * rbp) +{ + int offset; + + func_enter(); + + rio_dprintk(RIO_DEBUG_BOOT, "Data at user address %p\n", rbp->DataP); + + /* + ** Check that we have set asside enough memory for this + */ + if (rbp->Count > SIXTY_FOUR_K) { + rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot Code Too Large!\n"); + p->RIOError.Error = HOST_FILE_TOO_LARGE; + func_exit(); + return -ENOMEM; + } + + if (p->RIOBooting) { + rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot Code : BUSY BUSY BUSY!\n"); + p->RIOError.Error = BOOT_IN_PROGRESS; + func_exit(); + return -EBUSY; + } + + /* + ** The data we load in must end on a (RTA_BOOT_DATA_SIZE) byte boundary, + ** so calculate how far we have to move the data up the buffer + ** to achieve this. + */ + offset = (RTA_BOOT_DATA_SIZE - (rbp->Count % RTA_BOOT_DATA_SIZE)) % RTA_BOOT_DATA_SIZE; + + /* + ** Be clean, and clear the 'unused' portion of the boot buffer, + ** because it will (eventually) be part of the Rta run time environment + ** and so should be zeroed. + */ + memset(p->RIOBootPackets, 0, offset); + + /* + ** Copy the data from user space into the array + */ + + if (copy_from_user(((u8 *)p->RIOBootPackets) + offset, rbp->DataP, rbp->Count)) { + rio_dprintk(RIO_DEBUG_BOOT, "Bad data copy from user space\n"); + p->RIOError.Error = COPYIN_FAILED; + func_exit(); + return -EFAULT; + } + + /* + ** Make sure that our copy of the size includes that offset we discussed + ** earlier. + */ + p->RIONumBootPkts = (rbp->Count + offset) / RTA_BOOT_DATA_SIZE; + p->RIOBootCount = rbp->Count; + + func_exit(); + return 0; +} + +/** + * rio_start_card_running - host card start + * @HostP: The RIO to kick off + * + * Start a RIO processor unit running. Encapsulates the knowledge + * of the card type. + */ + +void rio_start_card_running(struct Host *HostP) +{ + switch (HostP->Type) { + case RIO_AT: + rio_dprintk(RIO_DEBUG_BOOT, "Start ISA card running\n"); + writeb(BOOT_FROM_RAM | EXTERNAL_BUS_ON | HostP->Mode | RIOAtVec2Ctrl[HostP->Ivec & 0xF], &HostP->Control); + break; + case RIO_PCI: + /* + ** PCI is much the same as MCA. Everything is once again memory + ** mapped, so we are writing to memory registers instead of io + ** ports. + */ + rio_dprintk(RIO_DEBUG_BOOT, "Start PCI card running\n"); + writeb(PCITpBootFromRam | PCITpBusEnable | HostP->Mode, &HostP->Control); + break; + default: + rio_dprintk(RIO_DEBUG_BOOT, "Unknown host type %d\n", HostP->Type); + break; + } + return; +} + +/* +** Load in the host boot code - load it directly onto all halted hosts +** of the correct type. +** +** Put your rubber pants on before messing with this code - even the magic +** numbers have trouble understanding what they are doing here. +*/ + +int RIOBootCodeHOST(struct rio_info *p, struct DownLoad *rbp) +{ + struct Host *HostP; + u8 __iomem *Cad; + PARM_MAP __iomem *ParmMapP; + int RupN; + int PortN; + unsigned int host; + u8 __iomem *StartP; + u8 __iomem *DestP; + int wait_count; + u16 OldParmMap; + u16 offset; /* It is very important that this is a u16 */ + u8 *DownCode = NULL; + unsigned long flags; + + HostP = NULL; /* Assure the compiler we've initialized it */ + + + /* Walk the hosts */ + for (host = 0; host < p->RIONumHosts; host++) { + rio_dprintk(RIO_DEBUG_BOOT, "Attempt to boot host %d\n", host); + HostP = &p->RIOHosts[host]; + + rio_dprintk(RIO_DEBUG_BOOT, "Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec); + + /* Don't boot hosts already running */ + if ((HostP->Flags & RUN_STATE) != RC_WAITING) { + rio_dprintk(RIO_DEBUG_BOOT, "%s %d already running\n", "Host", host); + continue; + } + + /* + ** Grab a pointer to the card (ioremapped) + */ + Cad = HostP->Caddr; + + /* + ** We are going to (try) and load in rbp->Count bytes. + ** The last byte will reside at p->RIOConf.HostLoadBase-1; + ** Therefore, we need to start copying at address + ** (caddr+p->RIOConf.HostLoadBase-rbp->Count) + */ + StartP = &Cad[p->RIOConf.HostLoadBase - rbp->Count]; + + rio_dprintk(RIO_DEBUG_BOOT, "kernel virtual address for host is %p\n", Cad); + rio_dprintk(RIO_DEBUG_BOOT, "kernel virtual address for download is %p\n", StartP); + rio_dprintk(RIO_DEBUG_BOOT, "host loadbase is 0x%x\n", p->RIOConf.HostLoadBase); + rio_dprintk(RIO_DEBUG_BOOT, "size of download is 0x%x\n", rbp->Count); + + /* Make sure it fits */ + if (p->RIOConf.HostLoadBase < rbp->Count) { + rio_dprintk(RIO_DEBUG_BOOT, "Bin too large\n"); + p->RIOError.Error = HOST_FILE_TOO_LARGE; + func_exit(); + return -EFBIG; + } + /* + ** Ensure that the host really is stopped. + ** Disable it's external bus & twang its reset line. + */ + RIOHostReset(HostP->Type, HostP->CardP, HostP->Slot); + + /* + ** Copy the data directly from user space to the SRAM. + ** This ain't going to be none too clever if the download + ** code is bigger than this segment. + */ + rio_dprintk(RIO_DEBUG_BOOT, "Copy in code\n"); + + /* Buffer to local memory as we want to use I/O space and + some cards only do 8 or 16 bit I/O */ + + DownCode = vmalloc(rbp->Count); + if (!DownCode) { + p->RIOError.Error = NOT_ENOUGH_CORE_FOR_PCI_COPY; + func_exit(); + return -ENOMEM; + } + if (copy_from_user(DownCode, rbp->DataP, rbp->Count)) { + kfree(DownCode); + p->RIOError.Error = COPYIN_FAILED; + func_exit(); + return -EFAULT; + } + HostP->Copy(DownCode, StartP, rbp->Count); + vfree(DownCode); + + rio_dprintk(RIO_DEBUG_BOOT, "Copy completed\n"); + + /* + ** S T O P ! + ** + ** Upto this point the code has been fairly rational, and possibly + ** even straight forward. What follows is a pile of crud that will + ** magically turn into six bytes of transputer assembler. Normally + ** you would expect an array or something, but, being me, I have + ** chosen [been told] to use a technique whereby the startup code + ** will be correct if we change the loadbase for the code. Which + ** brings us onto another issue - the loadbase is the *end* of the + ** code, not the start. + ** + ** If I were you I wouldn't start from here. + */ + + /* + ** We now need to insert a short boot section into + ** the memory at the end of Sram2. This is normally (de)composed + ** of the last eight bytes of the download code. The + ** download has been assembled/compiled to expect to be + ** loaded from 0x7FFF downwards. We have loaded it + ** at some other address. The startup code goes into the small + ** ram window at Sram2, in the last 8 bytes, which are really + ** at addresses 0x7FF8-0x7FFF. + ** + ** If the loadbase is, say, 0x7C00, then we need to branch to + ** address 0x7BFE to run the host.bin startup code. We assemble + ** this jump manually. + ** + ** The two byte sequence 60 08 is loaded into memory at address + ** 0x7FFE,F. This is a local branch to location 0x7FF8 (60 is nfix 0, + ** which adds '0' to the .O register, complements .O, and then shifts + ** it left by 4 bit positions, 08 is a jump .O+8 instruction. This will + ** add 8 to .O (which was 0xFFF0), and will branch RELATIVE to the new + ** location. Now, the branch starts from the value of .PC (or .IP or + ** whatever the bloody register is called on this chip), and the .PC + ** will be pointing to the location AFTER the branch, in this case + ** .PC == 0x8000, so the branch will be to 0x8000+0xFFF8 = 0x7FF8. + ** + ** A long branch is coded at 0x7FF8. This consists of loading a four + ** byte offset into .O using nfix (as above) and pfix operators. The + ** pfix operates in exactly the same way as the nfix operator, but + ** without the complement operation. The offset, of course, must be + ** relative to the address of the byte AFTER the branch instruction, + ** which will be (urm) 0x7FFC, so, our final destination of the branch + ** (loadbase-2), has to be reached from here. Imagine that the loadbase + ** is 0x7C00 (which it is), then we will need to branch to 0x7BFE (which + ** is the first byte of the initial two byte short local branch of the + ** download code). + ** + ** To code a jump from 0x7FFC (which is where the branch will start + ** from) to 0x7BFE, we will need to branch 0xFC02 bytes (0x7FFC+0xFC02)= + ** 0x7BFE. + ** This will be coded as four bytes: + ** 60 2C 20 02 + ** being nfix .O+0 + ** pfix .O+C + ** pfix .O+0 + ** jump .O+2 + ** + ** The nfix operator is used, so that the startup code will be + ** compatible with the whole Tp family. (lies, damn lies, it'll never + ** work in a month of Sundays). + ** + ** The nfix nyble is the 1s complement of the nyble value you + ** want to load - in this case we wanted 'F' so we nfix loaded '0'. + */ + + + /* + ** Dest points to the top 8 bytes of Sram2. The Tp jumps + ** to 0x7FFE at reset time, and starts executing. This is + ** a short branch to 0x7FF8, where a long branch is coded. + */ + + DestP = &Cad[0x7FF8]; /* <<<---- READ THE ABOVE COMMENTS */ + +#define NFIX(N) (0x60 | (N)) /* .O = (~(.O + N))<<4 */ +#define PFIX(N) (0x20 | (N)) /* .O = (.O + N)<<4 */ +#define JUMP(N) (0x00 | (N)) /* .PC = .PC + .O */ + + /* + ** 0x7FFC is the address of the location following the last byte of + ** the four byte jump instruction. + ** READ THE ABOVE COMMENTS + ** + ** offset is (TO-FROM) % MEMSIZE, but with compound buggering about. + ** Memsize is 64K for this range of Tp, so offset is a short (unsigned, + ** cos I don't understand 2's complement). + */ + offset = (p->RIOConf.HostLoadBase - 2) - 0x7FFC; + + writeb(NFIX(((unsigned short) (~offset) >> (unsigned short) 12) & 0xF), DestP); + writeb(PFIX((offset >> 8) & 0xF), DestP + 1); + writeb(PFIX((offset >> 4) & 0xF), DestP + 2); + writeb(JUMP(offset & 0xF), DestP + 3); + + writeb(NFIX(0), DestP + 6); + writeb(JUMP(8), DestP + 7); + + rio_dprintk(RIO_DEBUG_BOOT, "host loadbase is 0x%x\n", p->RIOConf.HostLoadBase); + rio_dprintk(RIO_DEBUG_BOOT, "startup offset is 0x%x\n", offset); + + /* + ** Flag what is going on + */ + HostP->Flags &= ~RUN_STATE; + HostP->Flags |= RC_STARTUP; + + /* + ** Grab a copy of the current ParmMap pointer, so we + ** can tell when it has changed. + */ + OldParmMap = readw(&HostP->__ParmMapR); + + rio_dprintk(RIO_DEBUG_BOOT, "Original parmmap is 0x%x\n", OldParmMap); + + /* + ** And start it running (I hope). + ** As there is nothing dodgy or obscure about the + ** above code, this is guaranteed to work every time. + */ + rio_dprintk(RIO_DEBUG_BOOT, "Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec); + + rio_start_card_running(HostP); + + rio_dprintk(RIO_DEBUG_BOOT, "Set control port\n"); + + /* + ** Now, wait for upto five seconds for the Tp to setup the parmmap + ** pointer: + */ + for (wait_count = 0; (wait_count < p->RIOConf.StartupTime) && (readw(&HostP->__ParmMapR) == OldParmMap); wait_count++) { + rio_dprintk(RIO_DEBUG_BOOT, "Checkout %d, 0x%x\n", wait_count, readw(&HostP->__ParmMapR)); + mdelay(100); + + } + + /* + ** If the parmmap pointer is unchanged, then the host code + ** has crashed & burned in a really spectacular way + */ + if (readw(&HostP->__ParmMapR) == OldParmMap) { + rio_dprintk(RIO_DEBUG_BOOT, "parmmap 0x%x\n", readw(&HostP->__ParmMapR)); + rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail\n"); + HostP->Flags &= ~RUN_STATE; + HostP->Flags |= RC_STUFFED; + RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot ); + continue; + } + + rio_dprintk(RIO_DEBUG_BOOT, "Running 0x%x\n", readw(&HostP->__ParmMapR)); + + /* + ** Well, the board thought it was OK, and setup its parmmap + ** pointer. For the time being, we will pretend that this + ** board is running, and check out what the error flag says. + */ + + /* + ** Grab a 32 bit pointer to the parmmap structure + */ + ParmMapP = (PARM_MAP __iomem *) RIO_PTR(Cad, readw(&HostP->__ParmMapR)); + rio_dprintk(RIO_DEBUG_BOOT, "ParmMapP : %p\n", ParmMapP); + ParmMapP = (PARM_MAP __iomem *)(Cad + readw(&HostP->__ParmMapR)); + rio_dprintk(RIO_DEBUG_BOOT, "ParmMapP : %p\n", ParmMapP); + + /* + ** The links entry should be 0xFFFF; we set it up + ** with a mask to say how many PHBs to use, and + ** which links to use. + */ + if (readw(&ParmMapP->links) != 0xFFFF) { + rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail %s\n", HostP->Name); + rio_dprintk(RIO_DEBUG_BOOT, "Links = 0x%x\n", readw(&ParmMapP->links)); + HostP->Flags &= ~RUN_STATE; + HostP->Flags |= RC_STUFFED; + RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot ); + continue; + } + + writew(RIO_LINK_ENABLE, &ParmMapP->links); + + /* + ** now wait for the card to set all the parmmap->XXX stuff + ** this is a wait of upto two seconds.... + */ + rio_dprintk(RIO_DEBUG_BOOT, "Looking for init_done - %d ticks\n", p->RIOConf.StartupTime); + HostP->timeout_id = 0; + for (wait_count = 0; (wait_count < p->RIOConf.StartupTime) && !readw(&ParmMapP->init_done); wait_count++) { + rio_dprintk(RIO_DEBUG_BOOT, "Waiting for init_done\n"); + mdelay(100); + } + rio_dprintk(RIO_DEBUG_BOOT, "OK! init_done!\n"); + + if (readw(&ParmMapP->error) != E_NO_ERROR || !readw(&ParmMapP->init_done)) { + rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail %s\n", HostP->Name); + rio_dprintk(RIO_DEBUG_BOOT, "Timedout waiting for init_done\n"); + HostP->Flags &= ~RUN_STATE; + HostP->Flags |= RC_STUFFED; + RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot ); + continue; + } + + rio_dprintk(RIO_DEBUG_BOOT, "Got init_done\n"); + + /* + ** It runs! It runs! + */ + rio_dprintk(RIO_DEBUG_BOOT, "Host ID %x Running\n", HostP->UniqueNum); + + /* + ** set the time period between interrupts. + */ + writew(p->RIOConf.Timer, &ParmMapP->timer); + + /* + ** Translate all the 16 bit pointers in the __ParmMapR into + ** 32 bit pointers for the driver in ioremap space. + */ + HostP->ParmMapP = ParmMapP; + HostP->PhbP = (struct PHB __iomem *) RIO_PTR(Cad, readw(&ParmMapP->phb_ptr)); + HostP->RupP = (struct RUP __iomem *) RIO_PTR(Cad, readw(&ParmMapP->rups)); + HostP->PhbNumP = (unsigned short __iomem *) RIO_PTR(Cad, readw(&ParmMapP->phb_num_ptr)); + HostP->LinkStrP = (struct LPB __iomem *) RIO_PTR(Cad, readw(&ParmMapP->link_str_ptr)); + + /* + ** point the UnixRups at the real Rups + */ + for (RupN = 0; RupN < MAX_RUP; RupN++) { + HostP->UnixRups[RupN].RupP = &HostP->RupP[RupN]; + HostP->UnixRups[RupN].Id = RupN + 1; + HostP->UnixRups[RupN].BaseSysPort = NO_PORT; + spin_lock_init(&HostP->UnixRups[RupN].RupLock); + } + + for (RupN = 0; RupN < LINKS_PER_UNIT; RupN++) { + HostP->UnixRups[RupN + MAX_RUP].RupP = &HostP->LinkStrP[RupN].rup; + HostP->UnixRups[RupN + MAX_RUP].Id = 0; + HostP->UnixRups[RupN + MAX_RUP].BaseSysPort = NO_PORT; + spin_lock_init(&HostP->UnixRups[RupN + MAX_RUP].RupLock); + } + + /* + ** point the PortP->Phbs at the real Phbs + */ + for (PortN = p->RIOFirstPortsMapped; PortN < p->RIOLastPortsMapped + PORTS_PER_RTA; PortN++) { + if (p->RIOPortp[PortN]->HostP == HostP) { + struct Port *PortP = p->RIOPortp[PortN]; + struct PHB __iomem *PhbP; + /* int oldspl; */ + + if (!PortP->Mapped) + continue; + + PhbP = &HostP->PhbP[PortP->HostPort]; + rio_spin_lock_irqsave(&PortP->portSem, flags); + + PortP->PhbP = PhbP; + + PortP->TxAdd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_add)); + PortP->TxStart = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_start)); + PortP->TxEnd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_end)); + PortP->RxRemove = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_remove)); + PortP->RxStart = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_start)); + PortP->RxEnd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_end)); + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + /* + ** point the UnixRup at the base SysPort + */ + if (!(PortN % PORTS_PER_RTA)) + HostP->UnixRups[PortP->RupNum].BaseSysPort = PortN; + } + } + + rio_dprintk(RIO_DEBUG_BOOT, "Set the card running... \n"); + /* + ** last thing - show the world that everything is in place + */ + HostP->Flags &= ~RUN_STATE; + HostP->Flags |= RC_RUNNING; + } + /* + ** MPX always uses a poller. This is actually patched into the system + ** configuration and called directly from each clock tick. + ** + */ + p->RIOPolling = 1; + + p->RIOSystemUp++; + + rio_dprintk(RIO_DEBUG_BOOT, "Done everything %x\n", HostP->Ivec); + func_exit(); + return 0; +} + + + +/** + * RIOBootRup - Boot an RTA + * @p: rio we are working with + * @Rup: Rup number + * @HostP: host object + * @PacketP: packet to use + * + * If we have successfully processed this boot, then + * return 1. If we havent, then return 0. + */ + +int RIOBootRup(struct rio_info *p, unsigned int Rup, struct Host *HostP, struct PKT __iomem *PacketP) +{ + struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *) PacketP->data; + struct PktCmd_M *PktReplyP; + struct CmdBlk *CmdBlkP; + unsigned int sequence; + + /* + ** If we haven't been told what to boot, we can't boot it. + */ + if (p->RIONumBootPkts == 0) { + rio_dprintk(RIO_DEBUG_BOOT, "No RTA code to download yet\n"); + return 0; + } + + /* + ** Special case of boot completed - if we get one of these then we + ** don't need a command block. For all other cases we do, so handle + ** this first and then get a command block, then handle every other + ** case, relinquishing the command block if disaster strikes! + */ + if ((readb(&PacketP->len) & PKT_CMD_BIT) && (readb(&PktCmdP->Command) == BOOT_COMPLETED)) + return RIOBootComplete(p, HostP, Rup, PktCmdP); + + /* + ** Try to allocate a command block. This is in kernel space + */ + if (!(CmdBlkP = RIOGetCmdBlk())) { + rio_dprintk(RIO_DEBUG_BOOT, "No command blocks to boot RTA! come back later.\n"); + return 0; + } + + /* + ** Fill in the default info on the command block + */ + CmdBlkP->Packet.dest_unit = Rup < (unsigned short) MAX_RUP ? Rup : 0; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.src_port = BOOT_RUP; + + CmdBlkP->PreFuncP = CmdBlkP->PostFuncP = NULL; + PktReplyP = (struct PktCmd_M *) CmdBlkP->Packet.data; + + /* + ** process COMMANDS on the boot rup! + */ + if (readb(&PacketP->len) & PKT_CMD_BIT) { + /* + ** We only expect one type of command - a BOOT_REQUEST! + */ + if (readb(&PktCmdP->Command) != BOOT_REQUEST) { + rio_dprintk(RIO_DEBUG_BOOT, "Unexpected command %d on BOOT RUP %d of host %Zd\n", readb(&PktCmdP->Command), Rup, HostP - p->RIOHosts); + RIOFreeCmdBlk(CmdBlkP); + return 1; + } + + /* + ** Build a Boot Sequence command block + ** + ** We no longer need to use "Boot Mode", we'll always allow + ** boot requests - the boot will not complete if the device + ** appears in the bindings table. + ** + ** We'll just (always) set the command field in packet reply + ** to allow an attempted boot sequence : + */ + PktReplyP->Command = BOOT_SEQUENCE; + + PktReplyP->BootSequence.NumPackets = p->RIONumBootPkts; + PktReplyP->BootSequence.LoadBase = p->RIOConf.RtaLoadBase; + PktReplyP->BootSequence.CodeSize = p->RIOBootCount; + + CmdBlkP->Packet.len = BOOT_SEQUENCE_LEN | PKT_CMD_BIT; + + memcpy((void *) &CmdBlkP->Packet.data[BOOT_SEQUENCE_LEN], "BOOT", 4); + + rio_dprintk(RIO_DEBUG_BOOT, "Boot RTA on Host %Zd Rup %d - %d (0x%x) packets to 0x%x\n", HostP - p->RIOHosts, Rup, p->RIONumBootPkts, p->RIONumBootPkts, p->RIOConf.RtaLoadBase); + + /* + ** If this host is in slave mode, send the RTA an invalid boot + ** sequence command block to force it to kill the boot. We wait + ** for half a second before sending this packet to prevent the RTA + ** attempting to boot too often. The master host should then grab + ** the RTA and make it its own. + */ + p->RIOBooting++; + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + return 1; + } + + /* + ** It is a request for boot data. + */ + sequence = readw(&PktCmdP->Sequence); + + rio_dprintk(RIO_DEBUG_BOOT, "Boot block %d on Host %Zd Rup%d\n", sequence, HostP - p->RIOHosts, Rup); + + if (sequence >= p->RIONumBootPkts) { + rio_dprintk(RIO_DEBUG_BOOT, "Got a request for packet %d, max is %d\n", sequence, p->RIONumBootPkts); + } + + PktReplyP->Sequence = sequence; + memcpy(PktReplyP->BootData, p->RIOBootPackets[p->RIONumBootPkts - sequence - 1], RTA_BOOT_DATA_SIZE); + CmdBlkP->Packet.len = PKT_MAX_DATA_LEN; + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + return 1; +} + +/** + * RIOBootComplete - RTA boot is done + * @p: RIO we are working with + * @HostP: Host structure + * @Rup: RUP being used + * @PktCmdP: Packet command that was used + * + * This function is called when an RTA been booted. + * If booted by a host, HostP->HostUniqueNum is the booting host. + * If booted by an RTA, HostP->Mapping[Rup].RtaUniqueNum is the booting RTA. + * RtaUniq is the booted RTA. + */ + +static int RIOBootComplete(struct rio_info *p, struct Host *HostP, unsigned int Rup, struct PktCmd __iomem *PktCmdP) +{ + struct Map *MapP = NULL; + struct Map *MapP2 = NULL; + int Flag; + int found; + int host, rta; + int EmptySlot = -1; + int entry, entry2; + char *MyType, *MyName; + unsigned int MyLink; + unsigned short RtaType; + u32 RtaUniq = (readb(&PktCmdP->UniqNum[0])) + (readb(&PktCmdP->UniqNum[1]) << 8) + (readb(&PktCmdP->UniqNum[2]) << 16) + (readb(&PktCmdP->UniqNum[3]) << 24); + + p->RIOBooting = 0; + + rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot completed - BootInProgress now %d\n", p->RIOBooting); + + /* + ** Determine type of unit (16/8 port RTA). + */ + + RtaType = GetUnitType(RtaUniq); + if (Rup >= (unsigned short) MAX_RUP) + rio_dprintk(RIO_DEBUG_BOOT, "RIO: Host %s has booted an RTA(%d) on link %c\n", HostP->Name, 8 * RtaType, readb(&PktCmdP->LinkNum) + 'A'); + else + rio_dprintk(RIO_DEBUG_BOOT, "RIO: RTA %s has booted an RTA(%d) on link %c\n", HostP->Mapping[Rup].Name, 8 * RtaType, readb(&PktCmdP->LinkNum) + 'A'); + + rio_dprintk(RIO_DEBUG_BOOT, "UniqNum is 0x%x\n", RtaUniq); + + if (RtaUniq == 0x00000000 || RtaUniq == 0xffffffff) { + rio_dprintk(RIO_DEBUG_BOOT, "Illegal RTA Uniq Number\n"); + return 1; + } + + /* + ** If this RTA has just booted an RTA which doesn't belong to this + ** system, or the system is in slave mode, do not attempt to create + ** a new table entry for it. + */ + + if (!RIOBootOk(p, HostP, RtaUniq)) { + MyLink = readb(&PktCmdP->LinkNum); + if (Rup < (unsigned short) MAX_RUP) { + /* + ** RtaUniq was clone booted (by this RTA). Instruct this RTA + ** to hold off further attempts to boot on this link for 30 + ** seconds. + */ + if (RIOSuspendBootRta(HostP, HostP->Mapping[Rup].ID, MyLink)) { + rio_dprintk(RIO_DEBUG_BOOT, "RTA failed to suspend booting on link %c\n", 'A' + MyLink); + } + } else + /* + ** RtaUniq was booted by this host. Set the booting link + ** to hold off for 30 seconds to give another unit a + ** chance to boot it. + */ + writew(30, &HostP->LinkStrP[MyLink].WaitNoBoot); + rio_dprintk(RIO_DEBUG_BOOT, "RTA %x not owned - suspend booting down link %c on unit %x\n", RtaUniq, 'A' + MyLink, HostP->Mapping[Rup].RtaUniqueNum); + return 1; + } + + /* + ** Check for a SLOT_IN_USE entry for this RTA attached to the + ** current host card in the driver table. + ** + ** If it exists, make a note that we have booted it. Other parts of + ** the driver are interested in this information at a later date, + ** in particular when the booting RTA asks for an ID for this unit, + ** we must have set the BOOTED flag, and the NEWBOOT flag is used + ** to force an open on any ports that where previously open on this + ** unit. + */ + for (entry = 0; entry < MAX_RUP; entry++) { + unsigned int sysport; + + if ((HostP->Mapping[entry].Flags & SLOT_IN_USE) && (HostP->Mapping[entry].RtaUniqueNum == RtaUniq)) { + HostP->Mapping[entry].Flags |= RTA_BOOTED | RTA_NEWBOOT; + if ((sysport = HostP->Mapping[entry].SysPort) != NO_PORT) { + if (sysport < p->RIOFirstPortsBooted) + p->RIOFirstPortsBooted = sysport; + if (sysport > p->RIOLastPortsBooted) + p->RIOLastPortsBooted = sysport; + /* + ** For a 16 port RTA, check the second bank of 8 ports + */ + if (RtaType == TYPE_RTA16) { + entry2 = HostP->Mapping[entry].ID2 - 1; + HostP->Mapping[entry2].Flags |= RTA_BOOTED | RTA_NEWBOOT; + sysport = HostP->Mapping[entry2].SysPort; + if (sysport < p->RIOFirstPortsBooted) + p->RIOFirstPortsBooted = sysport; + if (sysport > p->RIOLastPortsBooted) + p->RIOLastPortsBooted = sysport; + } + } + if (RtaType == TYPE_RTA16) + rio_dprintk(RIO_DEBUG_BOOT, "RTA will be given IDs %d+%d\n", entry + 1, entry2 + 1); + else + rio_dprintk(RIO_DEBUG_BOOT, "RTA will be given ID %d\n", entry + 1); + return 1; + } + } + + rio_dprintk(RIO_DEBUG_BOOT, "RTA not configured for this host\n"); + + if (Rup >= (unsigned short) MAX_RUP) { + /* + ** It was a host that did the booting + */ + MyType = "Host"; + MyName = HostP->Name; + } else { + /* + ** It was an RTA that did the booting + */ + MyType = "RTA"; + MyName = HostP->Mapping[Rup].Name; + } + MyLink = readb(&PktCmdP->LinkNum); + + /* + ** There is no SLOT_IN_USE entry for this RTA attached to the current + ** host card in the driver table. + ** + ** Check for a SLOT_TENTATIVE entry for this RTA attached to the + ** current host card in the driver table. + ** + ** If we find one, then we re-use that slot. + */ + for (entry = 0; entry < MAX_RUP; entry++) { + if ((HostP->Mapping[entry].Flags & SLOT_TENTATIVE) && (HostP->Mapping[entry].RtaUniqueNum == RtaUniq)) { + if (RtaType == TYPE_RTA16) { + entry2 = HostP->Mapping[entry].ID2 - 1; + if ((HostP->Mapping[entry2].Flags & SLOT_TENTATIVE) && (HostP->Mapping[entry2].RtaUniqueNum == RtaUniq)) + rio_dprintk(RIO_DEBUG_BOOT, "Found previous tentative slots (%d+%d)\n", entry, entry2); + else + continue; + } else + rio_dprintk(RIO_DEBUG_BOOT, "Found previous tentative slot (%d)\n", entry); + if (!p->RIONoMessage) + printk("RTA connected to %s '%s' (%c) not configured.\n", MyType, MyName, MyLink + 'A'); + return 1; + } + } + + /* + ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA + ** attached to the current host card in the driver table. + ** + ** Check if there is a SLOT_IN_USE or SLOT_TENTATIVE entry on another + ** host for this RTA in the driver table. + ** + ** For a SLOT_IN_USE entry on another host, we need to delete the RTA + ** entry from the other host and add it to this host (using some of + ** the functions from table.c which do this). + ** For a SLOT_TENTATIVE entry on another host, we must cope with the + ** following scenario: + ** + ** + Plug 8 port RTA into host A. (This creates SLOT_TENTATIVE entry + ** in table) + ** + Unplug RTA and plug into host B. (We now have 2 SLOT_TENTATIVE + ** entries) + ** + Configure RTA on host B. (This slot now becomes SLOT_IN_USE) + ** + Unplug RTA and plug back into host A. + ** + Configure RTA on host A. We now have the same RTA configured + ** with different ports on two different hosts. + */ + rio_dprintk(RIO_DEBUG_BOOT, "Have we seen RTA %x before?\n", RtaUniq); + found = 0; + Flag = 0; /* Convince the compiler this variable is initialized */ + for (host = 0; !found && (host < p->RIONumHosts); host++) { + for (rta = 0; rta < MAX_RUP; rta++) { + if ((p->RIOHosts[host].Mapping[rta].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) && (p->RIOHosts[host].Mapping[rta].RtaUniqueNum == RtaUniq)) { + Flag = p->RIOHosts[host].Mapping[rta].Flags; + MapP = &p->RIOHosts[host].Mapping[rta]; + if (RtaType == TYPE_RTA16) { + MapP2 = &p->RIOHosts[host].Mapping[MapP->ID2 - 1]; + rio_dprintk(RIO_DEBUG_BOOT, "This RTA is units %d+%d from host %s\n", rta + 1, MapP->ID2, p->RIOHosts[host].Name); + } else + rio_dprintk(RIO_DEBUG_BOOT, "This RTA is unit %d from host %s\n", rta + 1, p->RIOHosts[host].Name); + found = 1; + break; + } + } + } + + /* + ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA + ** attached to the current host card in the driver table. + ** + ** If we have not found a SLOT_IN_USE or SLOT_TENTATIVE entry on + ** another host for this RTA in the driver table... + ** + ** Check for a SLOT_IN_USE entry for this RTA in the config table. + */ + if (!MapP) { + rio_dprintk(RIO_DEBUG_BOOT, "Look for RTA %x in RIOSavedTable\n", RtaUniq); + for (rta = 0; rta < TOTAL_MAP_ENTRIES; rta++) { + rio_dprintk(RIO_DEBUG_BOOT, "Check table entry %d (%x)", rta, p->RIOSavedTable[rta].RtaUniqueNum); + + if ((p->RIOSavedTable[rta].Flags & SLOT_IN_USE) && (p->RIOSavedTable[rta].RtaUniqueNum == RtaUniq)) { + MapP = &p->RIOSavedTable[rta]; + Flag = p->RIOSavedTable[rta].Flags; + if (RtaType == TYPE_RTA16) { + for (entry2 = rta + 1; entry2 < TOTAL_MAP_ENTRIES; entry2++) { + if (p->RIOSavedTable[entry2].RtaUniqueNum == RtaUniq) + break; + } + MapP2 = &p->RIOSavedTable[entry2]; + rio_dprintk(RIO_DEBUG_BOOT, "This RTA is from table entries %d+%d\n", rta, entry2); + } else + rio_dprintk(RIO_DEBUG_BOOT, "This RTA is from table entry %d\n", rta); + break; + } + } + } + + /* + ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA + ** attached to the current host card in the driver table. + ** + ** We may have found a SLOT_IN_USE entry on another host for this + ** RTA in the config table, or a SLOT_IN_USE or SLOT_TENTATIVE entry + ** on another host for this RTA in the driver table. + ** + ** Check the driver table for room to fit this newly discovered RTA. + ** RIOFindFreeID() first looks for free slots and if it does not + ** find any free slots it will then attempt to oust any + ** tentative entry in the table. + */ + EmptySlot = 1; + if (RtaType == TYPE_RTA16) { + if (RIOFindFreeID(p, HostP, &entry, &entry2) == 0) { + RIODefaultName(p, HostP, entry); + rio_fill_host_slot(entry, entry2, RtaUniq, HostP); + EmptySlot = 0; + } + } else { + if (RIOFindFreeID(p, HostP, &entry, NULL) == 0) { + RIODefaultName(p, HostP, entry); + rio_fill_host_slot(entry, 0, RtaUniq, HostP); + EmptySlot = 0; + } + } + + /* + ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA + ** attached to the current host card in the driver table. + ** + ** If we found a SLOT_IN_USE entry on another host for this + ** RTA in the config or driver table, and there are enough free + ** slots in the driver table, then we need to move it over and + ** delete it from the other host. + ** If we found a SLOT_TENTATIVE entry on another host for this + ** RTA in the driver table, just delete the other host entry. + */ + if (EmptySlot == 0) { + if (MapP) { + if (Flag & SLOT_IN_USE) { + rio_dprintk(RIO_DEBUG_BOOT, "This RTA configured on another host - move entry to current host (1)\n"); + HostP->Mapping[entry].SysPort = MapP->SysPort; + memcpy(HostP->Mapping[entry].Name, MapP->Name, MAX_NAME_LEN); + HostP->Mapping[entry].Flags = SLOT_IN_USE | RTA_BOOTED | RTA_NEWBOOT; + RIOReMapPorts(p, HostP, &HostP->Mapping[entry]); + if (HostP->Mapping[entry].SysPort < p->RIOFirstPortsBooted) + p->RIOFirstPortsBooted = HostP->Mapping[entry].SysPort; + if (HostP->Mapping[entry].SysPort > p->RIOLastPortsBooted) + p->RIOLastPortsBooted = HostP->Mapping[entry].SysPort; + rio_dprintk(RIO_DEBUG_BOOT, "SysPort %d, Name %s\n", (int) MapP->SysPort, MapP->Name); + } else { + rio_dprintk(RIO_DEBUG_BOOT, "This RTA has a tentative entry on another host - delete that entry (1)\n"); + HostP->Mapping[entry].Flags = SLOT_TENTATIVE | RTA_BOOTED | RTA_NEWBOOT; + } + if (RtaType == TYPE_RTA16) { + if (Flag & SLOT_IN_USE) { + HostP->Mapping[entry2].Flags = SLOT_IN_USE | RTA_BOOTED | RTA_NEWBOOT | RTA16_SECOND_SLOT; + HostP->Mapping[entry2].SysPort = MapP2->SysPort; + /* + ** Map second block of ttys for 16 port RTA + */ + RIOReMapPorts(p, HostP, &HostP->Mapping[entry2]); + if (HostP->Mapping[entry2].SysPort < p->RIOFirstPortsBooted) + p->RIOFirstPortsBooted = HostP->Mapping[entry2].SysPort; + if (HostP->Mapping[entry2].SysPort > p->RIOLastPortsBooted) + p->RIOLastPortsBooted = HostP->Mapping[entry2].SysPort; + rio_dprintk(RIO_DEBUG_BOOT, "SysPort %d, Name %s\n", (int) HostP->Mapping[entry2].SysPort, HostP->Mapping[entry].Name); + } else + HostP->Mapping[entry2].Flags = SLOT_TENTATIVE | RTA_BOOTED | RTA_NEWBOOT | RTA16_SECOND_SLOT; + memset(MapP2, 0, sizeof(struct Map)); + } + memset(MapP, 0, sizeof(struct Map)); + if (!p->RIONoMessage) + printk("An orphaned RTA has been adopted by %s '%s' (%c).\n", MyType, MyName, MyLink + 'A'); + } else if (!p->RIONoMessage) + printk("RTA connected to %s '%s' (%c) not configured.\n", MyType, MyName, MyLink + 'A'); + RIOSetChange(p); + return 1; + } + + /* + ** There is no room in the driver table to make an entry for the + ** booted RTA. Keep a note of its Uniq Num in the overflow table, + ** so we can ignore it's ID requests. + */ + if (!p->RIONoMessage) + printk("The RTA connected to %s '%s' (%c) cannot be configured. You cannot configure more than 128 ports to one host card.\n", MyType, MyName, MyLink + 'A'); + for (entry = 0; entry < HostP->NumExtraBooted; entry++) { + if (HostP->ExtraUnits[entry] == RtaUniq) { + /* + ** already got it! + */ + return 1; + } + } + /* + ** If there is room, add the unit to the list of extras + */ + if (HostP->NumExtraBooted < MAX_EXTRA_UNITS) + HostP->ExtraUnits[HostP->NumExtraBooted++] = RtaUniq; + return 1; +} + + +/* +** If the RTA or its host appears in the RIOBindTab[] structure then +** we mustn't boot the RTA and should return 0. +** This operation is slightly different from the other drivers for RIO +** in that this is designed to work with the new utilities +** not config.rio and is FAR SIMPLER. +** We no longer support the RIOBootMode variable. It is all done from the +** "boot/noboot" field in the rio.cf file. +*/ +int RIOBootOk(struct rio_info *p, struct Host *HostP, unsigned long RtaUniq) +{ + int Entry; + unsigned int HostUniq = HostP->UniqueNum; + + /* + ** Search bindings table for RTA or its parent. + ** If it exists, return 0, else 1. + */ + for (Entry = 0; (Entry < MAX_RTA_BINDINGS) && (p->RIOBindTab[Entry] != 0); Entry++) { + if ((p->RIOBindTab[Entry] == HostUniq) || (p->RIOBindTab[Entry] == RtaUniq)) + return 0; + } + return 1; +} + +/* +** Make an empty slot tentative. If this is a 16 port RTA, make both +** slots tentative, and the second one RTA_SECOND_SLOT as well. +*/ + +void rio_fill_host_slot(int entry, int entry2, unsigned int rta_uniq, struct Host *host) +{ + int link; + + rio_dprintk(RIO_DEBUG_BOOT, "rio_fill_host_slot(%d, %d, 0x%x...)\n", entry, entry2, rta_uniq); + + host->Mapping[entry].Flags = (RTA_BOOTED | RTA_NEWBOOT | SLOT_TENTATIVE); + host->Mapping[entry].SysPort = NO_PORT; + host->Mapping[entry].RtaUniqueNum = rta_uniq; + host->Mapping[entry].HostUniqueNum = host->UniqueNum; + host->Mapping[entry].ID = entry + 1; + host->Mapping[entry].ID2 = 0; + if (entry2) { + host->Mapping[entry2].Flags = (RTA_BOOTED | RTA_NEWBOOT | SLOT_TENTATIVE | RTA16_SECOND_SLOT); + host->Mapping[entry2].SysPort = NO_PORT; + host->Mapping[entry2].RtaUniqueNum = rta_uniq; + host->Mapping[entry2].HostUniqueNum = host->UniqueNum; + host->Mapping[entry2].Name[0] = '\0'; + host->Mapping[entry2].ID = entry2 + 1; + host->Mapping[entry2].ID2 = entry + 1; + host->Mapping[entry].ID2 = entry2 + 1; + } + /* + ** Must set these up, so that utilities show + ** topology of 16 port RTAs correctly + */ + for (link = 0; link < LINKS_PER_UNIT; link++) { + host->Mapping[entry].Topology[link].Unit = ROUTE_DISCONNECT; + host->Mapping[entry].Topology[link].Link = NO_LINK; + if (entry2) { + host->Mapping[entry2].Topology[link].Unit = ROUTE_DISCONNECT; + host->Mapping[entry2].Topology[link].Link = NO_LINK; + } + } +} diff --git a/drivers/staging/generic_serial/rio/riocmd.c b/drivers/staging/generic_serial/rio/riocmd.c new file mode 100644 index 0000000..f121357 --- /dev/null +++ b/drivers/staging/generic_serial/rio/riocmd.c @@ -0,0 +1,939 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** ported from the existing SCO driver source +** + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riocmd.c +** SID : 1.2 +** Last Modified : 11/6/98 10:33:41 +** Retrieved : 11/6/98 10:33:49 +** +** ident @(#)riocmd.c 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" + + +static struct IdentifyRta IdRta; +static struct KillNeighbour KillUnit; + +int RIOFoadRta(struct Host *HostP, struct Map *MapP) +{ + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA\n"); + + CmdBlkP = RIOGetCmdBlk(); + + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA: GetCmdBlk failed\n"); + return -ENXIO; + } + + CmdBlkP->Packet.dest_unit = MapP->ID; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.src_port = BOOT_RUP; + CmdBlkP->Packet.len = 0x84; + CmdBlkP->Packet.data[0] = IFOAD; + CmdBlkP->Packet.data[1] = 0; + CmdBlkP->Packet.data[2] = IFOAD_MAGIC & 0xFF; + CmdBlkP->Packet.data[3] = (IFOAD_MAGIC >> 8) & 0xFF; + + if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA: Failed to queue foad command\n"); + return -EIO; + } + return 0; +} + +int RIOZombieRta(struct Host *HostP, struct Map *MapP) +{ + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA\n"); + + CmdBlkP = RIOGetCmdBlk(); + + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA: GetCmdBlk failed\n"); + return -ENXIO; + } + + CmdBlkP->Packet.dest_unit = MapP->ID; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.src_port = BOOT_RUP; + CmdBlkP->Packet.len = 0x84; + CmdBlkP->Packet.data[0] = ZOMBIE; + CmdBlkP->Packet.data[1] = 0; + CmdBlkP->Packet.data[2] = ZOMBIE_MAGIC & 0xFF; + CmdBlkP->Packet.data[3] = (ZOMBIE_MAGIC >> 8) & 0xFF; + + if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA: Failed to queue zombie command\n"); + return -EIO; + } + return 0; +} + +int RIOCommandRta(struct rio_info *p, unsigned long RtaUnique, int (*func) (struct Host * HostP, struct Map * MapP)) +{ + unsigned int Host; + + rio_dprintk(RIO_DEBUG_CMD, "Command RTA 0x%lx func %p\n", RtaUnique, func); + + if (!RtaUnique) + return (0); + + for (Host = 0; Host < p->RIONumHosts; Host++) { + unsigned int Rta; + struct Host *HostP = &p->RIOHosts[Host]; + + for (Rta = 0; Rta < RTAS_PER_HOST; Rta++) { + struct Map *MapP = &HostP->Mapping[Rta]; + + if (MapP->RtaUniqueNum == RtaUnique) { + uint Link; + + /* + ** now, lets just check we have a route to it... + ** IF the routing stuff is working, then one of the + ** topology entries for this unit will have a legit + ** route *somewhere*. We care not where - if its got + ** any connections, we can get to it. + */ + for (Link = 0; Link < LINKS_PER_UNIT; Link++) { + if (MapP->Topology[Link].Unit <= (u8) MAX_RUP) { + /* + ** Its worth trying the operation... + */ + return (*func) (HostP, MapP); + } + } + } + } + } + return -ENXIO; +} + + +int RIOIdentifyRta(struct rio_info *p, void __user * arg) +{ + unsigned int Host; + + if (copy_from_user(&IdRta, arg, sizeof(IdRta))) { + rio_dprintk(RIO_DEBUG_CMD, "RIO_IDENTIFY_RTA copy failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + + for (Host = 0; Host < p->RIONumHosts; Host++) { + unsigned int Rta; + struct Host *HostP = &p->RIOHosts[Host]; + + for (Rta = 0; Rta < RTAS_PER_HOST; Rta++) { + struct Map *MapP = &HostP->Mapping[Rta]; + + if (MapP->RtaUniqueNum == IdRta.RtaUnique) { + uint Link; + /* + ** now, lets just check we have a route to it... + ** IF the routing stuff is working, then one of the + ** topology entries for this unit will have a legit + ** route *somewhere*. We care not where - if its got + ** any connections, we can get to it. + */ + for (Link = 0; Link < LINKS_PER_UNIT; Link++) { + if (MapP->Topology[Link].Unit <= (u8) MAX_RUP) { + /* + ** Its worth trying the operation... + */ + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA\n"); + + CmdBlkP = RIOGetCmdBlk(); + + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA: GetCmdBlk failed\n"); + return -ENXIO; + } + + CmdBlkP->Packet.dest_unit = MapP->ID; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.src_port = BOOT_RUP; + CmdBlkP->Packet.len = 0x84; + CmdBlkP->Packet.data[0] = IDENTIFY; + CmdBlkP->Packet.data[1] = 0; + CmdBlkP->Packet.data[2] = IdRta.ID; + + if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA: Failed to queue command\n"); + return -EIO; + } + return 0; + } + } + } + } + } + return -ENOENT; +} + + +int RIOKillNeighbour(struct rio_info *p, void __user * arg) +{ + uint Host; + uint ID; + struct Host *HostP; + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CMD, "KILL HOST NEIGHBOUR\n"); + + if (copy_from_user(&KillUnit, arg, sizeof(KillUnit))) { + rio_dprintk(RIO_DEBUG_CMD, "RIO_KILL_NEIGHBOUR copy failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + + if (KillUnit.Link > 3) + return -ENXIO; + + CmdBlkP = RIOGetCmdBlk(); + + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CMD, "UFOAD: GetCmdBlk failed\n"); + return -ENXIO; + } + + CmdBlkP->Packet.dest_unit = 0; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_port = BOOT_RUP; + CmdBlkP->Packet.len = 0x84; + CmdBlkP->Packet.data[0] = UFOAD; + CmdBlkP->Packet.data[1] = KillUnit.Link; + CmdBlkP->Packet.data[2] = UFOAD_MAGIC & 0xFF; + CmdBlkP->Packet.data[3] = (UFOAD_MAGIC >> 8) & 0xFF; + + for (Host = 0; Host < p->RIONumHosts; Host++) { + ID = 0; + HostP = &p->RIOHosts[Host]; + + if (HostP->UniqueNum == KillUnit.UniqueNum) { + if (RIOQueueCmdBlk(HostP, RTAS_PER_HOST + KillUnit.Link, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "UFOAD: Failed queue command\n"); + return -EIO; + } + return 0; + } + + for (ID = 0; ID < RTAS_PER_HOST; ID++) { + if (HostP->Mapping[ID].RtaUniqueNum == KillUnit.UniqueNum) { + CmdBlkP->Packet.dest_unit = ID + 1; + if (RIOQueueCmdBlk(HostP, ID, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "UFOAD: Failed queue command\n"); + return -EIO; + } + return 0; + } + } + } + RIOFreeCmdBlk(CmdBlkP); + return -ENXIO; +} + +int RIOSuspendBootRta(struct Host *HostP, int ID, int Link) +{ + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA ID %d, link %c\n", ID, 'A' + Link); + + CmdBlkP = RIOGetCmdBlk(); + + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA: GetCmdBlk failed\n"); + return -ENXIO; + } + + CmdBlkP->Packet.dest_unit = ID; + CmdBlkP->Packet.dest_port = BOOT_RUP; + CmdBlkP->Packet.src_unit = 0; + CmdBlkP->Packet.src_port = BOOT_RUP; + CmdBlkP->Packet.len = 0x84; + CmdBlkP->Packet.data[0] = IWAIT; + CmdBlkP->Packet.data[1] = Link; + CmdBlkP->Packet.data[2] = IWAIT_MAGIC & 0xFF; + CmdBlkP->Packet.data[3] = (IWAIT_MAGIC >> 8) & 0xFF; + + if (RIOQueueCmdBlk(HostP, ID - 1, CmdBlkP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA: Failed to queue iwait command\n"); + return -EIO; + } + return 0; +} + +int RIOFoadWakeup(struct rio_info *p) +{ + int port; + struct Port *PortP; + unsigned long flags; + + for (port = 0; port < RIO_PORTS; port++) { + PortP = p->RIOPortp[port]; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->Config = 0; + PortP->State = 0; + PortP->InUse = NOT_INUSE; + PortP->PortState = 0; + PortP->FlushCmdBodge = 0; + PortP->ModemLines = 0; + PortP->ModemState = 0; + PortP->CookMode = 0; + PortP->ParamSem = 0; + PortP->Mapped = 0; + PortP->WflushFlag = 0; + PortP->MagicFlags = 0; + PortP->RxDataStart = 0; + PortP->TxBufferIn = 0; + PortP->TxBufferOut = 0; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + return (0); +} + +/* +** Incoming command on the COMMAND_RUP to be processed. +*/ +static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struct PKT __iomem *PacketP) +{ + struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *)PacketP->data; + struct Port *PortP; + struct UnixRup *UnixRupP; + unsigned short SysPort; + unsigned short ReportedModemStatus; + unsigned short rup; + unsigned short subCommand; + unsigned long flags; + + func_enter(); + + /* + ** 16 port RTA note: + ** Command rup packets coming from the RTA will have pkt->data[1] (which + ** translates to PktCmdP->PhbNum) set to the host port number for the + ** particular unit. To access the correct BaseSysPort for a 16 port RTA, + ** we can use PhbNum to get the rup number for the appropriate 8 port + ** block (for the first block, this should be equal to 'Rup'). + */ + rup = readb(&PktCmdP->PhbNum) / (unsigned short) PORTS_PER_RTA; + UnixRupP = &HostP->UnixRups[rup]; + SysPort = UnixRupP->BaseSysPort + (readb(&PktCmdP->PhbNum) % (unsigned short) PORTS_PER_RTA); + rio_dprintk(RIO_DEBUG_CMD, "Command on rup %d, port %d\n", rup, SysPort); + + if (UnixRupP->BaseSysPort == NO_PORT) { + rio_dprintk(RIO_DEBUG_CMD, "OBSCURE ERROR!\n"); + rio_dprintk(RIO_DEBUG_CMD, "Diagnostics follow. Please WRITE THESE DOWN and report them to Specialix Technical Support\n"); + rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Host number %Zd, name ``%s''\n", HostP - p->RIOHosts, HostP->Name); + rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Rup number 0x%x\n", rup); + + if (Rup < (unsigned short) MAX_RUP) { + rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for RTA ``%s''\n", HostP->Mapping[Rup].Name); + } else + rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for link ``%c'' of host ``%s''\n", ('A' + Rup - MAX_RUP), HostP->Name); + + rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Destination 0x%x:0x%x\n", readb(&PacketP->dest_unit), readb(&PacketP->dest_port)); + rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Source 0x%x:0x%x\n", readb(&PacketP->src_unit), readb(&PacketP->src_port)); + rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Length 0x%x (%d)\n", readb(&PacketP->len), readb(&PacketP->len)); + rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Control 0x%x (%d)\n", readb(&PacketP->control), readb(&PacketP->control)); + rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Check 0x%x (%d)\n", readw(&PacketP->csum), readw(&PacketP->csum)); + rio_dprintk(RIO_DEBUG_CMD, "COMMAND information: Host Port Number 0x%x, " "Command Code 0x%x\n", readb(&PktCmdP->PhbNum), readb(&PktCmdP->Command)); + return 1; + } + PortP = p->RIOPortp[SysPort]; + rio_spin_lock_irqsave(&PortP->portSem, flags); + switch (readb(&PktCmdP->Command)) { + case RIOC_BREAK_RECEIVED: + rio_dprintk(RIO_DEBUG_CMD, "Received a break!\n"); + /* If the current line disc. is not multi-threading and + the current processor is not the default, reset rup_intr + and return 0 to ensure that the command packet is + not freed. */ + /* Call tmgr HANGUP HERE */ + /* Fix this later when every thing works !!!! RAMRAJ */ + gs_got_break(&PortP->gs); + break; + + case RIOC_COMPLETE: + rio_dprintk(RIO_DEBUG_CMD, "Command complete on phb %d host %Zd\n", readb(&PktCmdP->PhbNum), HostP - p->RIOHosts); + subCommand = 1; + switch (readb(&PktCmdP->SubCommand)) { + case RIOC_MEMDUMP: + rio_dprintk(RIO_DEBUG_CMD, "Memory dump cmd (0x%x) from addr 0x%x\n", readb(&PktCmdP->SubCommand), readw(&PktCmdP->SubAddr)); + break; + case RIOC_READ_REGISTER: + rio_dprintk(RIO_DEBUG_CMD, "Read register (0x%x)\n", readw(&PktCmdP->SubAddr)); + p->CdRegister = (readb(&PktCmdP->ModemStatus) & RIOC_MSVR1_HOST); + break; + default: + subCommand = 0; + break; + } + if (subCommand) + break; + rio_dprintk(RIO_DEBUG_CMD, "New status is 0x%x was 0x%x\n", readb(&PktCmdP->PortStatus), PortP->PortState); + if (PortP->PortState != readb(&PktCmdP->PortStatus)) { + rio_dprintk(RIO_DEBUG_CMD, "Mark status & wakeup\n"); + PortP->PortState = readb(&PktCmdP->PortStatus); + /* What should we do here ... + wakeup( &PortP->PortState ); + */ + } else + rio_dprintk(RIO_DEBUG_CMD, "No change\n"); + + /* FALLTHROUGH */ + case RIOC_MODEM_STATUS: + /* + ** Knock out the tbusy and tstop bits, as these are not relevant + ** to the check for modem status change (they're just there because + ** it's a convenient place to put them!). + */ + ReportedModemStatus = readb(&PktCmdP->ModemStatus); + if ((PortP->ModemState & RIOC_MSVR1_HOST) == + (ReportedModemStatus & RIOC_MSVR1_HOST)) { + rio_dprintk(RIO_DEBUG_CMD, "Modem status unchanged 0x%x\n", PortP->ModemState); + /* + ** Update ModemState just in case tbusy or tstop states have + ** changed. + */ + PortP->ModemState = ReportedModemStatus; + } else { + rio_dprintk(RIO_DEBUG_CMD, "Modem status change from 0x%x to 0x%x\n", PortP->ModemState, ReportedModemStatus); + PortP->ModemState = ReportedModemStatus; +#ifdef MODEM_SUPPORT + if (PortP->Mapped) { + /***********************************************************\ + ************************************************************* + *** *** + *** M O D E M S T A T E C H A N G E *** + *** *** + ************************************************************* + \***********************************************************/ + /* + ** If the device is a modem, then check the modem + ** carrier. + */ + if (PortP->gs.port.tty == NULL) + break; + if (PortP->gs.port.tty->termios == NULL) + break; + + if (!(PortP->gs.port.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) { + + rio_dprintk(RIO_DEBUG_CMD, "Is there a Carrier?\n"); + /* + ** Is there a carrier? + */ + if (PortP->ModemState & RIOC_MSVR1_CD) { + /* + ** Has carrier just appeared? + */ + if (!(PortP->State & RIO_CARR_ON)) { + rio_dprintk(RIO_DEBUG_CMD, "Carrier just came up.\n"); + PortP->State |= RIO_CARR_ON; + /* + ** wakeup anyone in WOPEN + */ + if (PortP->State & (PORT_ISOPEN | RIO_WOPEN)) + wake_up_interruptible(&PortP->gs.port.open_wait); + } + } else { + /* + ** Has carrier just dropped? + */ + if (PortP->State & RIO_CARR_ON) { + if (PortP->State & (PORT_ISOPEN | RIO_WOPEN | RIO_MOPEN)) + tty_hangup(PortP->gs.port.tty); + PortP->State &= ~RIO_CARR_ON; + rio_dprintk(RIO_DEBUG_CMD, "Carrirer just went down\n"); + } + } + } + } +#endif + } + break; + + default: + rio_dprintk(RIO_DEBUG_CMD, "Unknown command %d on CMD_RUP of host %Zd\n", readb(&PktCmdP->Command), HostP - p->RIOHosts); + break; + } + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + + func_exit(); + + return 1; +} + +/* +** The command mechanism: +** Each rup has a chain of commands associated with it. +** This chain is maintained by routines in this file. +** Periodically we are called and we run a quick check of all the +** active chains to determine if there is a command to be executed, +** and if the rup is ready to accept it. +** +*/ + +/* +** Allocate an empty command block. +*/ +struct CmdBlk *RIOGetCmdBlk(void) +{ + struct CmdBlk *CmdBlkP; + + CmdBlkP = kzalloc(sizeof(struct CmdBlk), GFP_ATOMIC); + return CmdBlkP; +} + +/* +** Return a block to the head of the free list. +*/ +void RIOFreeCmdBlk(struct CmdBlk *CmdBlkP) +{ + kfree(CmdBlkP); +} + +/* +** attach a command block to the list of commands to be performed for +** a given rup. +*/ +int RIOQueueCmdBlk(struct Host *HostP, uint Rup, struct CmdBlk *CmdBlkP) +{ + struct CmdBlk **Base; + struct UnixRup *UnixRupP; + unsigned long flags; + + if (Rup >= (unsigned short) (MAX_RUP + LINKS_PER_UNIT)) { + rio_dprintk(RIO_DEBUG_CMD, "Illegal rup number %d in RIOQueueCmdBlk\n", Rup); + RIOFreeCmdBlk(CmdBlkP); + return RIO_FAIL; + } + + UnixRupP = &HostP->UnixRups[Rup]; + + rio_spin_lock_irqsave(&UnixRupP->RupLock, flags); + + /* + ** If the RUP is currently inactive, then put the request + ** straight on the RUP.... + */ + if ((UnixRupP->CmdsWaitingP == NULL) && (UnixRupP->CmdPendingP == NULL) && (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE) && (CmdBlkP->PreFuncP ? (*CmdBlkP->PreFuncP) (CmdBlkP->PreArg, CmdBlkP) + : 1)) { + rio_dprintk(RIO_DEBUG_CMD, "RUP inactive-placing command straight on. Cmd byte is 0x%x\n", CmdBlkP->Packet.data[0]); + + /* + ** Whammy! blat that pack! + */ + HostP->Copy(&CmdBlkP->Packet, RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->txpkt)), sizeof(struct PKT)); + + /* + ** place command packet on the pending position. + */ + UnixRupP->CmdPendingP = CmdBlkP; + + /* + ** set the command register + */ + writew(TX_PACKET_READY, &UnixRupP->RupP->txcontrol); + + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + + return 0; + } + rio_dprintk(RIO_DEBUG_CMD, "RUP active - en-queing\n"); + + if (UnixRupP->CmdsWaitingP != NULL) + rio_dprintk(RIO_DEBUG_CMD, "Rup active - command waiting\n"); + if (UnixRupP->CmdPendingP != NULL) + rio_dprintk(RIO_DEBUG_CMD, "Rup active - command pending\n"); + if (readw(&UnixRupP->RupP->txcontrol) != TX_RUP_INACTIVE) + rio_dprintk(RIO_DEBUG_CMD, "Rup active - command rup not ready\n"); + + Base = &UnixRupP->CmdsWaitingP; + + rio_dprintk(RIO_DEBUG_CMD, "First try to queue cmdblk %p at %p\n", CmdBlkP, Base); + + while (*Base) { + rio_dprintk(RIO_DEBUG_CMD, "Command cmdblk %p here\n", *Base); + Base = &((*Base)->NextP); + rio_dprintk(RIO_DEBUG_CMD, "Now try to queue cmd cmdblk %p at %p\n", CmdBlkP, Base); + } + + rio_dprintk(RIO_DEBUG_CMD, "Will queue cmdblk %p at %p\n", CmdBlkP, Base); + + *Base = CmdBlkP; + + CmdBlkP->NextP = NULL; + + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + + return 0; +} + +/* +** Here we go - if there is an empty rup, fill it! +** must be called at splrio() or higher. +*/ +void RIOPollHostCommands(struct rio_info *p, struct Host *HostP) +{ + struct CmdBlk *CmdBlkP; + struct UnixRup *UnixRupP; + struct PKT __iomem *PacketP; + unsigned short Rup; + unsigned long flags; + + + Rup = MAX_RUP + LINKS_PER_UNIT; + + do { /* do this loop for each RUP */ + /* + ** locate the rup we are processing & lock it + */ + UnixRupP = &HostP->UnixRups[--Rup]; + + spin_lock_irqsave(&UnixRupP->RupLock, flags); + + /* + ** First check for incoming commands: + */ + if (readw(&UnixRupP->RupP->rxcontrol) != RX_RUP_INACTIVE) { + int FreeMe; + + PacketP = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->rxpkt)); + + switch (readb(&PacketP->dest_port)) { + case BOOT_RUP: + rio_dprintk(RIO_DEBUG_CMD, "Incoming Boot %s packet '%x'\n", readb(&PacketP->len) & 0x80 ? "Command" : "Data", readb(&PacketP->data[0])); + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + FreeMe = RIOBootRup(p, Rup, HostP, PacketP); + rio_spin_lock_irqsave(&UnixRupP->RupLock, flags); + break; + + case COMMAND_RUP: + /* + ** Free the RUP lock as loss of carrier causes a + ** ttyflush which will (eventually) call another + ** routine that uses the RUP lock. + */ + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + FreeMe = RIOCommandRup(p, Rup, HostP, PacketP); + if (readb(&PacketP->data[5]) == RIOC_MEMDUMP) { + rio_dprintk(RIO_DEBUG_CMD, "Memdump from 0x%x complete\n", readw(&(PacketP->data[6]))); + rio_memcpy_fromio(p->RIOMemDump, &(PacketP->data[8]), 32); + } + rio_spin_lock_irqsave(&UnixRupP->RupLock, flags); + break; + + case ROUTE_RUP: + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + FreeMe = RIORouteRup(p, Rup, HostP, PacketP); + rio_spin_lock_irqsave(&UnixRupP->RupLock, flags); + break; + + default: + rio_dprintk(RIO_DEBUG_CMD, "Unknown RUP %d\n", readb(&PacketP->dest_port)); + FreeMe = 1; + break; + } + + if (FreeMe) { + rio_dprintk(RIO_DEBUG_CMD, "Free processed incoming command packet\n"); + put_free_end(HostP, PacketP); + + writew(RX_RUP_INACTIVE, &UnixRupP->RupP->rxcontrol); + + if (readw(&UnixRupP->RupP->handshake) == PHB_HANDSHAKE_SET) { + rio_dprintk(RIO_DEBUG_CMD, "Handshake rup %d\n", Rup); + writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &UnixRupP->RupP->handshake); + } + } + } + + /* + ** IF a command was running on the port, + ** and it has completed, then tidy it up. + */ + if ((CmdBlkP = UnixRupP->CmdPendingP) && /* ASSIGN! */ + (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE)) { + /* + ** we are idle. + ** there is a command in pending. + ** Therefore, this command has finished. + ** So, wakeup whoever is waiting for it (and tell them + ** what happened). + */ + if (CmdBlkP->Packet.dest_port == BOOT_RUP) + rio_dprintk(RIO_DEBUG_CMD, "Free Boot %s Command Block '%x'\n", CmdBlkP->Packet.len & 0x80 ? "Command" : "Data", CmdBlkP->Packet.data[0]); + + rio_dprintk(RIO_DEBUG_CMD, "Command %p completed\n", CmdBlkP); + + /* + ** Clear the Rup lock to prevent mutual exclusion. + */ + if (CmdBlkP->PostFuncP) { + rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + (*CmdBlkP->PostFuncP) (CmdBlkP->PostArg, CmdBlkP); + rio_spin_lock_irqsave(&UnixRupP->RupLock, flags); + } + + /* + ** ....clear the pending flag.... + */ + UnixRupP->CmdPendingP = NULL; + + /* + ** ....and return the command block to the freelist. + */ + RIOFreeCmdBlk(CmdBlkP); + } + + /* + ** If there is a command for this rup, and the rup + ** is idle, then process the command + */ + if ((CmdBlkP = UnixRupP->CmdsWaitingP) && /* ASSIGN! */ + (UnixRupP->CmdPendingP == NULL) && (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE)) { + /* + ** if the pre-function is non-zero, call it. + ** If it returns RIO_FAIL then don't + ** send this command yet! + */ + if (!(CmdBlkP->PreFuncP ? (*CmdBlkP->PreFuncP) (CmdBlkP->PreArg, CmdBlkP) : 1)) { + rio_dprintk(RIO_DEBUG_CMD, "Not ready to start command %p\n", CmdBlkP); + } else { + rio_dprintk(RIO_DEBUG_CMD, "Start new command %p Cmd byte is 0x%x\n", CmdBlkP, CmdBlkP->Packet.data[0]); + /* + ** Whammy! blat that pack! + */ + HostP->Copy(&CmdBlkP->Packet, RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->txpkt)), sizeof(struct PKT)); + + /* + ** remove the command from the rup command queue... + */ + UnixRupP->CmdsWaitingP = CmdBlkP->NextP; + + /* + ** ...and place it on the pending position. + */ + UnixRupP->CmdPendingP = CmdBlkP; + + /* + ** set the command register + */ + writew(TX_PACKET_READY, &UnixRupP->RupP->txcontrol); + + /* + ** the command block will be freed + ** when the command has been processed. + */ + } + } + spin_unlock_irqrestore(&UnixRupP->RupLock, flags); + } while (Rup); +} + +int RIOWFlushMark(unsigned long iPortP, struct CmdBlk *CmdBlkP) +{ + struct Port *PortP = (struct Port *) iPortP; + unsigned long flags; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->WflushFlag++; + PortP->MagicFlags |= MAGIC_FLUSH; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return RIOUnUse(iPortP, CmdBlkP); +} + +int RIORFlushEnable(unsigned long iPortP, struct CmdBlk *CmdBlkP) +{ + struct Port *PortP = (struct Port *) iPortP; + struct PKT __iomem *PacketP; + unsigned long flags; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + while (can_remove_receive(&PacketP, PortP)) { + remove_receive(PortP); + put_free_end(PortP->HostP, PacketP); + } + + if (readw(&PortP->PhbP->handshake) == PHB_HANDSHAKE_SET) { + /* + ** MAGIC! (Basically, handshake the RX buffer, so that + ** the RTAs upstream can be re-enabled.) + */ + rio_dprintk(RIO_DEBUG_CMD, "Util: Set RX handshake bit\n"); + writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &PortP->PhbP->handshake); + } + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return RIOUnUse(iPortP, CmdBlkP); +} + +int RIOUnUse(unsigned long iPortP, struct CmdBlk *CmdBlkP) +{ + struct Port *PortP = (struct Port *) iPortP; + unsigned long flags; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + rio_dprintk(RIO_DEBUG_CMD, "Decrement in use count for port\n"); + + if (PortP->InUse) { + if (--PortP->InUse != NOT_INUSE) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return 0; + } + } + /* + ** While PortP->InUse is set (i.e. a preemptive command has been sent to + ** the RTA and is awaiting completion), any transmit data is prevented from + ** being transferred from the write queue into the transmit packets + ** (add_transmit) and no furthur transmit interrupt will be sent for that + ** data. The next interrupt will occur up to 500ms later (RIOIntr is called + ** twice a second as a saftey measure). This was the case when kermit was + ** used to send data into a RIO port. After each packet was sent, TCFLSH + ** was called to flush the read queue preemptively. PortP->InUse was + ** incremented, thereby blocking the 6 byte acknowledgement packet + ** transmitted back. This acknowledgment hung around for 500ms before + ** being sent, thus reducing input performance substantially!. + ** When PortP->InUse becomes NOT_INUSE, we must ensure that any data + ** hanging around in the transmit buffer is sent immediately. + */ + writew(1, &PortP->HostP->ParmMapP->tx_intr); + /* What to do here .. + wakeup( (caddr_t)&(PortP->InUse) ); + */ + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return 0; +} + +/* +** +** How to use this file: +** +** To send a command down a rup, you need to allocate a command block, fill +** in the packet information, fill in the command number, fill in the pre- +** and post- functions and arguments, and then add the command block to the +** queue of command blocks for the port in question. When the port is idle, +** then the pre-function will be called. If this returns RIO_FAIL then the +** command will be re-queued and tried again at a later date (probably in one +** clock tick). If the pre-function returns NOT RIO_FAIL, then the command +** packet will be queued on the RUP, and the txcontrol field set to the +** command number. When the txcontrol field has changed from being the +** command number, then the post-function will be called, with the argument +** specified earlier, a pointer to the command block, and the value of +** txcontrol. +** +** To allocate a command block, call RIOGetCmdBlk(). This returns a pointer +** to the command block structure allocated, or NULL if there aren't any. +** The block will have been zeroed for you. +** +** The structure has the following fields: +** +** struct CmdBlk +** { +** struct CmdBlk *NextP; ** Pointer to next command block ** +** struct PKT Packet; ** A packet, to copy to the rup ** +** int (*PreFuncP)(); ** The func to call to check if OK ** +** int PreArg; ** The arg for the func ** +** int (*PostFuncP)(); ** The func to call when completed ** +** int PostArg; ** The arg for the func ** +** }; +** +** You need to fill in ALL fields EXCEPT NextP, which is used to link the +** blocks together either on the free list or on the Rup list. +** +** Packet is an actual packet structure to be filled in with the packet +** information associated with the command. You need to fill in everything, +** as the command processor doesn't process the command packet in any way. +** +** The PreFuncP is called before the packet is enqueued on the host rup. +** PreFuncP is called as (*PreFuncP)(PreArg, CmdBlkP);. PreFuncP must +** return !RIO_FAIL to have the packet queued on the rup, and RIO_FAIL +** if the packet is NOT to be queued. +** +** The PostFuncP is called when the command has completed. It is called +** as (*PostFuncP)(PostArg, CmdBlkP, txcontrol);. PostFuncP is not expected +** to return a value. PostFuncP does NOT need to free the command block, +** as this happens automatically after PostFuncP returns. +** +** Once the command block has been filled in, it is attached to the correct +** queue by calling RIOQueueCmdBlk( HostP, Rup, CmdBlkP ) where HostP is +** a pointer to the struct Host, Rup is the NUMBER of the rup (NOT a pointer +** to it!), and CmdBlkP is the pointer to the command block allocated using +** RIOGetCmdBlk(). +** +*/ diff --git a/drivers/staging/generic_serial/rio/rioctrl.c b/drivers/staging/generic_serial/rio/rioctrl.c new file mode 100644 index 0000000..7805063 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioctrl.c @@ -0,0 +1,1504 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioctrl.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:42 +** Retrieved : 11/6/98 10:33:49 +** +** ident @(#)rioctrl.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" + + +static struct LpbReq LpbReq; +static struct RupReq RupReq; +static struct PortReq PortReq; +static struct HostReq HostReq; /* oh really? global? and no locking? */ +static struct HostDpRam HostDpRam; +static struct DebugCtrl DebugCtrl; +static struct Map MapEnt; +static struct PortSetup PortSetup; +static struct DownLoad DownLoad; +static struct SendPack SendPack; +/* static struct StreamInfo StreamInfo; */ +/* static char modemtable[RIO_PORTS]; */ +static struct SpecialRupCmd SpecialRupCmd; +static struct PortParams PortParams; +static struct portStats portStats; + +static struct SubCmdStruct { + ushort Host; + ushort Rup; + ushort Port; + ushort Addr; +} SubCmd; + +struct PortTty { + uint port; + struct ttystatics Tty; +}; + +static struct PortTty PortTty; +typedef struct ttystatics TERMIO; + +/* +** This table is used when the config.rio downloads bin code to the +** driver. We index the table using the product code, 0-F, and call +** the function pointed to by the entry, passing the information +** about the boot. +** The RIOBootCodeUNKNOWN entry is there to politely tell the calling +** process to bog off. +*/ +static int + (*RIOBootTable[MAX_PRODUCT]) (struct rio_info *, struct DownLoad *) = { + /* 0 */ RIOBootCodeHOST, + /* Host Card */ + /* 1 */ RIOBootCodeRTA, + /* RTA */ +}; + +#define drv_makedev(maj, min) ((((uint) maj & 0xff) << 8) | ((uint) min & 0xff)) + +static int copy_from_io(void __user *to, void __iomem *from, size_t size) +{ + void *buf = kmalloc(size, GFP_KERNEL); + int res = -ENOMEM; + if (buf) { + rio_memcpy_fromio(buf, from, size); + res = copy_to_user(to, buf, size); + kfree(buf); + } + return res; +} + +int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su) +{ + uint Host; /* leave me unsigned! */ + uint port; /* and me! */ + struct Host *HostP; + ushort loop; + int Entry; + struct Port *PortP; + struct PKT __iomem *PacketP; + int retval = 0; + unsigned long flags; + void __user *argp = (void __user *)arg; + + func_enter(); + + /* Confuse the compiler to think that we've initialized these */ + Host = 0; + PortP = NULL; + + rio_dprintk(RIO_DEBUG_CTRL, "control ioctl cmd: 0x%x arg: %p\n", cmd, argp); + + switch (cmd) { + /* + ** RIO_SET_TIMER + ** + ** Change the value of the host card interrupt timer. + ** If the host card number is -1 then all host cards are changed + ** otherwise just the specified host card will be changed. + */ + case RIO_SET_TIMER: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_TIMER to %ldms\n", arg); + { + int host, value; + host = (arg >> 16) & 0x0000FFFF; + value = arg & 0x0000ffff; + if (host == -1) { + for (host = 0; host < p->RIONumHosts; host++) { + if (p->RIOHosts[host].Flags == RC_RUNNING) { + writew(value, &p->RIOHosts[host].ParmMapP->timer); + } + } + } else if (host >= p->RIONumHosts) { + return -EINVAL; + } else { + if (p->RIOHosts[host].Flags == RC_RUNNING) { + writew(value, &p->RIOHosts[host].ParmMapP->timer); + } + } + } + return 0; + + case RIO_FOAD_RTA: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_FOAD_RTA\n"); + return RIOCommandRta(p, arg, RIOFoadRta); + + case RIO_ZOMBIE_RTA: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_ZOMBIE_RTA\n"); + return RIOCommandRta(p, arg, RIOZombieRta); + + case RIO_IDENTIFY_RTA: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_IDENTIFY_RTA\n"); + return RIOIdentifyRta(p, argp); + + case RIO_KILL_NEIGHBOUR: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_KILL_NEIGHBOUR\n"); + return RIOKillNeighbour(p, argp); + + case SPECIAL_RUP_CMD: + { + struct CmdBlk *CmdBlkP; + + rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD\n"); + if (copy_from_user(&SpecialRupCmd, argp, sizeof(SpecialRupCmd))) { + rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD copy failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + CmdBlkP = RIOGetCmdBlk(); + if (!CmdBlkP) { + rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD GetCmdBlk failed\n"); + return -ENXIO; + } + CmdBlkP->Packet = SpecialRupCmd.Packet; + if (SpecialRupCmd.Host >= p->RIONumHosts) + SpecialRupCmd.Host = 0; + rio_dprintk(RIO_DEBUG_CTRL, "Queue special rup command for host %d rup %d\n", SpecialRupCmd.Host, SpecialRupCmd.RupNum); + if (RIOQueueCmdBlk(&p->RIOHosts[SpecialRupCmd.Host], SpecialRupCmd.RupNum, CmdBlkP) == RIO_FAIL) { + printk(KERN_WARNING "rio: FAILED TO QUEUE SPECIAL RUP COMMAND\n"); + } + return 0; + } + + case RIO_DEBUG_MEM: + return -EPERM; + + case RIO_ALL_MODEM: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_ALL_MODEM\n"); + p->RIOError.Error = IOCTL_COMMAND_UNKNOWN; + return -EINVAL; + + case RIO_GET_TABLE: + /* + ** Read the routing table from the device driver to user space + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_TABLE\n"); + + if ((retval = RIOApel(p)) != 0) + return retval; + + if (copy_to_user(argp, p->RIOConnectTable, TOTAL_MAP_ENTRIES * sizeof(struct Map))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_TABLE copy failed\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + + { + int entry; + rio_dprintk(RIO_DEBUG_CTRL, "*****\nMAP ENTRIES\n"); + for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) { + if ((p->RIOConnectTable[entry].ID == 0) && (p->RIOConnectTable[entry].HostUniqueNum == 0) && (p->RIOConnectTable[entry].RtaUniqueNum == 0)) + continue; + + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.HostUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].HostUniqueNum); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.RtaUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].RtaUniqueNum); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.ID = 0x%x\n", entry, p->RIOConnectTable[entry].ID); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.ID2 = 0x%x\n", entry, p->RIOConnectTable[entry].ID2); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Flags = 0x%x\n", entry, (int) p->RIOConnectTable[entry].Flags); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.SysPort = 0x%x\n", entry, (int) p->RIOConnectTable[entry].SysPort); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[0].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[0].Unit); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[0].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[0].Link); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[1].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[1].Unit); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[1].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[1].Link); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[2].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[2].Unit); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[2].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[2].Link); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[3].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[3].Unit); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[4].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[3].Link); + rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Name = %s\n", entry, p->RIOConnectTable[entry].Name); + } + rio_dprintk(RIO_DEBUG_CTRL, "*****\nEND MAP ENTRIES\n"); + } + p->RIOQuickCheck = NOT_CHANGED; /* a table has been gotten */ + return 0; + + case RIO_PUT_TABLE: + /* + ** Write the routing table to the device driver from user space + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE\n"); + + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&p->RIOConnectTable[0], argp, TOTAL_MAP_ENTRIES * sizeof(struct Map))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE copy failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } +/* +*********************************** + { + int entry; + rio_dprint(RIO_DEBUG_CTRL, ("*****\nMAP ENTRIES\n") ); + for ( entry=0; entry<TOTAL_MAP_ENTRIES; entry++ ) + { + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.HostUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].HostUniqueNum ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.RtaUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].RtaUniqueNum ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.ID = 0x%x\n", entry, p->RIOConnectTable[entry].ID ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.ID2 = 0x%x\n", entry, p->RIOConnectTable[entry].ID2 ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Flags = 0x%x\n", entry, p->RIOConnectTable[entry].Flags ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.SysPort = 0x%x\n", entry, p->RIOConnectTable[entry].SysPort ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[0].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[0].Unit ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[0].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[0].Link ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[1].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[1].Unit ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[1].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[1].Link ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[2].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[2].Unit ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[2].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[2].Link ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[3].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[3].Unit ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[4].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[3].Link ) ); + rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Name = %s\n", entry, p->RIOConnectTable[entry].Name ) ); + } + rio_dprint(RIO_DEBUG_CTRL, ("*****\nEND MAP ENTRIES\n") ); + } +*********************************** +*/ + return RIONewTable(p); + + case RIO_GET_BINDINGS: + /* + ** Send bindings table, containing unique numbers of RTAs owned + ** by this system to user space + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS\n"); + + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_to_user(argp, p->RIOBindTab, (sizeof(ulong) * MAX_RTA_BINDINGS))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS copy failed\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + + case RIO_PUT_BINDINGS: + /* + ** Receive a bindings table, containing unique numbers of RTAs owned + ** by this system + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS\n"); + + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&p->RIOBindTab[0], argp, (sizeof(ulong) * MAX_RTA_BINDINGS))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS copy failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + return 0; + + case RIO_BIND_RTA: + { + int EmptySlot = -1; + /* + ** Bind this RTA to host, so that it will be booted by + ** host in 'boot owned RTAs' mode. + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_BIND_RTA\n"); + + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_BIND_RTA !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + for (Entry = 0; Entry < MAX_RTA_BINDINGS; Entry++) { + if ((EmptySlot == -1) && (p->RIOBindTab[Entry] == 0L)) + EmptySlot = Entry; + else if (p->RIOBindTab[Entry] == arg) { + /* + ** Already exists - delete + */ + p->RIOBindTab[Entry] = 0L; + rio_dprintk(RIO_DEBUG_CTRL, "Removing Rta %ld from p->RIOBindTab\n", arg); + return 0; + } + } + /* + ** Dosen't exist - add + */ + if (EmptySlot != -1) { + p->RIOBindTab[EmptySlot] = arg; + rio_dprintk(RIO_DEBUG_CTRL, "Adding Rta %lx to p->RIOBindTab\n", arg); + } else { + rio_dprintk(RIO_DEBUG_CTRL, "p->RIOBindTab full! - Rta %lx not added\n", arg); + return -ENOMEM; + } + return 0; + } + + case RIO_RESUME: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME\n"); + port = arg; + if ((port < 0) || (port > 511)) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Bad port number %d\n", port); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + PortP = p->RIOPortp[port]; + if (!PortP->Mapped) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d not mapped\n", port); + p->RIOError.Error = PORT_NOT_MAPPED_INTO_SYSTEM; + return -EINVAL; + } + if (!(PortP->State & (RIO_LOPEN | RIO_MOPEN))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d not open\n", port); + return -EINVAL; + } + + rio_spin_lock_irqsave(&PortP->portSem, flags); + if (RIOPreemptiveCmd(p, (p->RIOPortp[port]), RIOC_RESUME) == + RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME failed\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return -EBUSY; + } else { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d resumed\n", port); + PortP->State |= RIO_BUSY; + } + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_ASSIGN_RTA: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_ASSIGN_RTA\n"); + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_ASSIGN_RTA !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) { + rio_dprintk(RIO_DEBUG_CTRL, "Copy from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + return RIOAssignRta(p, &MapEnt); + + case RIO_CHANGE_NAME: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_CHANGE_NAME\n"); + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_CHANGE_NAME !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) { + rio_dprintk(RIO_DEBUG_CTRL, "Copy from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + return RIOChangeName(p, &MapEnt); + + case RIO_DELETE_RTA: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DELETE_RTA\n"); + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DELETE_RTA !Root\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) { + rio_dprintk(RIO_DEBUG_CTRL, "Copy from data space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + return RIODeleteRta(p, &MapEnt); + + case RIO_QUICK_CHECK: + if (copy_to_user(argp, &p->RIORtaDisCons, sizeof(unsigned int))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + + case RIO_LAST_ERROR: + if (copy_to_user(argp, &p->RIOError, sizeof(struct Error))) + return -EFAULT; + return 0; + + case RIO_GET_LOG: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_LOG\n"); + return -EINVAL; + + case RIO_GET_MODTYPE: + if (copy_from_user(&port, argp, sizeof(unsigned int))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + rio_dprintk(RIO_DEBUG_CTRL, "Get module type for port %d\n", port); + if (port < 0 || port > 511) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_MODTYPE: Bad port number %d\n", port); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + PortP = (p->RIOPortp[port]); + if (!PortP->Mapped) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_MODTYPE: Port %d not mapped\n", port); + p->RIOError.Error = PORT_NOT_MAPPED_INTO_SYSTEM; + return -EINVAL; + } + /* + ** Return module type of port + */ + port = PortP->HostP->UnixRups[PortP->RupNum].ModTypes; + if (copy_to_user(argp, &port, sizeof(unsigned int))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return (0); + case RIO_BLOCK_OPENS: + rio_dprintk(RIO_DEBUG_CTRL, "Opens block until booted\n"); + for (Entry = 0; Entry < RIO_PORTS; Entry++) { + rio_spin_lock_irqsave(&PortP->portSem, flags); + p->RIOPortp[Entry]->WaitUntilBooted = 1; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + return 0; + + case RIO_SETUP_PORTS: + rio_dprintk(RIO_DEBUG_CTRL, "Setup ports\n"); + if (copy_from_user(&PortSetup, argp, sizeof(PortSetup))) { + p->RIOError.Error = COPYIN_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "EFAULT"); + return -EFAULT; + } + if (PortSetup.From > PortSetup.To || PortSetup.To >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + rio_dprintk(RIO_DEBUG_CTRL, "ENXIO"); + return -ENXIO; + } + if (PortSetup.XpCps > p->RIOConf.MaxXpCps || PortSetup.XpCps < p->RIOConf.MinXpCps) { + p->RIOError.Error = XPRINT_CPS_OUT_OF_RANGE; + rio_dprintk(RIO_DEBUG_CTRL, "EINVAL"); + return -EINVAL; + } + if (!p->RIOPortp) { + printk(KERN_ERR "rio: No p->RIOPortp array!\n"); + rio_dprintk(RIO_DEBUG_CTRL, "No p->RIOPortp array!\n"); + return -EIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "entering loop (%d %d)!\n", PortSetup.From, PortSetup.To); + for (loop = PortSetup.From; loop <= PortSetup.To; loop++) { + rio_dprintk(RIO_DEBUG_CTRL, "in loop (%d)!\n", loop); + } + rio_dprintk(RIO_DEBUG_CTRL, "after loop (%d)!\n", loop); + rio_dprintk(RIO_DEBUG_CTRL, "Retval:%x\n", retval); + return retval; + + case RIO_GET_PORT_SETUP: + rio_dprintk(RIO_DEBUG_CTRL, "Get port setup\n"); + if (copy_from_user(&PortSetup, argp, sizeof(PortSetup))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (PortSetup.From >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + + port = PortSetup.To = PortSetup.From; + PortSetup.IxAny = (p->RIOPortp[port]->Config & RIO_IXANY) ? 1 : 0; + PortSetup.IxOn = (p->RIOPortp[port]->Config & RIO_IXON) ? 1 : 0; + PortSetup.Drain = (p->RIOPortp[port]->Config & RIO_WAITDRAIN) ? 1 : 0; + PortSetup.Store = p->RIOPortp[port]->Store; + PortSetup.Lock = p->RIOPortp[port]->Lock; + PortSetup.XpCps = p->RIOPortp[port]->Xprint.XpCps; + memcpy(PortSetup.XpOn, p->RIOPortp[port]->Xprint.XpOn, MAX_XP_CTRL_LEN); + memcpy(PortSetup.XpOff, p->RIOPortp[port]->Xprint.XpOff, MAX_XP_CTRL_LEN); + PortSetup.XpOn[MAX_XP_CTRL_LEN - 1] = '\0'; + PortSetup.XpOff[MAX_XP_CTRL_LEN - 1] = '\0'; + + if (copy_to_user(argp, &PortSetup, sizeof(PortSetup))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_GET_PORT_PARAMS: + rio_dprintk(RIO_DEBUG_CTRL, "Get port params\n"); + if (copy_from_user(&PortParams, argp, sizeof(struct PortParams))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (PortParams.Port >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[PortParams.Port]); + PortParams.Config = PortP->Config; + PortParams.State = PortP->State; + rio_dprintk(RIO_DEBUG_CTRL, "Port %d\n", PortParams.Port); + + if (copy_to_user(argp, &PortParams, sizeof(struct PortParams))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_GET_PORT_TTY: + rio_dprintk(RIO_DEBUG_CTRL, "Get port tty\n"); + if (copy_from_user(&PortTty, argp, sizeof(struct PortTty))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (PortTty.port >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + + rio_dprintk(RIO_DEBUG_CTRL, "Port %d\n", PortTty.port); + PortP = (p->RIOPortp[PortTty.port]); + if (copy_to_user(argp, &PortTty, sizeof(struct PortTty))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_SET_PORT_TTY: + if (copy_from_user(&PortTty, argp, sizeof(struct PortTty))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + rio_dprintk(RIO_DEBUG_CTRL, "Set port %d tty\n", PortTty.port); + if (PortTty.port >= (ushort) RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[PortTty.port]); + RIOParam(PortP, RIOC_CONFIG, PortP->State & RIO_MODEM, + OK_TO_SLEEP); + return retval; + + case RIO_SET_PORT_PARAMS: + rio_dprintk(RIO_DEBUG_CTRL, "Set port params\n"); + if (copy_from_user(&PortParams, argp, sizeof(PortParams))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (PortParams.Port >= (ushort) RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[PortParams.Port]); + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->Config = PortParams.Config; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_GET_PORT_STATS: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_PORT_STATS\n"); + if (copy_from_user(&portStats, argp, sizeof(struct portStats))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (portStats.port < 0 || portStats.port >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[portStats.port]); + portStats.gather = PortP->statsGather; + portStats.txchars = PortP->txchars; + portStats.rxchars = PortP->rxchars; + portStats.opens = PortP->opens; + portStats.closes = PortP->closes; + portStats.ioctls = PortP->ioctls; + if (copy_to_user(argp, &portStats, sizeof(struct portStats))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_RESET_PORT_STATS: + port = arg; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESET_PORT_STATS\n"); + if (port >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[port]); + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->txchars = 0; + PortP->rxchars = 0; + PortP->opens = 0; + PortP->closes = 0; + PortP->ioctls = 0; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_GATHER_PORT_STATS: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GATHER_PORT_STATS\n"); + if (copy_from_user(&portStats, argp, sizeof(struct portStats))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (portStats.port < 0 || portStats.port >= RIO_PORTS) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + PortP = (p->RIOPortp[portStats.port]); + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->statsGather = portStats.gather; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_READ_CONFIG: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_CONFIG\n"); + if (copy_to_user(argp, &p->RIOConf, sizeof(struct Conf))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_SET_CONFIG: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_CONFIG\n"); + if (!su) { + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&p->RIOConf, argp, sizeof(struct Conf))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + /* + ** move a few value around + */ + for (Host = 0; Host < p->RIONumHosts; Host++) + if ((p->RIOHosts[Host].Flags & RUN_STATE) == RC_RUNNING) + writew(p->RIOConf.Timer, &p->RIOHosts[Host].ParmMapP->timer); + return retval; + + case RIO_START_POLLER: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_START_POLLER\n"); + return -EINVAL; + + case RIO_STOP_POLLER: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_STOP_POLLER\n"); + if (!su) { + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + p->RIOPolling = NOT_POLLING; + return retval; + + case RIO_SETDEBUG: + case RIO_GETDEBUG: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SETDEBUG/RIO_GETDEBUG\n"); + if (copy_from_user(&DebugCtrl, argp, sizeof(DebugCtrl))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (DebugCtrl.SysPort == NO_PORT) { + if (cmd == RIO_SETDEBUG) { + if (!su) { + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + p->rio_debug = DebugCtrl.Debug; + p->RIODebugWait = DebugCtrl.Wait; + rio_dprintk(RIO_DEBUG_CTRL, "Set global debug to 0x%x set wait to 0x%x\n", p->rio_debug, p->RIODebugWait); + } else { + rio_dprintk(RIO_DEBUG_CTRL, "Get global debug 0x%x wait 0x%x\n", p->rio_debug, p->RIODebugWait); + DebugCtrl.Debug = p->rio_debug; + DebugCtrl.Wait = p->RIODebugWait; + if (copy_to_user(argp, &DebugCtrl, sizeof(DebugCtrl))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET/GET DEBUG: bad port number %d\n", DebugCtrl.SysPort); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + } + } else if (DebugCtrl.SysPort >= RIO_PORTS && DebugCtrl.SysPort != NO_PORT) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET/GET DEBUG: bad port number %d\n", DebugCtrl.SysPort); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } else if (cmd == RIO_SETDEBUG) { + if (!su) { + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + p->RIOPortp[DebugCtrl.SysPort]->Debug = DebugCtrl.Debug; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SETDEBUG 0x%x\n", p->RIOPortp[DebugCtrl.SysPort]->Debug); + } else { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GETDEBUG 0x%x\n", p->RIOPortp[DebugCtrl.SysPort]->Debug); + DebugCtrl.Debug = p->RIOPortp[DebugCtrl.SysPort]->Debug; + if (copy_to_user(argp, &DebugCtrl, sizeof(DebugCtrl))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_GETDEBUG: Bad copy to user space\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + } + return retval; + + case RIO_VERSID: + /* + ** Enquire about the release and version. + ** We return MAX_VERSION_LEN bytes, being a + ** textual null terminated string. + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_VERSID\n"); + if (copy_to_user(argp, RIOVersid(), sizeof(struct rioVersion))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_VERSID: Bad copy to user space (host=%d)\n", Host); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_NUM_HOSTS: + /* + ** Enquire as to the number of hosts located + ** at init time. + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_NUM_HOSTS\n"); + if (copy_to_user(argp, &p->RIONumHosts, sizeof(p->RIONumHosts))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_NUM_HOSTS: Bad copy to user space\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + case RIO_HOST_FOAD: + /* + ** Kill host. This may not be in the final version... + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_FOAD %ld\n", arg); + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_FOAD: Not super user\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + p->RIOHalted = 1; + p->RIOSystemUp = 0; + + for (Host = 0; Host < p->RIONumHosts; Host++) { + (void) RIOBoardTest(p->RIOHosts[Host].PaddrP, p->RIOHosts[Host].Caddr, p->RIOHosts[Host].Type, p->RIOHosts[Host].Slot); + memset(&p->RIOHosts[Host].Flags, 0, ((char *) &p->RIOHosts[Host].____end_marker____) - ((char *) &p->RIOHosts[Host].Flags)); + p->RIOHosts[Host].Flags = RC_WAITING; + } + RIOFoadWakeup(p); + p->RIONumBootPkts = 0; + p->RIOBooting = 0; + printk("HEEEEELP!\n"); + + for (loop = 0; loop < RIO_PORTS; loop++) { + spin_lock_init(&p->RIOPortp[loop]->portSem); + p->RIOPortp[loop]->InUse = NOT_INUSE; + } + + p->RIOSystemUp = 0; + return retval; + + case RIO_DOWNLOAD: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD\n"); + if (!su) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Not super user\n"); + p->RIOError.Error = NOT_SUPER_USER; + return -EPERM; + } + if (copy_from_user(&DownLoad, argp, sizeof(DownLoad))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + rio_dprintk(RIO_DEBUG_CTRL, "Copied in download code for product code 0x%x\n", DownLoad.ProductCode); + + /* + ** It is important that the product code is an unsigned object! + */ + if (DownLoad.ProductCode >= MAX_PRODUCT) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Bad product code %d passed\n", DownLoad.ProductCode); + p->RIOError.Error = NO_SUCH_PRODUCT; + return -ENXIO; + } + /* + ** do something! + */ + retval = (*(RIOBootTable[DownLoad.ProductCode])) (p, &DownLoad); + /* <-- Panic */ + p->RIOHalted = 0; + /* + ** and go back, content with a job well completed. + */ + return retval; + + case RIO_PARMS: + { + unsigned int host; + + if (copy_from_user(&host, argp, sizeof(host))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + /* + ** Fetch the parmmap + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PARMS\n"); + if (copy_from_io(argp, p->RIOHosts[host].ParmMapP, sizeof(PARM_MAP))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_PARMS: Copy out to user space failed\n"); + return -EFAULT; + } + } + return retval; + + case RIO_HOST_REQ: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ\n"); + if (copy_from_user(&HostReq, argp, sizeof(HostReq))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (HostReq.HostNum >= p->RIONumHosts) { + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Illegal host number %d\n", HostReq.HostNum); + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "Request for host %d\n", HostReq.HostNum); + + if (copy_to_user(HostReq.HostP, &p->RIOHosts[HostReq.HostNum], sizeof(struct Host))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Bad copy to user space\n"); + return -EFAULT; + } + return retval; + + case RIO_HOST_DPRAM: + rio_dprintk(RIO_DEBUG_CTRL, "Request for DPRAM\n"); + if (copy_from_user(&HostDpRam, argp, sizeof(HostDpRam))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (HostDpRam.HostNum >= p->RIONumHosts) { + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Illegal host number %d\n", HostDpRam.HostNum); + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "Request for host %d\n", HostDpRam.HostNum); + + if (p->RIOHosts[HostDpRam.HostNum].Type == RIO_PCI) { + int off; + /* It's hardware like this that really gets on my tits. */ + static unsigned char copy[sizeof(struct DpRam)]; + for (off = 0; off < sizeof(struct DpRam); off++) + copy[off] = readb(p->RIOHosts[HostDpRam.HostNum].Caddr + off); + if (copy_to_user(HostDpRam.DpRamP, copy, sizeof(struct DpRam))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Bad copy to user space\n"); + return -EFAULT; + } + } else if (copy_from_io(HostDpRam.DpRamP, p->RIOHosts[HostDpRam.HostNum].Caddr, sizeof(struct DpRam))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Bad copy to user space\n"); + return -EFAULT; + } + return retval; + + case RIO_SET_BUSY: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_BUSY\n"); + if (arg > 511) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_BUSY: Bad port number %ld\n", arg); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + p->RIOPortp[arg]->State |= RIO_BUSY; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_HOST_PORT: + /* + ** The daemon want port information + ** (probably for debug reasons) + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT\n"); + if (copy_from_user(&PortReq, argp, sizeof(PortReq))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + + if (PortReq.SysPort >= RIO_PORTS) { /* SysPort is unsigned */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Illegal port number %d\n", PortReq.SysPort); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "Request for port %d\n", PortReq.SysPort); + if (copy_to_user(PortReq.PortP, p->RIOPortp[PortReq.SysPort], sizeof(struct Port))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Bad copy to user space\n"); + return -EFAULT; + } + return retval; + + case RIO_HOST_RUP: + /* + ** The daemon want rup information + ** (probably for debug reasons) + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP\n"); + if (copy_from_user(&RupReq, argp, sizeof(RupReq))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Copy in from user space failed\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (RupReq.HostNum >= p->RIONumHosts) { /* host is unsigned */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Illegal host number %d\n", RupReq.HostNum); + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + if (RupReq.RupNum >= MAX_RUP + LINKS_PER_UNIT) { /* eek! */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Illegal rup number %d\n", RupReq.RupNum); + p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + HostP = &p->RIOHosts[RupReq.HostNum]; + + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Host %d not running\n", RupReq.HostNum); + p->RIOError.Error = HOST_NOT_RUNNING; + return -EIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "Request for rup %d from host %d\n", RupReq.RupNum, RupReq.HostNum); + + if (copy_from_io(RupReq.RupP, HostP->UnixRups[RupReq.RupNum].RupP, sizeof(struct RUP))) { + p->RIOError.Error = COPYOUT_FAILED; + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Bad copy to user space\n"); + return -EFAULT; + } + return retval; + + case RIO_HOST_LPB: + /* + ** The daemon want lpb information + ** (probably for debug reasons) + */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB\n"); + if (copy_from_user(&LpbReq, argp, sizeof(LpbReq))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Bad copy from user space\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (LpbReq.Host >= p->RIONumHosts) { /* host is unsigned */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Illegal host number %d\n", LpbReq.Host); + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + if (LpbReq.Link >= LINKS_PER_UNIT) { /* eek! */ + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Illegal link number %d\n", LpbReq.Link); + p->RIOError.Error = LINK_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + HostP = &p->RIOHosts[LpbReq.Host]; + + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Host %d not running\n", LpbReq.Host); + p->RIOError.Error = HOST_NOT_RUNNING; + return -EIO; + } + rio_dprintk(RIO_DEBUG_CTRL, "Request for lpb %d from host %d\n", LpbReq.Link, LpbReq.Host); + + if (copy_from_io(LpbReq.LpbP, &HostP->LinkStrP[LpbReq.Link], sizeof(struct LPB))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Bad copy to user space\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return retval; + + /* + ** Here 3 IOCTL's that allow us to change the way in which + ** rio logs errors. send them just to syslog or send them + ** to both syslog and console or send them to just the console. + ** + ** See RioStrBuf() in util.c for the other half. + */ + case RIO_SYSLOG_ONLY: + p->RIOPrintLogState = PRINT_TO_LOG; /* Just syslog */ + return 0; + + case RIO_SYSLOG_CONS: + p->RIOPrintLogState = PRINT_TO_LOG_CONS; /* syslog and console */ + return 0; + + case RIO_CONS_ONLY: + p->RIOPrintLogState = PRINT_TO_CONS; /* Just console */ + return 0; + + case RIO_SIGNALS_ON: + if (p->RIOSignalProcess) { + p->RIOError.Error = SIGNALS_ALREADY_SET; + return -EBUSY; + } + /* FIXME: PID tracking */ + p->RIOSignalProcess = current->pid; + p->RIOPrintDisabled = DONT_PRINT; + return retval; + + case RIO_SIGNALS_OFF: + if (p->RIOSignalProcess != current->pid) { + p->RIOError.Error = NOT_RECEIVING_PROCESS; + return -EPERM; + } + rio_dprintk(RIO_DEBUG_CTRL, "Clear signal process to zero\n"); + p->RIOSignalProcess = 0; + return retval; + + case RIO_SET_BYTE_MODE: + for (Host = 0; Host < p->RIONumHosts; Host++) + if (p->RIOHosts[Host].Type == RIO_AT) + p->RIOHosts[Host].Mode &= ~WORD_OPERATION; + return retval; + + case RIO_SET_WORD_MODE: + for (Host = 0; Host < p->RIONumHosts; Host++) + if (p->RIOHosts[Host].Type == RIO_AT) + p->RIOHosts[Host].Mode |= WORD_OPERATION; + return retval; + + case RIO_SET_FAST_BUS: + for (Host = 0; Host < p->RIONumHosts; Host++) + if (p->RIOHosts[Host].Type == RIO_AT) + p->RIOHosts[Host].Mode |= FAST_AT_BUS; + return retval; + + case RIO_SET_SLOW_BUS: + for (Host = 0; Host < p->RIONumHosts; Host++) + if (p->RIOHosts[Host].Type == RIO_AT) + p->RIOHosts[Host].Mode &= ~FAST_AT_BUS; + return retval; + + case RIO_MAP_B50_TO_50: + case RIO_MAP_B50_TO_57600: + case RIO_MAP_B110_TO_110: + case RIO_MAP_B110_TO_115200: + rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping\n"); + port = arg; + if (port < 0 || port > 511) { + rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping: Bad port number %d\n", port); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + switch (cmd) { + case RIO_MAP_B50_TO_50: + p->RIOPortp[port]->Config |= RIO_MAP_50_TO_50; + break; + case RIO_MAP_B50_TO_57600: + p->RIOPortp[port]->Config &= ~RIO_MAP_50_TO_50; + break; + case RIO_MAP_B110_TO_110: + p->RIOPortp[port]->Config |= RIO_MAP_110_TO_110; + break; + case RIO_MAP_B110_TO_115200: + p->RIOPortp[port]->Config &= ~RIO_MAP_110_TO_110; + break; + } + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_STREAM_INFO: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_STREAM_INFO\n"); + return -EINVAL; + + case RIO_SEND_PACKET: + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SEND_PACKET\n"); + if (copy_from_user(&SendPack, argp, sizeof(SendPack))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_SEND_PACKET: Bad copy from user space\n"); + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + if (SendPack.PortNum >= 128) { + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -ENXIO; + } + + PortP = p->RIOPortp[SendPack.PortNum]; + rio_spin_lock_irqsave(&PortP->portSem, flags); + + if (!can_add_transmit(&PacketP, PortP)) { + p->RIOError.Error = UNIT_IS_IN_USE; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return -ENOSPC; + } + + for (loop = 0; loop < (ushort) (SendPack.Len & 127); loop++) + writeb(SendPack.Data[loop], &PacketP->data[loop]); + + writeb(SendPack.Len, &PacketP->len); + + add_transmit(PortP); + /* + ** Count characters transmitted for port statistics reporting + */ + if (PortP->statsGather) + PortP->txchars += (SendPack.Len & 127); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + + case RIO_NO_MESG: + if (su) + p->RIONoMessage = 1; + return su ? 0 : -EPERM; + + case RIO_MESG: + if (su) + p->RIONoMessage = 0; + return su ? 0 : -EPERM; + + case RIO_WHAT_MESG: + if (copy_to_user(argp, &p->RIONoMessage, sizeof(p->RIONoMessage))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_WHAT_MESG: Bad copy to user space\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + + case RIO_MEM_DUMP: + if (copy_from_user(&SubCmd, argp, sizeof(struct SubCmdStruct))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP host %d rup %d addr %x\n", SubCmd.Host, SubCmd.Rup, SubCmd.Addr); + + if (SubCmd.Rup >= MAX_RUP + LINKS_PER_UNIT) { + p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + if (SubCmd.Host >= p->RIONumHosts) { + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + port = p->RIOHosts[SubCmd.Host].UnixRups[SubCmd.Rup].BaseSysPort; + + PortP = p->RIOPortp[port]; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + if (RIOPreemptiveCmd(p, PortP, RIOC_MEMDUMP) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP failed\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return -EBUSY; + } else + PortP->State |= RIO_BUSY; + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (copy_to_user(argp, p->RIOMemDump, MEMDUMP_SIZE)) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP copy failed\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + + case RIO_TICK: + if (arg >= p->RIONumHosts) + return -EINVAL; + rio_dprintk(RIO_DEBUG_CTRL, "Set interrupt for host %ld\n", arg); + writeb(0xFF, &p->RIOHosts[arg].SetInt); + return 0; + + case RIO_TOCK: + if (arg >= p->RIONumHosts) + return -EINVAL; + rio_dprintk(RIO_DEBUG_CTRL, "Clear interrupt for host %ld\n", arg); + writeb(0xFF, &p->RIOHosts[arg].ResetInt); + return 0; + + case RIO_READ_CHECK: + /* Check reads for pkts with data[0] the same */ + p->RIOReadCheck = !p->RIOReadCheck; + if (copy_to_user(argp, &p->RIOReadCheck, sizeof(unsigned int))) { + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + + case RIO_READ_REGISTER: + if (copy_from_user(&SubCmd, argp, sizeof(struct SubCmdStruct))) { + p->RIOError.Error = COPYIN_FAILED; + return -EFAULT; + } + rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER host %d rup %d port %d reg %x\n", SubCmd.Host, SubCmd.Rup, SubCmd.Port, SubCmd.Addr); + + if (SubCmd.Port > 511) { + rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping: Bad port number %d\n", SubCmd.Port); + p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + if (SubCmd.Rup >= MAX_RUP + LINKS_PER_UNIT) { + p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + if (SubCmd.Host >= p->RIONumHosts) { + p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + port = p->RIOHosts[SubCmd.Host].UnixRups[SubCmd.Rup].BaseSysPort + SubCmd.Port; + PortP = p->RIOPortp[port]; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + if (RIOPreemptiveCmd(p, PortP, RIOC_READ_REGISTER) == + RIO_FAIL) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER failed\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return -EBUSY; + } else + PortP->State |= RIO_BUSY; + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (copy_to_user(argp, &p->CdRegister, sizeof(unsigned int))) { + rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER copy failed\n"); + p->RIOError.Error = COPYOUT_FAILED; + return -EFAULT; + } + return 0; + /* + ** rio_make_dev: given port number (0-511) ORed with port type + ** (RIO_DEV_DIRECT, RIO_DEV_MODEM, RIO_DEV_XPRINT) return dev_t + ** value to pass to mknod to create the correct device node. + */ + case RIO_MAKE_DEV: + { + unsigned int port = arg & RIO_MODEM_MASK; + unsigned int ret; + + switch (arg & RIO_DEV_MASK) { + case RIO_DEV_DIRECT: + ret = drv_makedev(MAJOR(dev), port); + rio_dprintk(RIO_DEBUG_CTRL, "Makedev direct 0x%x is 0x%x\n", port, ret); + return ret; + case RIO_DEV_MODEM: + ret = drv_makedev(MAJOR(dev), (port | RIO_MODEM_BIT)); + rio_dprintk(RIO_DEBUG_CTRL, "Makedev modem 0x%x is 0x%x\n", port, ret); + return ret; + case RIO_DEV_XPRINT: + ret = drv_makedev(MAJOR(dev), port); + rio_dprintk(RIO_DEBUG_CTRL, "Makedev printer 0x%x is 0x%x\n", port, ret); + return ret; + } + rio_dprintk(RIO_DEBUG_CTRL, "MAKE Device is called\n"); + return -EINVAL; + } + /* + ** rio_minor: given a dev_t from a stat() call, return + ** the port number (0-511) ORed with the port type + ** ( RIO_DEV_DIRECT, RIO_DEV_MODEM, RIO_DEV_XPRINT ) + */ + case RIO_MINOR: + { + dev_t dv; + int mino; + unsigned long ret; + + dv = (dev_t) (arg); + mino = RIO_UNMODEM(dv); + + if (RIO_ISMODEM(dv)) { + rio_dprintk(RIO_DEBUG_CTRL, "Minor for device 0x%x: modem %d\n", dv, mino); + ret = mino | RIO_DEV_MODEM; + } else { + rio_dprintk(RIO_DEBUG_CTRL, "Minor for device 0x%x: direct %d\n", dv, mino); + ret = mino | RIO_DEV_DIRECT; + } + return ret; + } + } + rio_dprintk(RIO_DEBUG_CTRL, "INVALID DAEMON IOCTL 0x%x\n", cmd); + p->RIOError.Error = IOCTL_COMMAND_UNKNOWN; + + func_exit(); + return -EINVAL; +} + +/* +** Pre-emptive commands go on RUPs and are only one byte long. +*/ +int RIOPreemptiveCmd(struct rio_info *p, struct Port *PortP, u8 Cmd) +{ + struct CmdBlk *CmdBlkP; + struct PktCmd_M *PktCmdP; + int Ret; + ushort rup; + int port; + + if (PortP->State & RIO_DELETED) { + rio_dprintk(RIO_DEBUG_CTRL, "Preemptive command to deleted RTA ignored\n"); + return RIO_FAIL; + } + + if ((PortP->InUse == (typeof(PortP->InUse))-1) || + !(CmdBlkP = RIOGetCmdBlk())) { + rio_dprintk(RIO_DEBUG_CTRL, "Cannot allocate command block " + "for command %d on port %d\n", Cmd, PortP->PortNum); + return RIO_FAIL; + } + + rio_dprintk(RIO_DEBUG_CTRL, "Command blk %p - InUse now %d\n", + CmdBlkP, PortP->InUse); + + PktCmdP = (struct PktCmd_M *)&CmdBlkP->Packet.data[0]; + + CmdBlkP->Packet.src_unit = 0; + if (PortP->SecondBlock) + rup = PortP->ID2; + else + rup = PortP->RupNum; + CmdBlkP->Packet.dest_unit = rup; + CmdBlkP->Packet.src_port = COMMAND_RUP; + CmdBlkP->Packet.dest_port = COMMAND_RUP; + CmdBlkP->Packet.len = PKT_CMD_BIT | 2; + CmdBlkP->PostFuncP = RIOUnUse; + CmdBlkP->PostArg = (unsigned long) PortP; + PktCmdP->Command = Cmd; + port = PortP->HostPort % (ushort) PORTS_PER_RTA; + /* + ** Index ports 8-15 for 2nd block of 16 port RTA. + */ + if (PortP->SecondBlock) + port += (ushort) PORTS_PER_RTA; + PktCmdP->PhbNum = port; + + switch (Cmd) { + case RIOC_MEMDUMP: + rio_dprintk(RIO_DEBUG_CTRL, "Queue MEMDUMP command blk %p " + "(addr 0x%x)\n", CmdBlkP, (int) SubCmd.Addr); + PktCmdP->SubCommand = RIOC_MEMDUMP; + PktCmdP->SubAddr = SubCmd.Addr; + break; + case RIOC_FCLOSE: + rio_dprintk(RIO_DEBUG_CTRL, "Queue FCLOSE command blk %p\n", + CmdBlkP); + break; + case RIOC_READ_REGISTER: + rio_dprintk(RIO_DEBUG_CTRL, "Queue READ_REGISTER (0x%x) " + "command blk %p\n", (int) SubCmd.Addr, CmdBlkP); + PktCmdP->SubCommand = RIOC_READ_REGISTER; + PktCmdP->SubAddr = SubCmd.Addr; + break; + case RIOC_RESUME: + rio_dprintk(RIO_DEBUG_CTRL, "Queue RESUME command blk %p\n", + CmdBlkP); + break; + case RIOC_RFLUSH: + rio_dprintk(RIO_DEBUG_CTRL, "Queue RFLUSH command blk %p\n", + CmdBlkP); + CmdBlkP->PostFuncP = RIORFlushEnable; + break; + case RIOC_SUSPEND: + rio_dprintk(RIO_DEBUG_CTRL, "Queue SUSPEND command blk %p\n", + CmdBlkP); + break; + + case RIOC_MGET: + rio_dprintk(RIO_DEBUG_CTRL, "Queue MGET command blk %p\n", + CmdBlkP); + break; + + case RIOC_MSET: + case RIOC_MBIC: + case RIOC_MBIS: + CmdBlkP->Packet.data[4] = (char) PortP->ModemLines; + rio_dprintk(RIO_DEBUG_CTRL, "Queue MSET/MBIC/MBIS command " + "blk %p\n", CmdBlkP); + break; + + case RIOC_WFLUSH: + /* + ** If we have queued up the maximum number of Write flushes + ** allowed then we should not bother sending any more to the + ** RTA. + */ + if (PortP->WflushFlag == (typeof(PortP->WflushFlag))-1) { + rio_dprintk(RIO_DEBUG_CTRL, "Trashed WFLUSH, " + "WflushFlag about to wrap!"); + RIOFreeCmdBlk(CmdBlkP); + return (RIO_FAIL); + } else { + rio_dprintk(RIO_DEBUG_CTRL, "Queue WFLUSH command " + "blk %p\n", CmdBlkP); + CmdBlkP->PostFuncP = RIOWFlushMark; + } + break; + } + + PortP->InUse++; + + Ret = RIOQueueCmdBlk(PortP->HostP, rup, CmdBlkP); + + return Ret; +} diff --git a/drivers/staging/generic_serial/rio/riodrvr.h b/drivers/staging/generic_serial/rio/riodrvr.h new file mode 100644 index 0000000..0907e71 --- /dev/null +++ b/drivers/staging/generic_serial/rio/riodrvr.h @@ -0,0 +1,138 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riodrvr.h +** SID : 1.3 +** Last Modified : 11/6/98 09:22:46 +** Retrieved : 11/6/98 09:22:46 +** +** ident @(#)riodrvr.h 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __riodrvr_h +#define __riodrvr_h + +#include <asm/param.h> /* for HZ */ + +#define MEMDUMP_SIZE 32 +#define MOD_DISABLE (RIO_NOREAD|RIO_NOWRITE|RIO_NOXPRINT) + + +struct rio_info { + int mode; /* Intr or polled, word/byte */ + spinlock_t RIOIntrSem; /* Interrupt thread sem */ + int current_chan; /* current channel */ + int RIOFailed; /* Not initialised ? */ + int RIOInstallAttempts; /* no. of rio-install() calls */ + int RIOLastPCISearch; /* status of last search */ + int RIONumHosts; /* Number of RIO Hosts */ + struct Host *RIOHosts; /* RIO Host values */ + struct Port **RIOPortp; /* RIO port values */ +/* +** 02.03.1999 ARG - ESIL 0820 fix +** We no longer use RIOBootMode +** + int RIOBootMode; * RIO boot mode * +** +*/ + int RIOPrintDisabled; /* RIO printing disabled ? */ + int RIOPrintLogState; /* RIO printing state ? */ + int RIOPolling; /* Polling ? */ +/* +** 09.12.1998 ARG - ESIL 0776 part fix +** The 'RIO_QUICK_CHECK' ioctl was using RIOHalted. +** The fix for this ESIL introduces another member (RIORtaDisCons) here to be +** updated in RIOConCon() - to keep track of RTA connections/disconnections. +** 'RIO_QUICK_CHECK' now returns the value of RIORtaDisCons. +*/ + int RIOHalted; /* halted ? */ + int RIORtaDisCons; /* RTA connections/disconnections */ + unsigned int RIOReadCheck; /* Rio read check */ + unsigned int RIONoMessage; /* To display message or not */ + unsigned int RIONumBootPkts; /* how many packets for an RTA */ + unsigned int RIOBootCount; /* size of RTA code */ + unsigned int RIOBooting; /* count of outstanding boots */ + unsigned int RIOSystemUp; /* Booted ?? */ + unsigned int RIOCounting; /* for counting interrupts */ + unsigned int RIOIntCount; /* # of intr since last check */ + unsigned int RIOTxCount; /* number of xmit intrs */ + unsigned int RIORxCount; /* number of rx intrs */ + unsigned int RIORupCount; /* number of rup intrs */ + int RIXTimer; + int RIOBufferSize; /* Buffersize */ + int RIOBufferMask; /* Buffersize */ + + int RIOFirstMajor; /* First host card's major no */ + + unsigned int RIOLastPortsMapped; /* highest port number known */ + unsigned int RIOFirstPortsMapped; /* lowest port number known */ + + unsigned int RIOLastPortsBooted; /* highest port number running */ + unsigned int RIOFirstPortsBooted; /* lowest port number running */ + + unsigned int RIOLastPortsOpened; /* highest port number running */ + unsigned int RIOFirstPortsOpened; /* lowest port number running */ + + /* Flag to say that the topology information has been changed. */ + unsigned int RIOQuickCheck; + unsigned int CdRegister; /* ??? */ + int RIOSignalProcess; /* Signalling process */ + int rio_debug; /* To debug ... */ + int RIODebugWait; /* For what ??? */ + int tpri; /* Thread prio */ + int tid; /* Thread id */ + unsigned int _RIO_Polled; /* Counter for polling */ + unsigned int _RIO_Interrupted; /* Counter for interrupt */ + int intr_tid; /* iointset return value */ + int TxEnSem; /* TxEnable Semaphore */ + + + struct Error RIOError; /* to Identify what went wrong */ + struct Conf RIOConf; /* Configuration ??? */ + struct ttystatics channel[RIO_PORTS]; /* channel information */ + char RIOBootPackets[1 + (SIXTY_FOUR_K / RTA_BOOT_DATA_SIZE)] + [RTA_BOOT_DATA_SIZE]; + struct Map RIOConnectTable[TOTAL_MAP_ENTRIES]; + struct Map RIOSavedTable[TOTAL_MAP_ENTRIES]; + + /* RTA to host binding table for master/slave operation */ + unsigned long RIOBindTab[MAX_RTA_BINDINGS]; + /* RTA memory dump variable */ + unsigned char RIOMemDump[MEMDUMP_SIZE]; + struct ModuleInfo RIOModuleTypes[MAX_MODULE_TYPES]; + +}; + + +#ifdef linux +#define debug(x) printk x +#else +#define debug(x) kkprintf x +#endif + + + +#define RIO_RESET_INT 0x7d80 + +#endif /* __riodrvr.h */ diff --git a/drivers/staging/generic_serial/rio/rioinfo.h b/drivers/staging/generic_serial/rio/rioinfo.h new file mode 100644 index 0000000..42ff1e7 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioinfo.h @@ -0,0 +1,92 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioinfo.h +** SID : 1.2 +** Last Modified : 11/6/98 14:07:49 +** Retrieved : 11/6/98 14:07:50 +** +** ident @(#)rioinfo.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rioinfo_h +#define __rioinfo_h + +/* +** Host card data structure +*/ +struct RioHostInfo { + long location; /* RIO Card Base I/O address */ + long vector; /* RIO Card IRQ vector */ + int bus; /* ISA/EISA/MCA/PCI */ + int mode; /* pointer to host mode - INTERRUPT / POLLED */ + struct old_sgttyb + *Sg; /* pointer to default term characteristics */ +}; + + +/* Mode in rio device info */ +#define INTERRUPTED_MODE 0x01 /* Interrupt is generated */ +#define POLLED_MODE 0x02 /* No interrupt */ +#define AUTO_MODE 0x03 /* Auto mode */ + +#define WORD_ACCESS_MODE 0x10 /* Word Access Mode */ +#define BYTE_ACCESS_MODE 0x20 /* Byte Access Mode */ + + +/* Bus type that RIO supports */ +#define ISA_BUS 0x01 /* The card is ISA */ +#define EISA_BUS 0x02 /* The card is EISA */ +#define MCA_BUS 0x04 /* The card is MCA */ +#define PCI_BUS 0x08 /* The card is PCI */ + +/* +** 11.11.1998 ARG - ESIL ???? part fix +** Moved definition for 'CHAN' here from rioinfo.c (it is now +** called 'DEF_TERM_CHARACTERISTICS'). +*/ + +#define DEF_TERM_CHARACTERISTICS \ +{ \ + B19200, B19200, /* input and output speed */ \ + 'H' - '@', /* erase char */ \ + -1, /* 2nd erase char */ \ + 'U' - '@', /* kill char */ \ + ECHO | CRMOD, /* mode */ \ + 'C' - '@', /* interrupt character */ \ + '\\' - '@', /* quit char */ \ + 'Q' - '@', /* start char */ \ + 'S' - '@', /* stop char */ \ + 'D' - '@', /* EOF */ \ + -1, /* brk */ \ + (LCRTBS | LCRTERA | LCRTKIL | LCTLECH), /* local mode word */ \ + 'Z' - '@', /* process stop */ \ + 'Y' - '@', /* delayed stop */ \ + 'R' - '@', /* reprint line */ \ + 'O' - '@', /* flush output */ \ + 'W' - '@', /* word erase */ \ + 'V' - '@' /* literal next char */ \ +} + +#endif /* __rioinfo_h */ diff --git a/drivers/staging/generic_serial/rio/rioinit.c b/drivers/staging/generic_serial/rio/rioinit.c new file mode 100644 index 0000000..24a282b --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioinit.c @@ -0,0 +1,421 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioinit.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:43 +** Retrieved : 11/6/98 10:33:49 +** +** ident @(#)rioinit.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "rio_linux.h" + +int RIOPCIinit(struct rio_info *p, int Mode); + +static int RIOScrub(int, u8 __iomem *, int); + + +/** +** RIOAssignAT : +** +** Fill out the fields in the p->RIOHosts structure now we know we know +** we have a board present. +** +** bits < 0 indicates 8 bit operation requested, +** bits > 0 indicates 16 bit operation. +*/ + +int RIOAssignAT(struct rio_info *p, int Base, void __iomem *virtAddr, int mode) +{ + int bits; + struct DpRam __iomem *cardp = (struct DpRam __iomem *)virtAddr; + + if ((Base < ONE_MEG) || (mode & BYTE_ACCESS_MODE)) + bits = BYTE_OPERATION; + else + bits = WORD_OPERATION; + + /* + ** Board has passed its scrub test. Fill in all the + ** transient stuff. + */ + p->RIOHosts[p->RIONumHosts].Caddr = virtAddr; + p->RIOHosts[p->RIONumHosts].CardP = virtAddr; + + /* + ** Revision 01 AT host cards don't support WORD operations, + */ + if (readb(&cardp->DpRevision) == 01) + bits = BYTE_OPERATION; + + p->RIOHosts[p->RIONumHosts].Type = RIO_AT; + p->RIOHosts[p->RIONumHosts].Copy = rio_copy_to_card; + /* set this later */ + p->RIOHosts[p->RIONumHosts].Slot = -1; + p->RIOHosts[p->RIONumHosts].Mode = SLOW_LINKS | SLOW_AT_BUS | bits; + writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | p->RIOHosts[p->RIONumHosts].Mode | INTERRUPT_DISABLE , + &p->RIOHosts[p->RIONumHosts].Control); + writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt); + writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | p->RIOHosts[p->RIONumHosts].Mode | INTERRUPT_DISABLE, + &p->RIOHosts[p->RIONumHosts].Control); + writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt); + p->RIOHosts[p->RIONumHosts].UniqueNum = + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0])&0xFF)<<0)| + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1])&0xFF)<<8)| + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2])&0xFF)<<16)| + ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3])&0xFF)<<24); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Uniquenum 0x%x\n",p->RIOHosts[p->RIONumHosts].UniqueNum); + + p->RIONumHosts++; + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Tests Passed at 0x%x\n", Base); + return(1); +} + +static u8 val[] = { +#ifdef VERY_LONG_TEST + 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0xa5, 0xff, 0x5a, 0x00, 0xff, 0xc9, 0x36, +#endif + 0xff, 0x00, 0x00 }; + +#define TEST_END sizeof(val) + +/* +** RAM test a board. +** Nothing too complicated, just enough to check it out. +*/ +int RIOBoardTest(unsigned long paddr, void __iomem *caddr, unsigned char type, int slot) +{ + struct DpRam __iomem *DpRam = caddr; + void __iomem *ram[4]; + int size[4]; + int op, bank; + int nbanks; + + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Reset host type=%d, DpRam=%p, slot=%d\n", + type, DpRam, slot); + + RIOHostReset(type, DpRam, slot); + + /* + ** Scrub the memory. This comes in several banks: + ** DPsram1 - 7000h bytes + ** DPsram2 - 200h bytes + ** DPsram3 - 7000h bytes + ** scratch - 1000h bytes + */ + + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Setup ram/size arrays\n"); + + size[0] = DP_SRAM1_SIZE; + size[1] = DP_SRAM2_SIZE; + size[2] = DP_SRAM3_SIZE; + size[3] = DP_SCRATCH_SIZE; + + ram[0] = DpRam->DpSram1; + ram[1] = DpRam->DpSram2; + ram[2] = DpRam->DpSram3; + nbanks = (type == RIO_PCI) ? 3 : 4; + if (nbanks == 4) + ram[3] = DpRam->DpScratch; + + + if (nbanks == 3) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Memory: %p(0x%x), %p(0x%x), %p(0x%x)\n", + ram[0], size[0], ram[1], size[1], ram[2], size[2]); + } else { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: %p(0x%x), %p(0x%x), %p(0x%x), %p(0x%x)\n", + ram[0], size[0], ram[1], size[1], ram[2], size[2], ram[3], size[3]); + } + + /* + ** This scrub operation will test for crosstalk between + ** banks. TEST_END is a magic number, and relates to the offset + ** within the 'val' array used by Scrub. + */ + for (op=0; op<TEST_END; op++) { + for (bank=0; bank<nbanks; bank++) { + if (RIOScrub(op, ram[bank], size[bank]) == RIO_FAIL) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: RIOScrub band %d, op %d failed\n", + bank, op); + return RIO_FAIL; + } + } + } + + rio_dprintk (RIO_DEBUG_INIT, "Test completed\n"); + return 0; +} + + +/* +** Scrub an area of RAM. +** Define PRETEST and POSTTEST for a more thorough checking of the +** state of the memory. +** Call with op set to an index into the above 'val' array to determine +** which value will be written into memory. +** Call with op set to zero means that the RAM will not be read and checked +** before it is written. +** Call with op not zero and the RAM will be read and compared with val[op-1] +** to check that the data from the previous phase was retained. +*/ + +static int RIOScrub(int op, u8 __iomem *ram, int size) +{ + int off; + unsigned char oldbyte; + unsigned char newbyte; + unsigned char invbyte; + unsigned short oldword; + unsigned short newword; + unsigned short invword; + unsigned short swapword; + + if (op) { + oldbyte = val[op-1]; + oldword = oldbyte | (oldbyte<<8); + } else + oldbyte = oldword = 0; /* Tell the compiler we've initilalized them. */ + newbyte = val[op]; + newword = newbyte | (newbyte<<8); + invbyte = ~newbyte; + invword = invbyte | (invbyte<<8); + + /* + ** Check that the RAM contains the value that should have been left there + ** by the previous test (not applicable for pass zero) + */ + if (op) { + for (off=0; off<size; off++) { + if (readb(ram + off) != oldbyte) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Pre Check 1: BYTE at offset 0x%x should have been=%x, was=%x\n", off, oldbyte, readb(ram + off)); + return RIO_FAIL; + } + } + for (off=0; off<size; off+=2) { + if (readw(ram + off) != oldword) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Pre Check: WORD at offset 0x%x should have been=%x, was=%x\n",off,oldword, readw(ram + off)); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Pre Check: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram+off+1)); + return RIO_FAIL; + } + } + } + + /* + ** Now write the INVERSE of the test data into every location, using + ** BYTE write operations, first checking before each byte is written + ** that the location contains the old value still, and checking after + ** the write that the location contains the data specified - this is + ** the BYTE read/write test. + */ + for (off=0; off<size; off++) { + if (op && (readb(ram + off) != oldbyte)) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Pre Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off, oldbyte, readb(ram + off)); + return RIO_FAIL; + } + writeb(invbyte, ram + off); + if (readb(ram + off) != invbyte) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Inv Check: BYTE at offset 0x%x should have been=%x, was=%x\n", off, invbyte, readb(ram + off)); + return RIO_FAIL; + } + } + + /* + ** now, use WORD operations to write the test value into every location, + ** check as before that the location contains the previous test value + ** before overwriting, and that it contains the data value written + ** afterwards. + ** This is the WORD operation test. + */ + for (off=0; off<size; off+=2) { + if (readw(ram + off) != invword) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Inv Check: WORD at offset 0x%x should have been=%x, was=%x\n", off, invword, readw(ram + off)); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Inv Check: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram+off+1)); + return RIO_FAIL; + } + + writew(newword, ram + off); + if ( readw(ram + off) != newword ) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 1: WORD at offset 0x%x should have been=%x, was=%x\n", off, newword, readw(ram + off)); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 1: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1)); + return RIO_FAIL; + } + } + + /* + ** now run through the block of memory again, first in byte mode + ** then in word mode, and check that all the locations contain the + ** required test data. + */ + for (off=0; off<size; off++) { + if (readb(ram + off) != newbyte) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Byte Check: BYTE at offset 0x%x should have been=%x, was=%x\n", off, newbyte, readb(ram + off)); + return RIO_FAIL; + } + } + + for (off=0; off<size; off+=2) { + if (readw(ram + off) != newword ) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 2: WORD at offset 0x%x should have been=%x, was=%x\n", off, newword, readw(ram + off)); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 2: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1)); + return RIO_FAIL; + } + } + + /* + ** time to check out byte swapping errors + */ + swapword = invbyte | (newbyte << 8); + + for (off=0; off<size; off+=2) { + writeb(invbyte, &ram[off]); + writeb(newbyte, &ram[off+1]); + } + + for ( off=0; off<size; off+=2 ) { + if (readw(ram + off) != swapword) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 1: WORD at offset 0x%x should have been=%x, was=%x\n", off, swapword, readw(ram + off)); + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 1: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1)); + return RIO_FAIL; + } + writew(~swapword, ram + off); + } + + for (off=0; off<size; off+=2) { + if (readb(ram + off) != newbyte) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off, newbyte, readb(ram + off)); + return RIO_FAIL; + } + if (readb(ram + off + 1) != invbyte) { + rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off+1, invbyte, readb(ram + off + 1)); + return RIO_FAIL; + } + writew(newword, ram + off); + } + return 0; +} + + +int RIODefaultName(struct rio_info *p, struct Host *HostP, unsigned int UnitId) +{ + memcpy(HostP->Mapping[UnitId].Name, "UNKNOWN RTA X-XX", 17); + HostP->Mapping[UnitId].Name[12]='1'+(HostP-p->RIOHosts); + if ((UnitId+1) > 9) { + HostP->Mapping[UnitId].Name[14]='0'+((UnitId+1)/10); + HostP->Mapping[UnitId].Name[15]='0'+((UnitId+1)%10); + } + else { + HostP->Mapping[UnitId].Name[14]='1'+UnitId; + HostP->Mapping[UnitId].Name[15]=0; + } + return 0; +} + +#define RIO_RELEASE "Linux" +#define RELEASE_ID "1.0" + +static struct rioVersion stVersion; + +struct rioVersion *RIOVersid(void) +{ + strlcpy(stVersion.version, "RIO driver for linux V1.0", + sizeof(stVersion.version)); + strlcpy(stVersion.buildDate, __DATE__, + sizeof(stVersion.buildDate)); + + return &stVersion; +} + +void RIOHostReset(unsigned int Type, struct DpRam __iomem *DpRamP, unsigned int Slot) +{ + /* + ** Reset the Tpu + */ + rio_dprintk (RIO_DEBUG_INIT, "RIOHostReset: type 0x%x", Type); + switch ( Type ) { + case RIO_AT: + rio_dprintk (RIO_DEBUG_INIT, " (RIO_AT)\n"); + writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | INTERRUPT_DISABLE | BYTE_OPERATION | + SLOW_LINKS | SLOW_AT_BUS, &DpRamP->DpControl); + writeb(0xFF, &DpRamP->DpResetTpu); + udelay(3); + rio_dprintk (RIO_DEBUG_INIT, "RIOHostReset: Don't know if it worked. Try reset again\n"); + writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | INTERRUPT_DISABLE | + BYTE_OPERATION | SLOW_LINKS | SLOW_AT_BUS, &DpRamP->DpControl); + writeb(0xFF, &DpRamP->DpResetTpu); + udelay(3); + break; + case RIO_PCI: + rio_dprintk (RIO_DEBUG_INIT, " (RIO_PCI)\n"); + writeb(RIO_PCI_BOOT_FROM_RAM, &DpRamP->DpControl); + writeb(0xFF, &DpRamP->DpResetInt); + writeb(0xFF, &DpRamP->DpResetTpu); + udelay(100); + break; + default: + rio_dprintk (RIO_DEBUG_INIT, " (UNKNOWN)\n"); + break; + } + return; +} diff --git a/drivers/staging/generic_serial/rio/riointr.c b/drivers/staging/generic_serial/rio/riointr.c new file mode 100644 index 0000000..2e71aec --- /dev/null +++ b/drivers/staging/generic_serial/rio/riointr.c @@ -0,0 +1,645 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riointr.c +** SID : 1.2 +** Last Modified : 11/6/98 10:33:44 +** Retrieved : 11/6/98 10:33:49 +** +** ident @(#)riointr.c 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + +#include <linux/delay.h> + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" + + +static void RIOReceive(struct rio_info *, struct Port *); + + +static char *firstchars(char *p, int nch) +{ + static char buf[2][128]; + static int t = 0; + t = !t; + memcpy(buf[t], p, nch); + buf[t][nch] = 0; + return buf[t]; +} + + +#define INCR( P, I ) ((P) = (((P)+(I)) & p->RIOBufferMask)) +/* Enable and start the transmission of packets */ +void RIOTxEnable(char *en) +{ + struct Port *PortP; + struct rio_info *p; + struct tty_struct *tty; + int c; + struct PKT __iomem *PacketP; + unsigned long flags; + + PortP = (struct Port *) en; + p = (struct rio_info *) PortP->p; + tty = PortP->gs.port.tty; + + + rio_dprintk(RIO_DEBUG_INTR, "tx port %d: %d chars queued.\n", PortP->PortNum, PortP->gs.xmit_cnt); + + if (!PortP->gs.xmit_cnt) + return; + + + /* This routine is an order of magnitude simpler than the specialix + version. One of the disadvantages is that this version will send + an incomplete packet (usually 64 bytes instead of 72) once for + every 4k worth of data. Let's just say that this won't influence + performance significantly..... */ + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + while (can_add_transmit(&PacketP, PortP)) { + c = PortP->gs.xmit_cnt; + if (c > PKT_MAX_DATA_LEN) + c = PKT_MAX_DATA_LEN; + + /* Don't copy past the end of the source buffer */ + if (c > SERIAL_XMIT_SIZE - PortP->gs.xmit_tail) + c = SERIAL_XMIT_SIZE - PortP->gs.xmit_tail; + + { + int t; + t = (c > 10) ? 10 : c; + + rio_dprintk(RIO_DEBUG_INTR, "rio: tx port %d: copying %d chars: %s - %s\n", PortP->PortNum, c, firstchars(PortP->gs.xmit_buf + PortP->gs.xmit_tail, t), firstchars(PortP->gs.xmit_buf + PortP->gs.xmit_tail + c - t, t)); + } + /* If for one reason or another, we can't copy more data, + we're done! */ + if (c == 0) + break; + + rio_memcpy_toio(PortP->HostP->Caddr, PacketP->data, PortP->gs.xmit_buf + PortP->gs.xmit_tail, c); + /* udelay (1); */ + + writeb(c, &(PacketP->len)); + if (!(PortP->State & RIO_DELETED)) { + add_transmit(PortP); + /* + ** Count chars tx'd for port statistics reporting + */ + if (PortP->statsGather) + PortP->txchars += c; + } + PortP->gs.xmit_tail = (PortP->gs.xmit_tail + c) & (SERIAL_XMIT_SIZE - 1); + PortP->gs.xmit_cnt -= c; + } + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + + if (PortP->gs.xmit_cnt <= (PortP->gs.wakeup_chars + 2 * PKT_MAX_DATA_LEN)) + tty_wakeup(PortP->gs.port.tty); + +} + + +/* +** RIO Host Service routine. Does all the work traditionally associated with an +** interrupt. +*/ +static int RupIntr; +static int RxIntr; +static int TxIntr; + +void RIOServiceHost(struct rio_info *p, struct Host *HostP) +{ + rio_spin_lock(&HostP->HostLock); + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) { + static int t = 0; + rio_spin_unlock(&HostP->HostLock); + if ((t++ % 200) == 0) + rio_dprintk(RIO_DEBUG_INTR, "Interrupt but host not running. flags=%x.\n", (int) HostP->Flags); + return; + } + rio_spin_unlock(&HostP->HostLock); + + if (readw(&HostP->ParmMapP->rup_intr)) { + writew(0, &HostP->ParmMapP->rup_intr); + p->RIORupCount++; + RupIntr++; + rio_dprintk(RIO_DEBUG_INTR, "rio: RUP interrupt on host %Zd\n", HostP - p->RIOHosts); + RIOPollHostCommands(p, HostP); + } + + if (readw(&HostP->ParmMapP->rx_intr)) { + int port; + + writew(0, &HostP->ParmMapP->rx_intr); + p->RIORxCount++; + RxIntr++; + + rio_dprintk(RIO_DEBUG_INTR, "rio: RX interrupt on host %Zd\n", HostP - p->RIOHosts); + /* + ** Loop through every port. If the port is mapped into + ** the system ( i.e. has /dev/ttyXXXX associated ) then it is + ** worth checking. If the port isn't open, grab any packets + ** hanging on its receive queue and stuff them on the free + ** list; check for commands on the way. + */ + for (port = p->RIOFirstPortsBooted; port < p->RIOLastPortsBooted + PORTS_PER_RTA; port++) { + struct Port *PortP = p->RIOPortp[port]; + struct tty_struct *ttyP; + struct PKT __iomem *PacketP; + + /* + ** not mapped in - most of the RIOPortp[] information + ** has not been set up! + ** Optimise: ports come in bundles of eight. + */ + if (!PortP->Mapped) { + port += 7; + continue; /* with the next port */ + } + + /* + ** If the host board isn't THIS host board, check the next one. + ** optimise: ports come in bundles of eight. + */ + if (PortP->HostP != HostP) { + port += 7; + continue; + } + + /* + ** Let us see - is the port open? If not, then don't service it. + */ + if (!(PortP->PortState & PORT_ISOPEN)) { + continue; + } + + /* + ** find corresponding tty structure. The process of mapping + ** the ports puts these here. + */ + ttyP = PortP->gs.port.tty; + + /* + ** Lock the port before we begin working on it. + */ + rio_spin_lock(&PortP->portSem); + + /* + ** Process received data if there is any. + */ + if (can_remove_receive(&PacketP, PortP)) + RIOReceive(p, PortP); + + /* + ** If there is no data left to be read from the port, and + ** it's handshake bit is set, then we must clear the handshake, + ** so that that downstream RTA is re-enabled. + */ + if (!can_remove_receive(&PacketP, PortP) && (readw(&PortP->PhbP->handshake) == PHB_HANDSHAKE_SET)) { + /* + ** MAGIC! ( Basically, handshake the RX buffer, so that + ** the RTAs upstream can be re-enabled. ) + */ + rio_dprintk(RIO_DEBUG_INTR, "Set RX handshake bit\n"); + writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &PortP->PhbP->handshake); + } + rio_spin_unlock(&PortP->portSem); + } + } + + if (readw(&HostP->ParmMapP->tx_intr)) { + int port; + + writew(0, &HostP->ParmMapP->tx_intr); + + p->RIOTxCount++; + TxIntr++; + rio_dprintk(RIO_DEBUG_INTR, "rio: TX interrupt on host %Zd\n", HostP - p->RIOHosts); + + /* + ** Loop through every port. + ** If the port is mapped into the system ( i.e. has /dev/ttyXXXX + ** associated ) then it is worth checking. + */ + for (port = p->RIOFirstPortsBooted; port < p->RIOLastPortsBooted + PORTS_PER_RTA; port++) { + struct Port *PortP = p->RIOPortp[port]; + struct tty_struct *ttyP; + struct PKT __iomem *PacketP; + + /* + ** not mapped in - most of the RIOPortp[] information + ** has not been set up! + */ + if (!PortP->Mapped) { + port += 7; + continue; /* with the next port */ + } + + /* + ** If the host board isn't running, then its data structures + ** are no use to us - continue quietly. + */ + if (PortP->HostP != HostP) { + port += 7; + continue; /* with the next port */ + } + + /* + ** Let us see - is the port open? If not, then don't service it. + */ + if (!(PortP->PortState & PORT_ISOPEN)) { + continue; + } + + rio_dprintk(RIO_DEBUG_INTR, "rio: Looking into port %d.\n", port); + /* + ** Lock the port before we begin working on it. + */ + rio_spin_lock(&PortP->portSem); + + /* + ** If we can't add anything to the transmit queue, then + ** we need do none of this processing. + */ + if (!can_add_transmit(&PacketP, PortP)) { + rio_dprintk(RIO_DEBUG_INTR, "Can't add to port, so skipping.\n"); + rio_spin_unlock(&PortP->portSem); + continue; + } + + /* + ** find corresponding tty structure. The process of mapping + ** the ports puts these here. + */ + ttyP = PortP->gs.port.tty; + /* If ttyP is NULL, the port is getting closed. Forget about it. */ + if (!ttyP) { + rio_dprintk(RIO_DEBUG_INTR, "no tty, so skipping.\n"); + rio_spin_unlock(&PortP->portSem); + continue; + } + /* + ** If there is more room available we start up the transmit + ** data process again. This can be direct I/O, if the cookmode + ** is set to COOK_RAW or COOK_MEDIUM, or will be a call to the + ** riotproc( T_OUTPUT ) if we are in COOK_WELL mode, to fetch + ** characters via the line discipline. We must always call + ** the line discipline, + ** so that user input characters can be echoed correctly. + ** + ** ++++ Update +++++ + ** With the advent of double buffering, we now see if + ** TxBufferOut-In is non-zero. If so, then we copy a packet + ** to the output place, and set it going. If this empties + ** the buffer, then we must issue a wakeup( ) on OUT. + ** If it frees space in the buffer then we must issue + ** a wakeup( ) on IN. + ** + ** ++++ Extra! Extra! If PortP->WflushFlag is set, then we + ** have to send a WFLUSH command down the PHB, to mark the + ** end point of a WFLUSH. We also need to clear out any + ** data from the double buffer! ( note that WflushFlag is a + ** *count* of the number of WFLUSH commands outstanding! ) + ** + ** ++++ And there's more! + ** If an RTA is powered off, then on again, and rebooted, + ** whilst it has ports open, then we need to re-open the ports. + ** ( reasonable enough ). We can't do this when we spot the + ** re-boot, in interrupt time, because the queue is probably + ** full. So, when we come in here, we need to test if any + ** ports are in this condition, and re-open the port before + ** we try to send any more data to it. Now, the re-booted + ** RTA will be discarding packets from the PHB until it + ** receives this open packet, but don't worry tooo much + ** about that. The one thing that is interesting is the + ** combination of this effect and the WFLUSH effect! + */ + /* For now don't handle RTA reboots. -- REW. + Reenabled. Otherwise RTA reboots didn't work. Duh. -- REW */ + if (PortP->MagicFlags) { + if (PortP->MagicFlags & MAGIC_REBOOT) { + /* + ** well, the RTA has been rebooted, and there is room + ** on its queue to add the open packet that is required. + ** + ** The messy part of this line is trying to decide if + ** we need to call the Param function as a tty or as + ** a modem. + ** DONT USE CLOCAL AS A TEST FOR THIS! + ** + ** If we can't param the port, then move on to the + ** next port. + */ + PortP->InUse = NOT_INUSE; + + rio_spin_unlock(&PortP->portSem); + if (RIOParam(PortP, RIOC_OPEN, ((PortP->Cor2Copy & (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) == (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) ? 1 : 0, DONT_SLEEP) == RIO_FAIL) + continue; /* with next port */ + rio_spin_lock(&PortP->portSem); + PortP->MagicFlags &= ~MAGIC_REBOOT; + } + + /* + ** As mentioned above, this is a tacky hack to cope + ** with WFLUSH + */ + if (PortP->WflushFlag) { + rio_dprintk(RIO_DEBUG_INTR, "Want to WFLUSH mark this port\n"); + + if (PortP->InUse) + rio_dprintk(RIO_DEBUG_INTR, "FAILS - PORT IS IN USE\n"); + } + + while (PortP->WflushFlag && can_add_transmit(&PacketP, PortP) && (PortP->InUse == NOT_INUSE)) { + int p; + struct PktCmd __iomem *PktCmdP; + + rio_dprintk(RIO_DEBUG_INTR, "Add WFLUSH marker to data queue\n"); + /* + ** make it look just like a WFLUSH command + */ + PktCmdP = (struct PktCmd __iomem *) &PacketP->data[0]; + + writeb(RIOC_WFLUSH, &PktCmdP->Command); + + p = PortP->HostPort % (u16) PORTS_PER_RTA; + + /* + ** If second block of ports for 16 port RTA, add 8 + ** to index 8-15. + */ + if (PortP->SecondBlock) + p += PORTS_PER_RTA; + + writeb(p, &PktCmdP->PhbNum); + + /* + ** to make debuggery easier + */ + writeb('W', &PacketP->data[2]); + writeb('F', &PacketP->data[3]); + writeb('L', &PacketP->data[4]); + writeb('U', &PacketP->data[5]); + writeb('S', &PacketP->data[6]); + writeb('H', &PacketP->data[7]); + writeb(' ', &PacketP->data[8]); + writeb('0' + PortP->WflushFlag, &PacketP->data[9]); + writeb(' ', &PacketP->data[10]); + writeb(' ', &PacketP->data[11]); + writeb('\0', &PacketP->data[12]); + + /* + ** its two bytes long! + */ + writeb(PKT_CMD_BIT | 2, &PacketP->len); + + /* + ** queue it! + */ + if (!(PortP->State & RIO_DELETED)) { + add_transmit(PortP); + /* + ** Count chars tx'd for port statistics reporting + */ + if (PortP->statsGather) + PortP->txchars += 2; + } + + if (--(PortP->WflushFlag) == 0) { + PortP->MagicFlags &= ~MAGIC_FLUSH; + } + + rio_dprintk(RIO_DEBUG_INTR, "Wflush count now stands at %d\n", PortP->WflushFlag); + } + if (PortP->MagicFlags & MORE_OUTPUT_EYGOR) { + if (PortP->MagicFlags & MAGIC_FLUSH) { + PortP->MagicFlags |= MORE_OUTPUT_EYGOR; + } else { + if (!can_add_transmit(&PacketP, PortP)) { + rio_spin_unlock(&PortP->portSem); + continue; + } + rio_spin_unlock(&PortP->portSem); + RIOTxEnable((char *) PortP); + rio_spin_lock(&PortP->portSem); + PortP->MagicFlags &= ~MORE_OUTPUT_EYGOR; + } + } + } + + + /* + ** If we can't add anything to the transmit queue, then + ** we need do none of the remaining processing. + */ + if (!can_add_transmit(&PacketP, PortP)) { + rio_spin_unlock(&PortP->portSem); + continue; + } + + rio_spin_unlock(&PortP->portSem); + RIOTxEnable((char *) PortP); + } + } +} + +/* +** Routine for handling received data for tty drivers +*/ +static void RIOReceive(struct rio_info *p, struct Port *PortP) +{ + struct tty_struct *TtyP; + unsigned short transCount; + struct PKT __iomem *PacketP; + register unsigned int DataCnt; + unsigned char __iomem *ptr; + unsigned char *buf; + int copied = 0; + + static int intCount, RxIntCnt; + + /* + ** The receive data process is to remove packets from the + ** PHB until there aren't any more or the current cblock + ** is full. When this occurs, there will be some left over + ** data in the packet, that we must do something with. + ** As we haven't unhooked the packet from the read list + ** yet, we can just leave the packet there, having first + ** made a note of how far we got. This means that we need + ** a pointer per port saying where we start taking the + ** data from - this will normally be zero, but when we + ** run out of space it will be set to the offset of the + ** next byte to copy from the packet data area. The packet + ** length field is decremented by the number of bytes that + ** we successfully removed from the packet. When this reaches + ** zero, we reset the offset pointer to be zero, and free + ** the packet from the front of the queue. + */ + + intCount++; + + TtyP = PortP->gs.port.tty; + if (!TtyP) { + rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: tty is null. \n"); + return; + } + + if (PortP->State & RIO_THROTTLE_RX) { + rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: Throttled. Can't handle more input.\n"); + return; + } + + if (PortP->State & RIO_DELETED) { + while (can_remove_receive(&PacketP, PortP)) { + remove_receive(PortP); + put_free_end(PortP->HostP, PacketP); + } + } else { + /* + ** loop, just so long as: + ** i ) there's some data ( i.e. can_remove_receive ) + ** ii ) we haven't been blocked + ** iii ) there's somewhere to put the data + ** iv ) we haven't outstayed our welcome + */ + transCount = 1; + while (can_remove_receive(&PacketP, PortP) + && transCount) { + RxIntCnt++; + + /* + ** check that it is not a command! + */ + if (readb(&PacketP->len) & PKT_CMD_BIT) { + rio_dprintk(RIO_DEBUG_INTR, "RIO: unexpected command packet received on PHB\n"); + /* rio_dprint(RIO_DEBUG_INTR, (" sysport = %d\n", p->RIOPortp->PortNum)); */ + rio_dprintk(RIO_DEBUG_INTR, " dest_unit = %d\n", readb(&PacketP->dest_unit)); + rio_dprintk(RIO_DEBUG_INTR, " dest_port = %d\n", readb(&PacketP->dest_port)); + rio_dprintk(RIO_DEBUG_INTR, " src_unit = %d\n", readb(&PacketP->src_unit)); + rio_dprintk(RIO_DEBUG_INTR, " src_port = %d\n", readb(&PacketP->src_port)); + rio_dprintk(RIO_DEBUG_INTR, " len = %d\n", readb(&PacketP->len)); + rio_dprintk(RIO_DEBUG_INTR, " control = %d\n", readb(&PacketP->control)); + rio_dprintk(RIO_DEBUG_INTR, " csum = %d\n", readw(&PacketP->csum)); + rio_dprintk(RIO_DEBUG_INTR, " data bytes: "); + for (DataCnt = 0; DataCnt < PKT_MAX_DATA_LEN; DataCnt++) + rio_dprintk(RIO_DEBUG_INTR, "%d\n", readb(&PacketP->data[DataCnt])); + remove_receive(PortP); + put_free_end(PortP->HostP, PacketP); + continue; /* with next packet */ + } + + /* + ** How many characters can we move 'upstream' ? + ** + ** Determine the minimum of the amount of data + ** available and the amount of space in which to + ** put it. + ** + ** 1. Get the packet length by masking 'len' + ** for only the length bits. + ** 2. Available space is [buffer size] - [space used] + ** + ** Transfer count is the minimum of packet length + ** and available space. + */ + + transCount = tty_buffer_request_room(TtyP, readb(&PacketP->len) & PKT_LEN_MASK); + rio_dprintk(RIO_DEBUG_REC, "port %d: Copy %d bytes\n", PortP->PortNum, transCount); + /* + ** To use the following 'kkprintfs' for debugging - change the '#undef' + ** to '#define', (this is the only place ___DEBUG_IT___ occurs in the + ** driver). + */ + ptr = (unsigned char __iomem *) PacketP->data + PortP->RxDataStart; + + tty_prepare_flip_string(TtyP, &buf, transCount); + rio_memcpy_fromio(buf, ptr, transCount); + PortP->RxDataStart += transCount; + writeb(readb(&PacketP->len)-transCount, &PacketP->len); + copied += transCount; + + + + if (readb(&PacketP->len) == 0) { + /* + ** If we have emptied the packet, then we can + ** free it, and reset the start pointer for + ** the next packet. + */ + remove_receive(PortP); + put_free_end(PortP->HostP, PacketP); + PortP->RxDataStart = 0; + } + } + } + if (copied) { + rio_dprintk(RIO_DEBUG_REC, "port %d: pushing tty flip buffer: %d total bytes copied.\n", PortP->PortNum, copied); + tty_flip_buffer_push(TtyP); + } + + return; +} + diff --git a/drivers/staging/generic_serial/rio/rioioctl.h b/drivers/staging/generic_serial/rio/rioioctl.h new file mode 100644 index 0000000..e8af5b3 --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioioctl.h @@ -0,0 +1,57 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioioctl.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:13 +** Retrieved : 11/6/98 11:34:22 +** +** ident @(#)rioioctl.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rioioctl_h__ +#define __rioioctl_h__ + +/* +** RIO device driver - user ioctls and associated structures. +*/ + +struct portStats { + int port; + int gather; + unsigned long txchars; + unsigned long rxchars; + unsigned long opens; + unsigned long closes; + unsigned long ioctls; +}; + +#define RIOC ('R'<<8)|('i'<<16)|('o'<<24) + +#define RIO_QUICK_CHECK (RIOC | 105) +#define RIO_GATHER_PORT_STATS (RIOC | 193) +#define RIO_RESET_PORT_STATS (RIOC | 194) +#define RIO_GET_PORT_STATS (RIOC | 195) + +#endif /* __rioioctl_h__ */ diff --git a/drivers/staging/generic_serial/rio/rioparam.c b/drivers/staging/generic_serial/rio/rioparam.c new file mode 100644 index 0000000..6415f3f --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioparam.c @@ -0,0 +1,663 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioparam.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:45 +** Retrieved : 11/6/98 10:33:50 +** +** ident @(#)rioparam.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "param.h" + + + +/* +** The Scam, based on email from jeremyr@bugs.specialix.co.uk.... +** +** To send a command on a particular port, you put a packet with the +** command bit set onto the port. The command bit is in the len field, +** and gets ORed in with the actual byte count. +** +** When you send a packet with the command bit set the first +** data byte (data[0]) is interpreted as the command to execute. +** It also governs what data structure overlay should accompany the packet. +** Commands are defined in cirrus/cirrus.h +** +** If you want the command to pre-emt data already on the queue for the +** port, set the pre-emptive bit in conjunction with the command bit. +** It is not defined what will happen if you set the preemptive bit +** on a packet that is NOT a command. +** +** Pre-emptive commands should be queued at the head of the queue using +** add_start(), whereas normal commands and data are enqueued using +** add_end(). +** +** Most commands do not use the remaining bytes in the data array. The +** exceptions are OPEN MOPEN and CONFIG. (NB. As with the SI CONFIG and +** OPEN are currently analogous). With these three commands the following +** 11 data bytes are all used to pass config information such as baud rate etc. +** The fields are also defined in cirrus.h. Some contain straightforward +** information such as the transmit XON character. Two contain the transmit and +** receive baud rates respectively. For most baud rates there is a direct +** mapping between the rates defined in <sys/termio.h> and the byte in the +** packet. There are additional (non UNIX-standard) rates defined in +** /u/dos/rio/cirrus/h/brates.h. +** +** The rest of the data fields contain approximations to the Cirrus registers +** that are used to program number of bits etc. Each registers bit fields is +** defined in cirrus.h. +** +** NB. Only use those bits that are defined as being driver specific +** or common to the RTA and the driver. +** +** All commands going from RTA->Host will be dealt with by the Host code - you +** will never see them. As with the SI there will be three fields to look out +** for in each phb (not yet defined - needs defining a.s.a.p). +** +** modem_status - current state of handshake pins. +** +** port_status - current port status - equivalent to hi_stat for SI, indicates +** if port is IDLE_OPEN, IDLE_CLOSED etc. +** +** break_status - bit X set if break has been received. +** +** Happy hacking. +** +*/ + +/* +** RIOParam is used to open or configure a port. You pass it a PortP, +** which will have a tty struct attached to it. You also pass a command, +** either OPEN or CONFIG. The port's setup is taken from the t_ fields +** of the tty struct inside the PortP, and the port is either opened +** or re-configured. You must also tell RIOParam if the device is a modem +** device or not (i.e. top bit of minor number set or clear - take special +** care when deciding on this!). +** RIOParam neither flushes nor waits for drain, and is NOT preemptive. +** +** RIOParam assumes it will be called at splrio(), and also assumes +** that CookMode is set correctly in the port structure. +** +** NB. for MPX +** tty lock must NOT have been previously acquired. +*/ +int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) +{ + struct tty_struct *TtyP; + int retval; + struct phb_param __iomem *phb_param_ptr; + struct PKT __iomem *PacketP; + int res; + u8 Cor1 = 0, Cor2 = 0, Cor4 = 0, Cor5 = 0; + u8 TxXon = 0, TxXoff = 0, RxXon = 0, RxXoff = 0; + u8 LNext = 0, TxBaud = 0, RxBaud = 0; + int retries = 0xff; + unsigned long flags; + + func_enter(); + + TtyP = PortP->gs.port.tty; + + rio_dprintk(RIO_DEBUG_PARAM, "RIOParam: Port:%d cmd:%d Modem:%d SleepFlag:%d Mapped: %d, tty=%p\n", PortP->PortNum, cmd, Modem, SleepFlag, PortP->Mapped, TtyP); + + if (!TtyP) { + rio_dprintk(RIO_DEBUG_PARAM, "Can't call rioparam with null tty.\n"); + + func_exit(); + + return RIO_FAIL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + + if (cmd == RIOC_OPEN) { + /* + ** If the port is set to store or lock the parameters, and it is + ** paramed with OPEN, we want to restore the saved port termio, but + ** only if StoredTermio has been saved, i.e. NOT 1st open after reboot. + */ + } + + /* + ** wait for space + */ + while (!(res = can_add_transmit(&PacketP, PortP)) || (PortP->InUse != NOT_INUSE)) { + if (retries-- <= 0) { + break; + } + if (PortP->InUse != NOT_INUSE) { + rio_dprintk(RIO_DEBUG_PARAM, "Port IN_USE for pre-emptive command\n"); + } + + if (!res) { + rio_dprintk(RIO_DEBUG_PARAM, "Port has no space on transmit queue\n"); + } + + if (SleepFlag != OK_TO_SLEEP) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + + return RIO_FAIL; + } + + rio_dprintk(RIO_DEBUG_PARAM, "wait for can_add_transmit\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + retval = RIODelay(PortP, HUNDRED_MS); + rio_spin_lock_irqsave(&PortP->portSem, flags); + if (retval == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_PARAM, "wait for can_add_transmit broken by signal\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + return -EINTR; + } + if (PortP->State & RIO_DELETED) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + return 0; + } + } + + if (!res) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + + return RIO_FAIL; + } + + rio_dprintk(RIO_DEBUG_PARAM, "can_add_transmit() returns %x\n", res); + rio_dprintk(RIO_DEBUG_PARAM, "Packet is %p\n", PacketP); + + phb_param_ptr = (struct phb_param __iomem *) PacketP->data; + + + switch (TtyP->termios->c_cflag & CSIZE) { + case CS5: + { + rio_dprintk(RIO_DEBUG_PARAM, "5 bit data\n"); + Cor1 |= RIOC_COR1_5BITS; + break; + } + case CS6: + { + rio_dprintk(RIO_DEBUG_PARAM, "6 bit data\n"); + Cor1 |= RIOC_COR1_6BITS; + break; + } + case CS7: + { + rio_dprintk(RIO_DEBUG_PARAM, "7 bit data\n"); + Cor1 |= RIOC_COR1_7BITS; + break; + } + case CS8: + { + rio_dprintk(RIO_DEBUG_PARAM, "8 bit data\n"); + Cor1 |= RIOC_COR1_8BITS; + break; + } + } + + if (TtyP->termios->c_cflag & CSTOPB) { + rio_dprintk(RIO_DEBUG_PARAM, "2 stop bits\n"); + Cor1 |= RIOC_COR1_2STOP; + } else { + rio_dprintk(RIO_DEBUG_PARAM, "1 stop bit\n"); + Cor1 |= RIOC_COR1_1STOP; + } + + if (TtyP->termios->c_cflag & PARENB) { + rio_dprintk(RIO_DEBUG_PARAM, "Enable parity\n"); + Cor1 |= RIOC_COR1_NORMAL; + } else { + rio_dprintk(RIO_DEBUG_PARAM, "Disable parity\n"); + Cor1 |= RIOC_COR1_NOP; + } + if (TtyP->termios->c_cflag & PARODD) { + rio_dprintk(RIO_DEBUG_PARAM, "Odd parity\n"); + Cor1 |= RIOC_COR1_ODD; + } else { + rio_dprintk(RIO_DEBUG_PARAM, "Even parity\n"); + Cor1 |= RIOC_COR1_EVEN; + } + + /* + ** COR 2 + */ + if (TtyP->termios->c_iflag & IXON) { + rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop output control\n"); + Cor2 |= RIOC_COR2_IXON; + } else { + if (PortP->Config & RIO_IXON) { + rio_dprintk(RIO_DEBUG_PARAM, "Force enable start/stop output control\n"); + Cor2 |= RIOC_COR2_IXON; + } else + rio_dprintk(RIO_DEBUG_PARAM, "IXON has been disabled.\n"); + } + + if (TtyP->termios->c_iflag & IXANY) { + if (PortP->Config & RIO_IXANY) { + rio_dprintk(RIO_DEBUG_PARAM, "Enable any key to restart output\n"); + Cor2 |= RIOC_COR2_IXANY; + } else + rio_dprintk(RIO_DEBUG_PARAM, "IXANY has been disabled due to sanity reasons.\n"); + } + + if (TtyP->termios->c_iflag & IXOFF) { + rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop input control 2\n"); + Cor2 |= RIOC_COR2_IXOFF; + } + + if (TtyP->termios->c_cflag & HUPCL) { + rio_dprintk(RIO_DEBUG_PARAM, "Hangup on last close\n"); + Cor2 |= RIOC_COR2_HUPCL; + } + + if (C_CRTSCTS(TtyP)) { + rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control enabled\n"); + Cor2 |= RIOC_COR2_CTSFLOW; + Cor2 |= RIOC_COR2_RTSFLOW; + } else { + rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control disabled\n"); + Cor2 &= ~RIOC_COR2_CTSFLOW; + Cor2 &= ~RIOC_COR2_RTSFLOW; + } + + + if (TtyP->termios->c_cflag & CLOCAL) { + rio_dprintk(RIO_DEBUG_PARAM, "Local line\n"); + } else { + rio_dprintk(RIO_DEBUG_PARAM, "Possible Modem line\n"); + } + + /* + ** COR 4 (there is no COR 3) + */ + if (TtyP->termios->c_iflag & IGNBRK) { + rio_dprintk(RIO_DEBUG_PARAM, "Ignore break condition\n"); + Cor4 |= RIOC_COR4_IGNBRK; + } + if (!(TtyP->termios->c_iflag & BRKINT)) { + rio_dprintk(RIO_DEBUG_PARAM, "Break generates NULL condition\n"); + Cor4 |= RIOC_COR4_NBRKINT; + } else { + rio_dprintk(RIO_DEBUG_PARAM, "Interrupt on break condition\n"); + } + + if (TtyP->termios->c_iflag & INLCR) { + rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage return on input\n"); + Cor4 |= RIOC_COR4_INLCR; + } + + if (TtyP->termios->c_iflag & IGNCR) { + rio_dprintk(RIO_DEBUG_PARAM, "Ignore carriage return on input\n"); + Cor4 |= RIOC_COR4_IGNCR; + } + + if (TtyP->termios->c_iflag & ICRNL) { + rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on input\n"); + Cor4 |= RIOC_COR4_ICRNL; + } + if (TtyP->termios->c_iflag & IGNPAR) { + rio_dprintk(RIO_DEBUG_PARAM, "Ignore characters with parity errors\n"); + Cor4 |= RIOC_COR4_IGNPAR; + } + if (TtyP->termios->c_iflag & PARMRK) { + rio_dprintk(RIO_DEBUG_PARAM, "Mark parity errors\n"); + Cor4 |= RIOC_COR4_PARMRK; + } + + /* + ** Set the RAISEMOD flag to ensure that the modem lines are raised + ** on reception of a config packet. + ** The download code handles the zero baud condition. + */ + Cor4 |= RIOC_COR4_RAISEMOD; + + /* + ** COR 5 + */ + + Cor5 = RIOC_COR5_CMOE; + + /* + ** Set to monitor tbusy/tstop (or not). + */ + + if (PortP->MonitorTstate) + Cor5 |= RIOC_COR5_TSTATE_ON; + else + Cor5 |= RIOC_COR5_TSTATE_OFF; + + /* + ** Could set LNE here if you wanted LNext processing. SVR4 will use it. + */ + if (TtyP->termios->c_iflag & ISTRIP) { + rio_dprintk(RIO_DEBUG_PARAM, "Strip input characters\n"); + if (!(PortP->State & RIO_TRIAD_MODE)) { + Cor5 |= RIOC_COR5_ISTRIP; + } + } + + if (TtyP->termios->c_oflag & ONLCR) { + rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage-return, newline on output\n"); + if (PortP->CookMode == COOK_MEDIUM) + Cor5 |= RIOC_COR5_ONLCR; + } + if (TtyP->termios->c_oflag & OCRNL) { + rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on output\n"); + if (PortP->CookMode == COOK_MEDIUM) + Cor5 |= RIOC_COR5_OCRNL; + } + if ((TtyP->termios->c_oflag & TABDLY) == TAB3) { + rio_dprintk(RIO_DEBUG_PARAM, "Tab delay 3 set\n"); + if (PortP->CookMode == COOK_MEDIUM) + Cor5 |= RIOC_COR5_TAB3; + } + + /* + ** Flow control bytes. + */ + TxXon = TtyP->termios->c_cc[VSTART]; + TxXoff = TtyP->termios->c_cc[VSTOP]; + RxXon = TtyP->termios->c_cc[VSTART]; + RxXoff = TtyP->termios->c_cc[VSTOP]; + /* + ** LNEXT byte + */ + LNext = 0; + + /* + ** Baud rate bytes + */ + rio_dprintk(RIO_DEBUG_PARAM, "Mapping of rx/tx baud %x (%x)\n", TtyP->termios->c_cflag, CBAUD); + + switch (TtyP->termios->c_cflag & CBAUD) { +#define e(b) case B ## b : RxBaud = TxBaud = RIO_B ## b ;break + e(50); + e(75); + e(110); + e(134); + e(150); + e(200); + e(300); + e(600); + e(1200); + e(1800); + e(2400); + e(4800); + e(9600); + e(19200); + e(38400); + e(57600); + e(115200); /* e(230400);e(460800); e(921600); */ + } + + rio_dprintk(RIO_DEBUG_PARAM, "tx baud 0x%x, rx baud 0x%x\n", TxBaud, RxBaud); + + + /* + ** Leftovers + */ + if (TtyP->termios->c_cflag & CREAD) + rio_dprintk(RIO_DEBUG_PARAM, "Enable receiver\n"); +#ifdef RCV1EN + if (TtyP->termios->c_cflag & RCV1EN) + rio_dprintk(RIO_DEBUG_PARAM, "RCV1EN (?)\n"); +#endif +#ifdef XMT1EN + if (TtyP->termios->c_cflag & XMT1EN) + rio_dprintk(RIO_DEBUG_PARAM, "XMT1EN (?)\n"); +#endif + if (TtyP->termios->c_lflag & ISIG) + rio_dprintk(RIO_DEBUG_PARAM, "Input character signal generating enabled\n"); + if (TtyP->termios->c_lflag & ICANON) + rio_dprintk(RIO_DEBUG_PARAM, "Canonical input: erase and kill enabled\n"); + if (TtyP->termios->c_lflag & XCASE) + rio_dprintk(RIO_DEBUG_PARAM, "Canonical upper/lower presentation\n"); + if (TtyP->termios->c_lflag & ECHO) + rio_dprintk(RIO_DEBUG_PARAM, "Enable input echo\n"); + if (TtyP->termios->c_lflag & ECHOE) + rio_dprintk(RIO_DEBUG_PARAM, "Enable echo erase\n"); + if (TtyP->termios->c_lflag & ECHOK) + rio_dprintk(RIO_DEBUG_PARAM, "Enable echo kill\n"); + if (TtyP->termios->c_lflag & ECHONL) + rio_dprintk(RIO_DEBUG_PARAM, "Enable echo newline\n"); + if (TtyP->termios->c_lflag & NOFLSH) + rio_dprintk(RIO_DEBUG_PARAM, "Disable flush after interrupt or quit\n"); +#ifdef TOSTOP + if (TtyP->termios->c_lflag & TOSTOP) + rio_dprintk(RIO_DEBUG_PARAM, "Send SIGTTOU for background output\n"); +#endif +#ifdef XCLUDE + if (TtyP->termios->c_lflag & XCLUDE) + rio_dprintk(RIO_DEBUG_PARAM, "Exclusive use of this line\n"); +#endif + if (TtyP->termios->c_iflag & IUCLC) + rio_dprintk(RIO_DEBUG_PARAM, "Map uppercase to lowercase on input\n"); + if (TtyP->termios->c_oflag & OPOST) + rio_dprintk(RIO_DEBUG_PARAM, "Enable output post-processing\n"); + if (TtyP->termios->c_oflag & OLCUC) + rio_dprintk(RIO_DEBUG_PARAM, "Map lowercase to uppercase on output\n"); + if (TtyP->termios->c_oflag & ONOCR) + rio_dprintk(RIO_DEBUG_PARAM, "No carriage return output at column 0\n"); + if (TtyP->termios->c_oflag & ONLRET) + rio_dprintk(RIO_DEBUG_PARAM, "Newline performs carriage return function\n"); + if (TtyP->termios->c_oflag & OFILL) + rio_dprintk(RIO_DEBUG_PARAM, "Use fill characters for delay\n"); + if (TtyP->termios->c_oflag & OFDEL) + rio_dprintk(RIO_DEBUG_PARAM, "Fill character is DEL\n"); + if (TtyP->termios->c_oflag & NLDLY) + rio_dprintk(RIO_DEBUG_PARAM, "Newline delay set\n"); + if (TtyP->termios->c_oflag & CRDLY) + rio_dprintk(RIO_DEBUG_PARAM, "Carriage return delay set\n"); + if (TtyP->termios->c_oflag & TABDLY) + rio_dprintk(RIO_DEBUG_PARAM, "Tab delay set\n"); + /* + ** These things are kind of useful in a later life! + */ + PortP->Cor2Copy = Cor2; + + if (PortP->State & RIO_DELETED) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + + return RIO_FAIL; + } + + /* + ** Actually write the info into the packet to be sent + */ + writeb(cmd, &phb_param_ptr->Cmd); + writeb(Cor1, &phb_param_ptr->Cor1); + writeb(Cor2, &phb_param_ptr->Cor2); + writeb(Cor4, &phb_param_ptr->Cor4); + writeb(Cor5, &phb_param_ptr->Cor5); + writeb(TxXon, &phb_param_ptr->TxXon); + writeb(RxXon, &phb_param_ptr->RxXon); + writeb(TxXoff, &phb_param_ptr->TxXoff); + writeb(RxXoff, &phb_param_ptr->RxXoff); + writeb(LNext, &phb_param_ptr->LNext); + writeb(TxBaud, &phb_param_ptr->TxBaud); + writeb(RxBaud, &phb_param_ptr->RxBaud); + + /* + ** Set the length/command field + */ + writeb(12 | PKT_CMD_BIT, &PacketP->len); + + /* + ** The packet is formed - now, whack it off + ** to its final destination: + */ + add_transmit(PortP); + /* + ** Count characters transmitted for port statistics reporting + */ + if (PortP->statsGather) + PortP->txchars += 12; + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + + rio_dprintk(RIO_DEBUG_PARAM, "add_transmit returned.\n"); + /* + ** job done. + */ + func_exit(); + + return 0; +} + + +/* +** We can add another packet to a transmit queue if the packet pointer pointed +** to by the TxAdd pointer has PKT_IN_USE clear in its address. +*/ +int can_add_transmit(struct PKT __iomem **PktP, struct Port *PortP) +{ + struct PKT __iomem *tp; + + *PktP = tp = (struct PKT __iomem *) RIO_PTR(PortP->Caddr, readw(PortP->TxAdd)); + + return !((unsigned long) tp & PKT_IN_USE); +} + +/* +** To add a packet to the queue, you set the PKT_IN_USE bit in the address, +** and then move the TxAdd pointer along one position to point to the next +** packet pointer. You must wrap the pointer from the end back to the start. +*/ +void add_transmit(struct Port *PortP) +{ + if (readw(PortP->TxAdd) & PKT_IN_USE) { + rio_dprintk(RIO_DEBUG_PARAM, "add_transmit: Packet has been stolen!"); + } + writew(readw(PortP->TxAdd) | PKT_IN_USE, PortP->TxAdd); + PortP->TxAdd = (PortP->TxAdd == PortP->TxEnd) ? PortP->TxStart : PortP->TxAdd + 1; + writew(RIO_OFF(PortP->Caddr, PortP->TxAdd), &PortP->PhbP->tx_add); +} + +/**************************************** + * Put a packet onto the end of the + * free list + ****************************************/ +void put_free_end(struct Host *HostP, struct PKT __iomem *PktP) +{ + struct rio_free_list __iomem *tmp_pointer; + unsigned short old_end, new_end; + unsigned long flags; + + rio_spin_lock_irqsave(&HostP->HostLock, flags); + + /************************************************* + * Put a packet back onto the back of the free list + * + ************************************************/ + + rio_dprintk(RIO_DEBUG_PFE, "put_free_end(PktP=%p)\n", PktP); + + if ((old_end = readw(&HostP->ParmMapP->free_list_end)) != TPNULL) { + new_end = RIO_OFF(HostP->Caddr, PktP); + tmp_pointer = (struct rio_free_list __iomem *) RIO_PTR(HostP->Caddr, old_end); + writew(new_end, &tmp_pointer->next); + writew(old_end, &((struct rio_free_list __iomem *) PktP)->prev); + writew(TPNULL, &((struct rio_free_list __iomem *) PktP)->next); + writew(new_end, &HostP->ParmMapP->free_list_end); + } else { /* First packet on the free list this should never happen! */ + rio_dprintk(RIO_DEBUG_PFE, "put_free_end(): This should never happen\n"); + writew(RIO_OFF(HostP->Caddr, PktP), &HostP->ParmMapP->free_list_end); + tmp_pointer = (struct rio_free_list __iomem *) PktP; + writew(TPNULL, &tmp_pointer->prev); + writew(TPNULL, &tmp_pointer->next); + } + rio_dprintk(RIO_DEBUG_CMD, "Before unlock: %p\n", &HostP->HostLock); + rio_spin_unlock_irqrestore(&HostP->HostLock, flags); +} + +/* +** can_remove_receive(PktP,P) returns non-zero if PKT_IN_USE is set +** for the next packet on the queue. It will also set PktP to point to the +** relevant packet, [having cleared the PKT_IN_USE bit]. If PKT_IN_USE is clear, +** then can_remove_receive() returns 0. +*/ +int can_remove_receive(struct PKT __iomem **PktP, struct Port *PortP) +{ + if (readw(PortP->RxRemove) & PKT_IN_USE) { + *PktP = (struct PKT __iomem *) RIO_PTR(PortP->Caddr, readw(PortP->RxRemove) & ~PKT_IN_USE); + return 1; + } + return 0; +} + +/* +** To remove a packet from the receive queue you clear its PKT_IN_USE bit, +** and then bump the pointers. Once the pointers get to the end, they must +** be wrapped back to the start. +*/ +void remove_receive(struct Port *PortP) +{ + writew(readw(PortP->RxRemove) & ~PKT_IN_USE, PortP->RxRemove); + PortP->RxRemove = (PortP->RxRemove == PortP->RxEnd) ? PortP->RxStart : PortP->RxRemove + 1; + writew(RIO_OFF(PortP->Caddr, PortP->RxRemove), &PortP->PhbP->rx_remove); +} diff --git a/drivers/staging/generic_serial/rio/rioroute.c b/drivers/staging/generic_serial/rio/rioroute.c new file mode 100644 index 0000000..f9b936a --- /dev/null +++ b/drivers/staging/generic_serial/rio/rioroute.c @@ -0,0 +1,1039 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : rioroute.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:46 +** Retrieved : 11/6/98 10:33:50 +** +** ident @(#)rioroute.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "param.h" + +static int RIOCheckIsolated(struct rio_info *, struct Host *, unsigned int); +static int RIOIsolate(struct rio_info *, struct Host *, unsigned int); +static int RIOCheck(struct Host *, unsigned int); +static void RIOConCon(struct rio_info *, struct Host *, unsigned int, unsigned int, unsigned int, unsigned int, int); + + +/* +** Incoming on the ROUTE_RUP +** I wrote this while I was tired. Forgive me. +*/ +int RIORouteRup(struct rio_info *p, unsigned int Rup, struct Host *HostP, struct PKT __iomem * PacketP) +{ + struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *) PacketP->data; + struct PktCmd_M *PktReplyP; + struct CmdBlk *CmdBlkP; + struct Port *PortP; + struct Map *MapP; + struct Top *TopP; + int ThisLink, ThisLinkMin, ThisLinkMax; + int port; + int Mod, Mod1, Mod2; + unsigned short RtaType; + unsigned int RtaUniq; + unsigned int ThisUnit, ThisUnit2; /* 2 ids to accommodate 16 port RTA */ + unsigned int OldUnit, NewUnit, OldLink, NewLink; + char *MyType, *MyName; + int Lies; + unsigned long flags; + + /* + ** Is this unit telling us it's current link topology? + */ + if (readb(&PktCmdP->Command) == ROUTE_TOPOLOGY) { + MapP = HostP->Mapping; + + /* + ** The packet can be sent either by the host or by an RTA. + ** If it comes from the host, then we need to fill in the + ** Topology array in the host structure. If it came in + ** from an RTA then we need to fill in the Mapping structure's + ** Topology array for the unit. + */ + if (Rup >= (unsigned short) MAX_RUP) { + ThisUnit = HOST_ID; + TopP = HostP->Topology; + MyType = "Host"; + MyName = HostP->Name; + ThisLinkMin = ThisLinkMax = Rup - MAX_RUP; + } else { + ThisUnit = Rup + 1; + TopP = HostP->Mapping[Rup].Topology; + MyType = "RTA"; + MyName = HostP->Mapping[Rup].Name; + ThisLinkMin = 0; + ThisLinkMax = LINKS_PER_UNIT - 1; + } + + /* + ** Lies will not be tolerated. + ** If any pair of links claim to be connected to the same + ** place, then ignore this packet completely. + */ + Lies = 0; + for (ThisLink = ThisLinkMin + 1; ThisLink <= ThisLinkMax; ThisLink++) { + /* + ** it won't lie about network interconnect, total disconnects + ** and no-IDs. (or at least, it doesn't *matter* if it does) + */ + if (readb(&PktCmdP->RouteTopology[ThisLink].Unit) > (unsigned short) MAX_RUP) + continue; + + for (NewLink = ThisLinkMin; NewLink < ThisLink; NewLink++) { + if ((readb(&PktCmdP->RouteTopology[ThisLink].Unit) == readb(&PktCmdP->RouteTopology[NewLink].Unit)) && (readb(&PktCmdP->RouteTopology[ThisLink].Link) == readb(&PktCmdP->RouteTopology[NewLink].Link))) { + Lies++; + } + } + } + + if (Lies) { + rio_dprintk(RIO_DEBUG_ROUTE, "LIES! DAMN LIES! %d LIES!\n", Lies); + rio_dprintk(RIO_DEBUG_ROUTE, "%d:%c %d:%c %d:%c %d:%c\n", + readb(&PktCmdP->RouteTopology[0].Unit), + 'A' + readb(&PktCmdP->RouteTopology[0].Link), + readb(&PktCmdP->RouteTopology[1].Unit), + 'A' + readb(&PktCmdP->RouteTopology[1].Link), readb(&PktCmdP->RouteTopology[2].Unit), 'A' + readb(&PktCmdP->RouteTopology[2].Link), readb(&PktCmdP->RouteTopology[3].Unit), 'A' + readb(&PktCmdP->RouteTopology[3].Link)); + return 1; + } + + /* + ** now, process each link. + */ + for (ThisLink = ThisLinkMin; ThisLink <= ThisLinkMax; ThisLink++) { + /* + ** this is what it was connected to + */ + OldUnit = TopP[ThisLink].Unit; + OldLink = TopP[ThisLink].Link; + + /* + ** this is what it is now connected to + */ + NewUnit = readb(&PktCmdP->RouteTopology[ThisLink].Unit); + NewLink = readb(&PktCmdP->RouteTopology[ThisLink].Link); + + if (OldUnit != NewUnit || OldLink != NewLink) { + /* + ** something has changed! + */ + + if (NewUnit > MAX_RUP && NewUnit != ROUTE_DISCONNECT && NewUnit != ROUTE_NO_ID && NewUnit != ROUTE_INTERCONNECT) { + rio_dprintk(RIO_DEBUG_ROUTE, "I have a link from %s %s to unit %d:%d - I don't like it.\n", MyType, MyName, NewUnit, NewLink); + } else { + /* + ** put the new values in + */ + TopP[ThisLink].Unit = NewUnit; + TopP[ThisLink].Link = NewLink; + + RIOSetChange(p); + + if (OldUnit <= MAX_RUP) { + /* + ** If something has become bust, then re-enable them messages + */ + if (!p->RIONoMessage) + RIOConCon(p, HostP, ThisUnit, ThisLink, OldUnit, OldLink, DISCONNECT); + } + + if ((NewUnit <= MAX_RUP) && !p->RIONoMessage) + RIOConCon(p, HostP, ThisUnit, ThisLink, NewUnit, NewLink, CONNECT); + + if (NewUnit == ROUTE_NO_ID) + rio_dprintk(RIO_DEBUG_ROUTE, "%s %s (%c) is connected to an unconfigured unit.\n", MyType, MyName, 'A' + ThisLink); + + if (NewUnit == ROUTE_INTERCONNECT) { + if (!p->RIONoMessage) + printk(KERN_DEBUG "rio: %s '%s' (%c) is connected to another network.\n", MyType, MyName, 'A' + ThisLink); + } + + /* + ** perform an update for 'the other end', so that these messages + ** only appears once. Only disconnect the other end if it is pointing + ** at us! + */ + if (OldUnit == HOST_ID) { + if (HostP->Topology[OldLink].Unit == ThisUnit && HostP->Topology[OldLink].Link == ThisLink) { + rio_dprintk(RIO_DEBUG_ROUTE, "SETTING HOST (%c) TO DISCONNECTED!\n", OldLink + 'A'); + HostP->Topology[OldLink].Unit = ROUTE_DISCONNECT; + HostP->Topology[OldLink].Link = NO_LINK; + } else { + rio_dprintk(RIO_DEBUG_ROUTE, "HOST(%c) WAS NOT CONNECTED TO %s (%c)!\n", OldLink + 'A', HostP->Mapping[ThisUnit - 1].Name, ThisLink + 'A'); + } + } else if (OldUnit <= MAX_RUP) { + if (HostP->Mapping[OldUnit - 1].Topology[OldLink].Unit == ThisUnit && HostP->Mapping[OldUnit - 1].Topology[OldLink].Link == ThisLink) { + rio_dprintk(RIO_DEBUG_ROUTE, "SETTING RTA %s (%c) TO DISCONNECTED!\n", HostP->Mapping[OldUnit - 1].Name, OldLink + 'A'); + HostP->Mapping[OldUnit - 1].Topology[OldLink].Unit = ROUTE_DISCONNECT; + HostP->Mapping[OldUnit - 1].Topology[OldLink].Link = NO_LINK; + } else { + rio_dprintk(RIO_DEBUG_ROUTE, "RTA %s (%c) WAS NOT CONNECTED TO %s (%c)\n", HostP->Mapping[OldUnit - 1].Name, OldLink + 'A', HostP->Mapping[ThisUnit - 1].Name, ThisLink + 'A'); + } + } + if (NewUnit == HOST_ID) { + rio_dprintk(RIO_DEBUG_ROUTE, "MARKING HOST (%c) CONNECTED TO %s (%c)\n", NewLink + 'A', MyName, ThisLink + 'A'); + HostP->Topology[NewLink].Unit = ThisUnit; + HostP->Topology[NewLink].Link = ThisLink; + } else if (NewUnit <= MAX_RUP) { + rio_dprintk(RIO_DEBUG_ROUTE, "MARKING RTA %s (%c) CONNECTED TO %s (%c)\n", HostP->Mapping[NewUnit - 1].Name, NewLink + 'A', MyName, ThisLink + 'A'); + HostP->Mapping[NewUnit - 1].Topology[NewLink].Unit = ThisUnit; + HostP->Mapping[NewUnit - 1].Topology[NewLink].Link = ThisLink; + } + } + RIOSetChange(p); + RIOCheckIsolated(p, HostP, OldUnit); + } + } + return 1; + } + + /* + ** The only other command we recognise is a route_request command + */ + if (readb(&PktCmdP->Command) != ROUTE_REQUEST) { + rio_dprintk(RIO_DEBUG_ROUTE, "Unknown command %d received on rup %d host %p ROUTE_RUP\n", readb(&PktCmdP->Command), Rup, HostP); + return 1; + } + + RtaUniq = (readb(&PktCmdP->UniqNum[0])) + (readb(&PktCmdP->UniqNum[1]) << 8) + (readb(&PktCmdP->UniqNum[2]) << 16) + (readb(&PktCmdP->UniqNum[3]) << 24); + + /* + ** Determine if 8 or 16 port RTA + */ + RtaType = GetUnitType(RtaUniq); + + rio_dprintk(RIO_DEBUG_ROUTE, "Received a request for an ID for serial number %x\n", RtaUniq); + + Mod = readb(&PktCmdP->ModuleTypes); + Mod1 = LONYBLE(Mod); + if (RtaType == TYPE_RTA16) { + /* + ** Only one ident is set for a 16 port RTA. To make compatible + ** with 8 port, set 2nd ident in Mod2 to the same as Mod1. + */ + Mod2 = Mod1; + rio_dprintk(RIO_DEBUG_ROUTE, "Backplane type is %s (all ports)\n", p->RIOModuleTypes[Mod1].Name); + } else { + Mod2 = HINYBLE(Mod); + rio_dprintk(RIO_DEBUG_ROUTE, "Module types are %s (ports 0-3) and %s (ports 4-7)\n", p->RIOModuleTypes[Mod1].Name, p->RIOModuleTypes[Mod2].Name); + } + + /* + ** try to unhook a command block from the command free list. + */ + if (!(CmdBlkP = RIOGetCmdBlk())) { + rio_dprintk(RIO_DEBUG_ROUTE, "No command blocks to route RTA! come back later.\n"); + return 0; + } + + /* + ** Fill in the default info on the command block + */ + CmdBlkP->Packet.dest_unit = Rup; + CmdBlkP->Packet.dest_port = ROUTE_RUP; + CmdBlkP->Packet.src_unit = HOST_ID; + CmdBlkP->Packet.src_port = ROUTE_RUP; + CmdBlkP->Packet.len = PKT_CMD_BIT | 1; + CmdBlkP->PreFuncP = CmdBlkP->PostFuncP = NULL; + PktReplyP = (struct PktCmd_M *) CmdBlkP->Packet.data; + + if (!RIOBootOk(p, HostP, RtaUniq)) { + rio_dprintk(RIO_DEBUG_ROUTE, "RTA %x tried to get an ID, but does not belong - FOAD it!\n", RtaUniq); + PktReplyP->Command = ROUTE_FOAD; + memcpy(PktReplyP->CommandText, "RT_FOAD", 7); + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + return 1; + } + + /* + ** Check to see if the RTA is configured for this host + */ + for (ThisUnit = 0; ThisUnit < MAX_RUP; ThisUnit++) { + rio_dprintk(RIO_DEBUG_ROUTE, "Entry %d Flags=%s %s UniqueNum=0x%x\n", + ThisUnit, HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE ? "Slot-In-Use" : "Not In Use", HostP->Mapping[ThisUnit].Flags & SLOT_TENTATIVE ? "Slot-Tentative" : "Not Tentative", HostP->Mapping[ThisUnit].RtaUniqueNum); + + /* + ** We have an entry for it. + */ + if ((HostP->Mapping[ThisUnit].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) && (HostP->Mapping[ThisUnit].RtaUniqueNum == RtaUniq)) { + if (RtaType == TYPE_RTA16) { + ThisUnit2 = HostP->Mapping[ThisUnit].ID2 - 1; + rio_dprintk(RIO_DEBUG_ROUTE, "Found unit 0x%x at slots %d+%d\n", RtaUniq, ThisUnit, ThisUnit2); + } else + rio_dprintk(RIO_DEBUG_ROUTE, "Found unit 0x%x at slot %d\n", RtaUniq, ThisUnit); + /* + ** If we have no knowledge of booting it, then the host has + ** been re-booted, and so we must kill the RTA, so that it + ** will be booted again (potentially with new bins) + ** and it will then re-ask for an ID, which we will service. + */ + if ((HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE) && !(HostP->Mapping[ThisUnit].Flags & RTA_BOOTED)) { + if (!(HostP->Mapping[ThisUnit].Flags & MSG_DONE)) { + if (!p->RIONoMessage) + printk(KERN_DEBUG "rio: RTA '%s' is being updated.\n", HostP->Mapping[ThisUnit].Name); + HostP->Mapping[ThisUnit].Flags |= MSG_DONE; + } + PktReplyP->Command = ROUTE_FOAD; + memcpy(PktReplyP->CommandText, "RT_FOAD", 7); + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + return 1; + } + + /* + ** Send the ID (entry) to this RTA. The ID number is implicit as + ** the offset into the table. It is worth noting at this stage + ** that offset zero in the table contains the entries for the + ** RTA with ID 1!!!! + */ + PktReplyP->Command = ROUTE_ALLOCATE; + PktReplyP->IDNum = ThisUnit + 1; + if (RtaType == TYPE_RTA16) { + if (HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE) + /* + ** Adjust the phb and tx pkt dest_units for 2nd block of 8 + ** only if the RTA has ports associated (SLOT_IN_USE) + */ + RIOFixPhbs(p, HostP, ThisUnit2); + PktReplyP->IDNum2 = ThisUnit2 + 1; + rio_dprintk(RIO_DEBUG_ROUTE, "RTA '%s' has been allocated IDs %d+%d\n", HostP->Mapping[ThisUnit].Name, PktReplyP->IDNum, PktReplyP->IDNum2); + } else { + PktReplyP->IDNum2 = ROUTE_NO_ID; + rio_dprintk(RIO_DEBUG_ROUTE, "RTA '%s' has been allocated ID %d\n", HostP->Mapping[ThisUnit].Name, PktReplyP->IDNum); + } + memcpy(PktReplyP->CommandText, "RT_ALLOCAT", 10); + + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + + /* + ** If this is a freshly booted RTA, then we need to re-open + ** the ports, if any where open, so that data may once more + ** flow around the system! + */ + if ((HostP->Mapping[ThisUnit].Flags & RTA_NEWBOOT) && (HostP->Mapping[ThisUnit].SysPort != NO_PORT)) { + /* + ** look at the ports associated with this beast and + ** see if any where open. If they was, then re-open + ** them, using the info from the tty flags. + */ + for (port = 0; port < PORTS_PER_RTA; port++) { + PortP = p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort]; + if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) { + rio_dprintk(RIO_DEBUG_ROUTE, "Re-opened this port\n"); + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->MagicFlags |= MAGIC_REBOOT; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + } + if (RtaType == TYPE_RTA16) { + for (port = 0; port < PORTS_PER_RTA; port++) { + PortP = p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort]; + if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) { + rio_dprintk(RIO_DEBUG_ROUTE, "Re-opened this port\n"); + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->MagicFlags |= MAGIC_REBOOT; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + } + } + } + + /* + ** keep a copy of the module types! + */ + HostP->UnixRups[ThisUnit].ModTypes = Mod; + if (RtaType == TYPE_RTA16) + HostP->UnixRups[ThisUnit2].ModTypes = Mod; + + /* + ** If either of the modules on this unit is read-only or write-only + ** or none-xprint, then we need to transfer that info over to the + ** relevant ports. + */ + if (HostP->Mapping[ThisUnit].SysPort != NO_PORT) { + for (port = 0; port < PORTS_PER_MODULE; port++) { + p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort]->Config &= ~RIO_NOMASK; + p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort]->Config |= p->RIOModuleTypes[Mod1].Flags[port]; + p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit].SysPort]->Config &= ~RIO_NOMASK; + p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit].SysPort]->Config |= p->RIOModuleTypes[Mod2].Flags[port]; + } + if (RtaType == TYPE_RTA16) { + for (port = 0; port < PORTS_PER_MODULE; port++) { + p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort]->Config &= ~RIO_NOMASK; + p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort]->Config |= p->RIOModuleTypes[Mod1].Flags[port]; + p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit2].SysPort]->Config &= ~RIO_NOMASK; + p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit2].SysPort]->Config |= p->RIOModuleTypes[Mod2].Flags[port]; + } + } + } + + /* + ** Job done, get on with the interrupts! + */ + return 1; + } + } + /* + ** There is no table entry for this RTA at all. + ** + ** Lets check to see if we actually booted this unit - if not, + ** then we reset it and it will go round the loop of being booted + ** we can then worry about trying to fit it into the table. + */ + for (ThisUnit = 0; ThisUnit < HostP->NumExtraBooted; ThisUnit++) + if (HostP->ExtraUnits[ThisUnit] == RtaUniq) + break; + if (ThisUnit == HostP->NumExtraBooted && ThisUnit != MAX_EXTRA_UNITS) { + /* + ** if the unit wasn't in the table, and the table wasn't full, then + ** we reset the unit, because we didn't boot it. + ** However, if the table is full, it could be that we did boot + ** this unit, and so we won't reboot it, because it isn't really + ** all that disasterous to keep the old bins in most cases. This + ** is a rather tacky feature, but we are on the edge of reallity + ** here, because the implication is that someone has connected + ** 16+MAX_EXTRA_UNITS onto one host. + */ + static int UnknownMesgDone = 0; + + if (!UnknownMesgDone) { + if (!p->RIONoMessage) + printk(KERN_DEBUG "rio: One or more unknown RTAs are being updated.\n"); + UnknownMesgDone = 1; + } + + PktReplyP->Command = ROUTE_FOAD; + memcpy(PktReplyP->CommandText, "RT_FOAD", 7); + } else { + /* + ** we did boot it (as an extra), and there may now be a table + ** slot free (because of a delete), so we will try to make + ** a tentative entry for it, so that the configurator can see it + ** and fill in the details for us. + */ + if (RtaType == TYPE_RTA16) { + if (RIOFindFreeID(p, HostP, &ThisUnit, &ThisUnit2) == 0) { + RIODefaultName(p, HostP, ThisUnit); + rio_fill_host_slot(ThisUnit, ThisUnit2, RtaUniq, HostP); + } + } else { + if (RIOFindFreeID(p, HostP, &ThisUnit, NULL) == 0) { + RIODefaultName(p, HostP, ThisUnit); + rio_fill_host_slot(ThisUnit, 0, RtaUniq, HostP); + } + } + PktReplyP->Command = ROUTE_USED; + memcpy(PktReplyP->CommandText, "RT_USED", 7); + } + RIOQueueCmdBlk(HostP, Rup, CmdBlkP); + return 1; +} + + +void RIOFixPhbs(struct rio_info *p, struct Host *HostP, unsigned int unit) +{ + unsigned short link, port; + struct Port *PortP; + unsigned long flags; + int PortN = HostP->Mapping[unit].SysPort; + + rio_dprintk(RIO_DEBUG_ROUTE, "RIOFixPhbs unit %d sysport %d\n", unit, PortN); + + if (PortN != -1) { + unsigned short dest_unit = HostP->Mapping[unit].ID2; + + /* + ** Get the link number used for the 1st 8 phbs on this unit. + */ + PortP = p->RIOPortp[HostP->Mapping[dest_unit - 1].SysPort]; + + link = readw(&PortP->PhbP->link); + + for (port = 0; port < PORTS_PER_RTA; port++, PortN++) { + unsigned short dest_port = port + 8; + u16 __iomem *TxPktP; + struct PKT __iomem *Pkt; + + PortP = p->RIOPortp[PortN]; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + /* + ** If RTA is not powered on, the tx packets will be + ** unset, so go no further. + */ + if (!PortP->TxStart) { + rio_dprintk(RIO_DEBUG_ROUTE, "Tx pkts not set up yet\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + break; + } + + /* + ** For the second slot of a 16 port RTA, the driver needs to + ** sort out the phb to port mappings. The dest_unit for this + ** group of 8 phbs is set to the dest_unit of the accompanying + ** 8 port block. The dest_port of the second unit is set to + ** be in the range 8-15 (i.e. 8 is added). Thus, for a 16 port + ** RTA with IDs 5 and 6, traffic bound for port 6 of unit 6 + ** (being the second map ID) will be sent to dest_unit 5, port + ** 14. When this RTA is deleted, dest_unit for ID 6 will be + ** restored, and the dest_port will be reduced by 8. + ** Transmit packets also have a destination field which needs + ** adjusting in the same manner. + ** Note that the unit/port bytes in 'dest' are swapped. + ** We also need to adjust the phb and rup link numbers for the + ** second block of 8 ttys. + */ + for (TxPktP = PortP->TxStart; TxPktP <= PortP->TxEnd; TxPktP++) { + /* + ** *TxPktP is the pointer to the transmit packet on the host + ** card. This needs to be translated into a 32 bit pointer + ** so it can be accessed from the driver. + */ + Pkt = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(TxPktP)); + + /* + ** If the packet is used, reset it. + */ + Pkt = (struct PKT __iomem *) ((unsigned long) Pkt & ~PKT_IN_USE); + writeb(dest_unit, &Pkt->dest_unit); + writeb(dest_port, &Pkt->dest_port); + } + rio_dprintk(RIO_DEBUG_ROUTE, "phb dest: Old %x:%x New %x:%x\n", readw(&PortP->PhbP->destination) & 0xff, (readw(&PortP->PhbP->destination) >> 8) & 0xff, dest_unit, dest_port); + writew(dest_unit + (dest_port << 8), &PortP->PhbP->destination); + writew(link, &PortP->PhbP->link); + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + /* + ** Now make sure the range of ports to be serviced includes + ** the 2nd 8 on this 16 port RTA. + */ + if (link > 3) + return; + if (((unit * 8) + 7) > readw(&HostP->LinkStrP[link].last_port)) { + rio_dprintk(RIO_DEBUG_ROUTE, "last port on host link %d: %d\n", link, (unit * 8) + 7); + writew((unit * 8) + 7, &HostP->LinkStrP[link].last_port); + } + } +} + +/* +** Check to see if the new disconnection has isolated this unit. +** If it has, then invalidate all its link information, and tell +** the world about it. This is done to ensure that the configurator +** only gets up-to-date information about what is going on. +*/ +static int RIOCheckIsolated(struct rio_info *p, struct Host *HostP, unsigned int UnitId) +{ + unsigned long flags; + rio_spin_lock_irqsave(&HostP->HostLock, flags); + + if (RIOCheck(HostP, UnitId)) { + rio_dprintk(RIO_DEBUG_ROUTE, "Unit %d is NOT isolated\n", UnitId); + rio_spin_unlock_irqrestore(&HostP->HostLock, flags); + return (0); + } + + RIOIsolate(p, HostP, UnitId); + RIOSetChange(p); + rio_spin_unlock_irqrestore(&HostP->HostLock, flags); + return 1; +} + +/* +** Invalidate all the link interconnectivity of this unit, and of +** all the units attached to it. This will mean that the entire +** subnet will re-introduce itself. +*/ +static int RIOIsolate(struct rio_info *p, struct Host *HostP, unsigned int UnitId) +{ + unsigned int link, unit; + + UnitId--; /* this trick relies on the Unit Id being UNSIGNED! */ + + if (UnitId >= MAX_RUP) /* dontcha just lurv unsigned maths! */ + return (0); + + if (HostP->Mapping[UnitId].Flags & BEEN_HERE) + return (0); + + HostP->Mapping[UnitId].Flags |= BEEN_HERE; + + if (p->RIOPrintDisabled == DO_PRINT) + rio_dprintk(RIO_DEBUG_ROUTE, "RIOMesgIsolated %s", HostP->Mapping[UnitId].Name); + + for (link = 0; link < LINKS_PER_UNIT; link++) { + unit = HostP->Mapping[UnitId].Topology[link].Unit; + HostP->Mapping[UnitId].Topology[link].Unit = ROUTE_DISCONNECT; + HostP->Mapping[UnitId].Topology[link].Link = NO_LINK; + RIOIsolate(p, HostP, unit); + } + HostP->Mapping[UnitId].Flags &= ~BEEN_HERE; + return 1; +} + +static int RIOCheck(struct Host *HostP, unsigned int UnitId) +{ + unsigned char link; + +/* rio_dprint(RIO_DEBUG_ROUTE, ("Check to see if unit %d has a route to the host\n",UnitId)); */ + rio_dprintk(RIO_DEBUG_ROUTE, "RIOCheck : UnitID = %d\n", UnitId); + + if (UnitId == HOST_ID) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is NOT isolated - it IS the host!\n", UnitId)); */ + return 1; + } + + UnitId--; + + if (UnitId >= MAX_RUP) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d - ignored.\n", UnitId)); */ + return 0; + } + + for (link = 0; link < LINKS_PER_UNIT; link++) { + if (HostP->Mapping[UnitId].Topology[link].Unit == HOST_ID) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is connected directly to host via link (%c).\n", + UnitId, 'A'+link)); */ + return 1; + } + } + + if (HostP->Mapping[UnitId].Flags & BEEN_HERE) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Been to Unit %d before - ignoring\n", UnitId)); */ + return 0; + } + + HostP->Mapping[UnitId].Flags |= BEEN_HERE; + + for (link = 0; link < LINKS_PER_UNIT; link++) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d check link (%c)\n", UnitId,'A'+link)); */ + if (RIOCheck(HostP, HostP->Mapping[UnitId].Topology[link].Unit)) { + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is connected to something that knows the host via link (%c)\n", UnitId,link+'A')); */ + HostP->Mapping[UnitId].Flags &= ~BEEN_HERE; + return 1; + } + } + + HostP->Mapping[UnitId].Flags &= ~BEEN_HERE; + + /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d DOESNT KNOW THE HOST!\n", UnitId)); */ + + return 0; +} + +/* +** Returns the type of unit (host, 16/8 port RTA) +*/ + +unsigned int GetUnitType(unsigned int Uniq) +{ + switch ((Uniq >> 28) & 0xf) { + case RIO_AT: + case RIO_MCA: + case RIO_EISA: + case RIO_PCI: + rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: Host\n"); + return (TYPE_HOST); + case RIO_RTA_16: + rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: 16 port RTA\n"); + return (TYPE_RTA16); + case RIO_RTA: + rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: 8 port RTA\n"); + return (TYPE_RTA8); + default: + rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: Unrecognised\n"); + return (99); + } +} + +int RIOSetChange(struct rio_info *p) +{ + if (p->RIOQuickCheck != NOT_CHANGED) + return (0); + p->RIOQuickCheck = CHANGED; + if (p->RIOSignalProcess) { + rio_dprintk(RIO_DEBUG_ROUTE, "Send SIG-HUP"); + /* + psignal( RIOSignalProcess, SIGHUP ); + */ + } + return (0); +} + +static void RIOConCon(struct rio_info *p, + struct Host *HostP, + unsigned int FromId, + unsigned int FromLink, + unsigned int ToId, + unsigned int ToLink, + int Change) +{ + char *FromName; + char *FromType; + char *ToName; + char *ToType; + unsigned int tp; + +/* +** 15.10.1998 ARG - ESIL 0759 +** (Part) fix for port being trashed when opened whilst RTA "disconnected" +** +** What's this doing in here anyway ? +** It was causing the port to be 'unmapped' if opened whilst RTA "disconnected" +** +** 09.12.1998 ARG - ESIL 0776 - part fix +** Okay, We've found out what this was all about now ! +** Someone had botched this to use RIOHalted to indicated the number of RTAs +** 'disconnected'. The value in RIOHalted was then being used in the +** 'RIO_QUICK_CHECK' ioctl. A none zero value indicating that a least one RTA +** is 'disconnected'. The change was put in to satisfy a customer's needs. +** Having taken this bit of code out 'RIO_QUICK_CHECK' now no longer works for +** the customer. +** + if (Change == CONNECT) { + if (p->RIOHalted) p->RIOHalted --; + } + else { + p->RIOHalted ++; + } +** +** So - we need to implement it slightly differently - a new member of the +** rio_info struct - RIORtaDisCons (RIO RTA connections) keeps track of RTA +** connections and disconnections. +*/ + if (Change == CONNECT) { + if (p->RIORtaDisCons) + p->RIORtaDisCons--; + } else { + p->RIORtaDisCons++; + } + + if (p->RIOPrintDisabled == DONT_PRINT) + return; + + if (FromId > ToId) { + tp = FromId; + FromId = ToId; + ToId = tp; + tp = FromLink; + FromLink = ToLink; + ToLink = tp; + } + + FromName = FromId ? HostP->Mapping[FromId - 1].Name : HostP->Name; + FromType = FromId ? "RTA" : "HOST"; + ToName = ToId ? HostP->Mapping[ToId - 1].Name : HostP->Name; + ToType = ToId ? "RTA" : "HOST"; + + rio_dprintk(RIO_DEBUG_ROUTE, "Link between %s '%s' (%c) and %s '%s' (%c) %s.\n", FromType, FromName, 'A' + FromLink, ToType, ToName, 'A' + ToLink, (Change == CONNECT) ? "established" : "disconnected"); + printk(KERN_DEBUG "rio: Link between %s '%s' (%c) and %s '%s' (%c) %s.\n", FromType, FromName, 'A' + FromLink, ToType, ToName, 'A' + ToLink, (Change == CONNECT) ? "established" : "disconnected"); +} + +/* +** RIORemoveFromSavedTable : +** +** Delete and RTA entry from the saved table given to us +** by the configuration program. +*/ +static int RIORemoveFromSavedTable(struct rio_info *p, struct Map *pMap) +{ + int entry; + + /* + ** We loop for all entries even after finding an entry and + ** zeroing it because we may have two entries to delete if + ** it's a 16 port RTA. + */ + for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) { + if (p->RIOSavedTable[entry].RtaUniqueNum == pMap->RtaUniqueNum) { + memset(&p->RIOSavedTable[entry], 0, sizeof(struct Map)); + } + } + return 0; +} + + +/* +** RIOCheckDisconnected : +** +** Scan the unit links to and return zero if the unit is completely +** disconnected. +*/ +static int RIOFreeDisconnected(struct rio_info *p, struct Host *HostP, int unit) +{ + int link; + + + rio_dprintk(RIO_DEBUG_ROUTE, "RIOFreeDisconnect unit %d\n", unit); + /* + ** If the slot is tentative and does not belong to the + ** second half of a 16 port RTA then scan to see if + ** is disconnected. + */ + for (link = 0; link < LINKS_PER_UNIT; link++) { + if (HostP->Mapping[unit].Topology[link].Unit != ROUTE_DISCONNECT) + break; + } + + /* + ** If not all links are disconnected then we can forget about it. + */ + if (link < LINKS_PER_UNIT) + return 1; + +#ifdef NEED_TO_FIX_THIS + /* Ok so all the links are disconnected. But we may have only just + ** made this slot tentative and not yet received a topology update. + ** Lets check how long ago we made it tentative. + */ + rio_dprintk(RIO_DEBUG_ROUTE, "Just about to check LBOLT on entry %d\n", unit); + if (drv_getparm(LBOLT, (ulong_t *) & current_time)) + rio_dprintk(RIO_DEBUG_ROUTE, "drv_getparm(LBOLT,....) Failed.\n"); + + elapse_time = current_time - TentTime[unit]; + rio_dprintk(RIO_DEBUG_ROUTE, "elapse %d = current %d - tent %d (%d usec)\n", elapse_time, current_time, TentTime[unit], drv_hztousec(elapse_time)); + if (drv_hztousec(elapse_time) < WAIT_TO_FINISH) { + rio_dprintk(RIO_DEBUG_ROUTE, "Skipping slot %d, not timed out yet %d\n", unit, drv_hztousec(elapse_time)); + return 1; + } +#endif + + /* + ** We have found an usable slot. + ** If it is half of a 16 port RTA then delete the other half. + */ + if (HostP->Mapping[unit].ID2 != 0) { + int nOther = (HostP->Mapping[unit].ID2) - 1; + + rio_dprintk(RIO_DEBUG_ROUTE, "RioFreedis second slot %d.\n", nOther); + memset(&HostP->Mapping[nOther], 0, sizeof(struct Map)); + } + RIORemoveFromSavedTable(p, &HostP->Mapping[unit]); + + return 0; +} + + +/* +** RIOFindFreeID : +** +** This function scans the given host table for either one +** or two free unit ID's. +*/ + +int RIOFindFreeID(struct rio_info *p, struct Host *HostP, unsigned int * pID1, unsigned int * pID2) +{ + int unit, tempID; + + /* + ** Initialise the ID's to MAX_RUP. + ** We do this to make the loop for setting the ID's as simple as + ** possible. + */ + *pID1 = MAX_RUP; + if (pID2 != NULL) + *pID2 = MAX_RUP; + + /* + ** Scan all entries of the host mapping table for free slots. + ** We scan for free slots first and then if that is not successful + ** we start all over again looking for tentative slots we can re-use. + */ + for (unit = 0; unit < MAX_RUP; unit++) { + rio_dprintk(RIO_DEBUG_ROUTE, "Scanning unit %d\n", unit); + /* + ** If the flags are zero then the slot is empty. + */ + if (HostP->Mapping[unit].Flags == 0) { + rio_dprintk(RIO_DEBUG_ROUTE, " This slot is empty.\n"); + /* + ** If we haven't allocated the first ID then do it now. + */ + if (*pID1 == MAX_RUP) { + rio_dprintk(RIO_DEBUG_ROUTE, "Make tentative entry for first unit %d\n", unit); + *pID1 = unit; + + /* + ** If the second ID is not needed then we can return + ** now. + */ + if (pID2 == NULL) + return 0; + } else { + /* + ** Allocate the second slot and return. + */ + rio_dprintk(RIO_DEBUG_ROUTE, "Make tentative entry for second unit %d\n", unit); + *pID2 = unit; + return 0; + } + } + } + + /* + ** If we manage to come out of the free slot loop then we + ** need to start all over again looking for tentative slots + ** that we can re-use. + */ + rio_dprintk(RIO_DEBUG_ROUTE, "Starting to scan for tentative slots\n"); + for (unit = 0; unit < MAX_RUP; unit++) { + if (((HostP->Mapping[unit].Flags & SLOT_TENTATIVE) || (HostP->Mapping[unit].Flags == 0)) && !(HostP->Mapping[unit].Flags & RTA16_SECOND_SLOT)) { + rio_dprintk(RIO_DEBUG_ROUTE, " Slot %d looks promising.\n", unit); + + if (unit == *pID1) { + rio_dprintk(RIO_DEBUG_ROUTE, " No it isn't, its the 1st half\n"); + continue; + } + + /* + ** Slot is Tentative or Empty, but not a tentative second + ** slot of a 16 porter. + ** Attempt to free up this slot (and its parnter if + ** it is a 16 port slot. The second slot will become + ** empty after a call to RIOFreeDisconnected so thats why + ** we look for empty slots above as well). + */ + if (HostP->Mapping[unit].Flags != 0) + if (RIOFreeDisconnected(p, HostP, unit) != 0) + continue; + /* + ** If we haven't allocated the first ID then do it now. + */ + if (*pID1 == MAX_RUP) { + rio_dprintk(RIO_DEBUG_ROUTE, "Grab tentative entry for first unit %d\n", unit); + *pID1 = unit; + + /* + ** Clear out this slot now that we intend to use it. + */ + memset(&HostP->Mapping[unit], 0, sizeof(struct Map)); + + /* + ** If the second ID is not needed then we can return + ** now. + */ + if (pID2 == NULL) + return 0; + } else { + /* + ** Allocate the second slot and return. + */ + rio_dprintk(RIO_DEBUG_ROUTE, "Grab tentative/empty entry for second unit %d\n", unit); + *pID2 = unit; + + /* + ** Clear out this slot now that we intend to use it. + */ + memset(&HostP->Mapping[unit], 0, sizeof(struct Map)); + + /* At this point under the right(wrong?) conditions + ** we may have a first unit ID being higher than the + ** second unit ID. This is a bad idea if we are about + ** to fill the slots with a 16 port RTA. + ** Better check and swap them over. + */ + + if (*pID1 > *pID2) { + rio_dprintk(RIO_DEBUG_ROUTE, "Swapping IDS %d %d\n", *pID1, *pID2); + tempID = *pID1; + *pID1 = *pID2; + *pID2 = tempID; + } + return 0; + } + } + } + + /* + ** If we manage to get to the end of the second loop then we + ** can give up and return a failure. + */ + return 1; +} + + +/* +** The link switch scenario. +** +** Rta Wun (A) is connected to Tuw (A). +** The tables are all up to date, and the system is OK. +** +** If Wun (A) is now moved to Wun (B) before Wun (A) can +** become disconnected, then the follow happens: +** +** Tuw (A) spots the change of unit:link at the other end +** of its link and Tuw sends a topology packet reflecting +** the change: Tuw (A) now disconnected from Wun (A), and +** this is closely followed by a packet indicating that +** Tuw (A) is now connected to Wun (B). +** +** Wun (B) will spot that it has now become connected, and +** Wun will send a topology packet, which indicates that +** both Wun (A) and Wun (B) is connected to Tuw (A). +** +** Eventually Wun (A) realises that it is now disconnected +** and Wun will send out a topology packet indicating that +** Wun (A) is now disconnected. +*/ diff --git a/drivers/staging/generic_serial/rio/riospace.h b/drivers/staging/generic_serial/rio/riospace.h new file mode 100644 index 0000000..ffb31d4 --- /dev/null +++ b/drivers/staging/generic_serial/rio/riospace.h @@ -0,0 +1,154 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riospace.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:13 +** Retrieved : 11/6/98 11:34:22 +** +** ident @(#)riospace.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_riospace_h__ +#define __rio_riospace_h__ + +#define RIO_LOCATOR_LEN 16 +#define MAX_RIO_BOARDS 4 + +/* +** DONT change this file. At all. Unless you can rebuild the entire +** device driver, which you probably can't, then the rest of the +** driver won't see any changes you make here. So don't make any. +** In particular, it won't be able to see changes to RIO_SLOTS +*/ + +struct Conf { + char Locator[24]; + unsigned int StartupTime; + unsigned int SlowCook; + unsigned int IntrPollTime; + unsigned int BreakInterval; + unsigned int Timer; + unsigned int RtaLoadBase; + unsigned int HostLoadBase; + unsigned int XpHz; + unsigned int XpCps; + char *XpOn; + char *XpOff; + unsigned int MaxXpCps; + unsigned int MinXpCps; + unsigned int SpinCmds; + unsigned int FirstAddr; + unsigned int LastAddr; + unsigned int BufferSize; + unsigned int LowWater; + unsigned int LineLength; + unsigned int CmdTime; +}; + +/* +** Board types - these MUST correspond to product codes! +*/ +#define RIO_EMPTY 0x0 +#define RIO_EISA 0x3 +#define RIO_RTA_16 0x9 +#define RIO_AT 0xA +#define RIO_MCA 0xB +#define RIO_PCI 0xD +#define RIO_RTA 0xE + +/* +** Board data structure. This is used for configuration info +*/ +struct Brd { + unsigned char Type; /* RIO_EISA, RIO_MCA, RIO_AT, RIO_EMPTY... */ + unsigned char Ivec; /* POLLED or ivec number */ + unsigned char Mode; /* Control stuff, see below */ +}; + +struct Board { + char Locator[RIO_LOCATOR_LEN]; + int NumSlots; + struct Brd Boards[MAX_RIO_BOARDS]; +}; + +#define BOOT_FROM_LINK 0x00 +#define BOOT_FROM_RAM 0x01 +#define EXTERNAL_BUS_OFF 0x00 +#define EXTERNAL_BUS_ON 0x02 +#define INTERRUPT_DISABLE 0x00 +#define INTERRUPT_ENABLE 0x04 +#define BYTE_OPERATION 0x00 +#define WORD_OPERATION 0x08 +#define POLLED INTERRUPT_DISABLE +#define IRQ_15 (0x00 | INTERRUPT_ENABLE) +#define IRQ_12 (0x10 | INTERRUPT_ENABLE) +#define IRQ_11 (0x20 | INTERRUPT_ENABLE) +#define IRQ_9 (0x30 | INTERRUPT_ENABLE) +#define SLOW_LINKS 0x00 +#define FAST_LINKS 0x40 +#define SLOW_AT_BUS 0x00 +#define FAST_AT_BUS 0x80 +#define SLOW_PCI_TP 0x00 +#define FAST_PCI_TP 0x80 +/* +** Debug levels +*/ +#define DBG_NONE 0x00000000 + +#define DBG_INIT 0x00000001 +#define DBG_OPEN 0x00000002 +#define DBG_CLOSE 0x00000004 +#define DBG_IOCTL 0x00000008 + +#define DBG_READ 0x00000010 +#define DBG_WRITE 0x00000020 +#define DBG_INTR 0x00000040 +#define DBG_PROC 0x00000080 + +#define DBG_PARAM 0x00000100 +#define DBG_CMD 0x00000200 +#define DBG_XPRINT 0x00000400 +#define DBG_POLL 0x00000800 + +#define DBG_DAEMON 0x00001000 +#define DBG_FAIL 0x00002000 +#define DBG_MODEM 0x00004000 +#define DBG_LIST 0x00008000 + +#define DBG_ROUTE 0x00010000 +#define DBG_UTIL 0x00020000 +#define DBG_BOOT 0x00040000 +#define DBG_BUFFER 0x00080000 + +#define DBG_MON 0x00100000 +#define DBG_SPECIAL 0x00200000 +#define DBG_VPIX 0x00400000 +#define DBG_FLUSH 0x00800000 + +#define DBG_QENABLE 0x01000000 + +#define DBG_ALWAYS 0x80000000 + +#endif /* __rio_riospace_h__ */ diff --git a/drivers/staging/generic_serial/rio/riotable.c b/drivers/staging/generic_serial/rio/riotable.c new file mode 100644 index 0000000..3d15802 --- /dev/null +++ b/drivers/staging/generic_serial/rio/riotable.c @@ -0,0 +1,941 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riotable.c +** SID : 1.2 +** Last Modified : 11/6/98 10:33:47 +** Retrieved : 11/6/98 10:33:50 +** +** ident @(#)riotable.c 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/string.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "param.h" +#include "protsts.h" + +/* +** A configuration table has been loaded. It is now up to us +** to sort it out and use the information contained therein. +*/ +int RIONewTable(struct rio_info *p) +{ + int Host, Host1, Host2, NameIsUnique, Entry, SubEnt; + struct Map *MapP; + struct Map *HostMapP; + struct Host *HostP; + + char *cptr; + + /* + ** We have been sent a new table to install. We need to break + ** it down into little bits and spread it around a bit to see + ** what we have got. + */ + /* + ** Things to check: + ** (things marked 'xx' aren't checked any more!) + ** (1) That there are no booted Hosts/RTAs out there. + ** (2) That the names are properly formed + ** (3) That blank entries really are. + ** xx (4) That hosts mentioned in the table actually exist. xx + ** (5) That the IDs are unique (per host). + ** (6) That host IDs are zero + ** (7) That port numbers are valid + ** (8) That port numbers aren't duplicated + ** (9) That names aren't duplicated + ** xx (10) That hosts that actually exist are mentioned in the table. xx + */ + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(1)\n"); + if (p->RIOSystemUp) { /* (1) */ + p->RIOError.Error = HOST_HAS_ALREADY_BEEN_BOOTED; + return -EBUSY; + } + + p->RIOError.Error = NOTHING_WRONG_AT_ALL; + p->RIOError.Entry = -1; + p->RIOError.Other = -1; + + for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) { + MapP = &p->RIOConnectTable[Entry]; + if ((MapP->Flags & RTA16_SECOND_SLOT) == 0) { + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(2)\n"); + cptr = MapP->Name; /* (2) */ + cptr[MAX_NAME_LEN - 1] = '\0'; + if (cptr[0] == '\0') { + memcpy(MapP->Name, MapP->RtaUniqueNum ? "RTA NN" : "HOST NN", 8); + MapP->Name[5] = '0' + Entry / 10; + MapP->Name[6] = '0' + Entry % 10; + } + while (*cptr) { + if (*cptr < ' ' || *cptr > '~') { + p->RIOError.Error = BAD_CHARACTER_IN_NAME; + p->RIOError.Entry = Entry; + return -ENXIO; + } + cptr++; + } + } + + /* + ** If the entry saved was a tentative entry then just forget + ** about it. + */ + if (MapP->Flags & SLOT_TENTATIVE) { + MapP->HostUniqueNum = 0; + MapP->RtaUniqueNum = 0; + continue; + } + + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(3)\n"); + if (!MapP->RtaUniqueNum && !MapP->HostUniqueNum) { /* (3) */ + if (MapP->ID || MapP->SysPort || MapP->Flags) { + rio_dprintk(RIO_DEBUG_TABLE, "%s pretending to be empty but isn't\n", MapP->Name); + p->RIOError.Error = TABLE_ENTRY_ISNT_PROPERLY_NULL; + p->RIOError.Entry = Entry; + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_TABLE, "!RIO: Daemon: test (3) passes\n"); + continue; + } + + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(4)\n"); + for (Host = 0; Host < p->RIONumHosts; Host++) { /* (4) */ + if (p->RIOHosts[Host].UniqueNum == MapP->HostUniqueNum) { + HostP = &p->RIOHosts[Host]; + /* + ** having done the lookup, we don't really want to do + ** it again, so hang the host number in a safe place + */ + MapP->Topology[0].Unit = Host; + break; + } + } + + if (Host >= p->RIONumHosts) { + rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has unknown host unique number 0x%x\n", MapP->Name, MapP->HostUniqueNum); + MapP->HostUniqueNum = 0; + /* MapP->RtaUniqueNum = 0; */ + /* MapP->ID = 0; */ + /* MapP->Flags = 0; */ + /* MapP->SysPort = 0; */ + /* MapP->Name[0] = 0; */ + continue; + } + + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(5)\n"); + if (MapP->RtaUniqueNum) { /* (5) */ + if (!MapP->ID) { + rio_dprintk(RIO_DEBUG_TABLE, "RIO: RTA %s has been allocated an ID of zero!\n", MapP->Name); + p->RIOError.Error = ZERO_RTA_ID; + p->RIOError.Entry = Entry; + return -ENXIO; + } + if (MapP->ID > MAX_RUP) { + rio_dprintk(RIO_DEBUG_TABLE, "RIO: RTA %s has been allocated an invalid ID %d\n", MapP->Name, MapP->ID); + p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE; + p->RIOError.Entry = Entry; + return -ENXIO; + } + for (SubEnt = 0; SubEnt < Entry; SubEnt++) { + if (MapP->HostUniqueNum == p->RIOConnectTable[SubEnt].HostUniqueNum && MapP->ID == p->RIOConnectTable[SubEnt].ID) { + rio_dprintk(RIO_DEBUG_TABLE, "Dupl. ID number allocated to RTA %s and RTA %s\n", MapP->Name, p->RIOConnectTable[SubEnt].Name); + p->RIOError.Error = DUPLICATED_RTA_ID; + p->RIOError.Entry = Entry; + p->RIOError.Other = SubEnt; + return -ENXIO; + } + /* + ** If the RtaUniqueNum is the same, it may be looking at both + ** entries for a 16 port RTA, so check the ids + */ + if ((MapP->RtaUniqueNum == p->RIOConnectTable[SubEnt].RtaUniqueNum) + && (MapP->ID2 != p->RIOConnectTable[SubEnt].ID)) { + rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has duplicate unique number\n", MapP->Name); + rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has duplicate unique number\n", p->RIOConnectTable[SubEnt].Name); + p->RIOError.Error = DUPLICATE_UNIQUE_NUMBER; + p->RIOError.Entry = Entry; + p->RIOError.Other = SubEnt; + return -ENXIO; + } + } + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(7a)\n"); + /* (7a) */ + if ((MapP->SysPort != NO_PORT) && (MapP->SysPort % PORTS_PER_RTA)) { + rio_dprintk(RIO_DEBUG_TABLE, "TTY Port number %d-RTA %s is not a multiple of %d!\n", (int) MapP->SysPort, MapP->Name, PORTS_PER_RTA); + p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE; + p->RIOError.Entry = Entry; + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(7b)\n"); + /* (7b) */ + if ((MapP->SysPort != NO_PORT) && (MapP->SysPort >= RIO_PORTS)) { + rio_dprintk(RIO_DEBUG_TABLE, "TTY Port number %d for RTA %s is too big\n", (int) MapP->SysPort, MapP->Name); + p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE; + p->RIOError.Entry = Entry; + return -ENXIO; + } + for (SubEnt = 0; SubEnt < Entry; SubEnt++) { + if (p->RIOConnectTable[SubEnt].Flags & RTA16_SECOND_SLOT) + continue; + if (p->RIOConnectTable[SubEnt].RtaUniqueNum) { + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(8)\n"); + /* (8) */ + if ((MapP->SysPort != NO_PORT) && (MapP->SysPort == p->RIOConnectTable[SubEnt].SysPort)) { + rio_dprintk(RIO_DEBUG_TABLE, "RTA %s:same TTY port # as RTA %s (%d)\n", MapP->Name, p->RIOConnectTable[SubEnt].Name, (int) MapP->SysPort); + p->RIOError.Error = TTY_NUMBER_IN_USE; + p->RIOError.Entry = Entry; + p->RIOError.Other = SubEnt; + return -ENXIO; + } + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(9)\n"); + if (strcmp(MapP->Name, p->RIOConnectTable[SubEnt].Name) == 0 && !(MapP->Flags & RTA16_SECOND_SLOT)) { /* (9) */ + rio_dprintk(RIO_DEBUG_TABLE, "RTA name %s used twice\n", MapP->Name); + p->RIOError.Error = NAME_USED_TWICE; + p->RIOError.Entry = Entry; + p->RIOError.Other = SubEnt; + return -ENXIO; + } + } + } + } else { /* (6) */ + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(6)\n"); + if (MapP->ID) { + rio_dprintk(RIO_DEBUG_TABLE, "RIO:HOST %s has been allocated ID that isn't zero!\n", MapP->Name); + p->RIOError.Error = HOST_ID_NOT_ZERO; + p->RIOError.Entry = Entry; + return -ENXIO; + } + if (MapP->SysPort != NO_PORT) { + rio_dprintk(RIO_DEBUG_TABLE, "RIO: HOST %s has been allocated port numbers!\n", MapP->Name); + p->RIOError.Error = HOST_SYSPORT_BAD; + p->RIOError.Entry = Entry; + return -ENXIO; + } + } + } + + /* + ** wow! if we get here then it's a goody! + */ + + /* + ** Zero the (old) entries for each host... + */ + for (Host = 0; Host < RIO_HOSTS; Host++) { + for (Entry = 0; Entry < MAX_RUP; Entry++) { + memset(&p->RIOHosts[Host].Mapping[Entry], 0, sizeof(struct Map)); + } + memset(&p->RIOHosts[Host].Name[0], 0, sizeof(p->RIOHosts[Host].Name)); + } + + /* + ** Copy in the new table entries + */ + for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) { + rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: Copy table for Host entry %d\n", Entry); + MapP = &p->RIOConnectTable[Entry]; + + /* + ** Now, if it is an empty slot ignore it! + */ + if (MapP->HostUniqueNum == 0) + continue; + + /* + ** we saved the host number earlier, so grab it back + */ + HostP = &p->RIOHosts[MapP->Topology[0].Unit]; + + /* + ** If it is a host, then we only need to fill in the name field. + */ + if (MapP->ID == 0) { + rio_dprintk(RIO_DEBUG_TABLE, "Host entry found. Name %s\n", MapP->Name); + memcpy(HostP->Name, MapP->Name, MAX_NAME_LEN); + continue; + } + + /* + ** Its an RTA entry, so fill in the host mapping entries for it + ** and the port mapping entries. Notice that entry zero is for + ** ID one. + */ + HostMapP = &HostP->Mapping[MapP->ID - 1]; + + if (MapP->Flags & SLOT_IN_USE) { + rio_dprintk(RIO_DEBUG_TABLE, "Rta entry found. Name %s\n", MapP->Name); + /* + ** structure assign, then sort out the bits we shouldn't have done + */ + *HostMapP = *MapP; + + HostMapP->Flags = SLOT_IN_USE; + if (MapP->Flags & RTA16_SECOND_SLOT) + HostMapP->Flags |= RTA16_SECOND_SLOT; + + RIOReMapPorts(p, HostP, HostMapP); + } else { + rio_dprintk(RIO_DEBUG_TABLE, "TENTATIVE Rta entry found. Name %s\n", MapP->Name); + } + } + + for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) { + p->RIOSavedTable[Entry] = p->RIOConnectTable[Entry]; + } + + for (Host = 0; Host < p->RIONumHosts; Host++) { + for (SubEnt = 0; SubEnt < LINKS_PER_UNIT; SubEnt++) { + p->RIOHosts[Host].Topology[SubEnt].Unit = ROUTE_DISCONNECT; + p->RIOHosts[Host].Topology[SubEnt].Link = NO_LINK; + } + for (Entry = 0; Entry < MAX_RUP; Entry++) { + for (SubEnt = 0; SubEnt < LINKS_PER_UNIT; SubEnt++) { + p->RIOHosts[Host].Mapping[Entry].Topology[SubEnt].Unit = ROUTE_DISCONNECT; + p->RIOHosts[Host].Mapping[Entry].Topology[SubEnt].Link = NO_LINK; + } + } + if (!p->RIOHosts[Host].Name[0]) { + memcpy(p->RIOHosts[Host].Name, "HOST 1", 7); + p->RIOHosts[Host].Name[5] += Host; + } + /* + ** Check that default name assigned is unique. + */ + Host1 = Host; + NameIsUnique = 0; + while (!NameIsUnique) { + NameIsUnique = 1; + for (Host2 = 0; Host2 < p->RIONumHosts; Host2++) { + if (Host2 == Host) + continue; + if (strcmp(p->RIOHosts[Host].Name, p->RIOHosts[Host2].Name) + == 0) { + NameIsUnique = 0; + Host1++; + if (Host1 >= p->RIONumHosts) + Host1 = 0; + p->RIOHosts[Host].Name[5] = '1' + Host1; + } + } + } + /* + ** Rename host if name already used. + */ + if (Host1 != Host) { + rio_dprintk(RIO_DEBUG_TABLE, "Default name %s already used\n", p->RIOHosts[Host].Name); + memcpy(p->RIOHosts[Host].Name, "HOST 1", 7); + p->RIOHosts[Host].Name[5] += Host1; + } + rio_dprintk(RIO_DEBUG_TABLE, "Assigning default name %s\n", p->RIOHosts[Host].Name); + } + return 0; +} + +/* +** User process needs the config table - build it from first +** principles. +** +* FIXME: SMP locking +*/ +int RIOApel(struct rio_info *p) +{ + int Host; + int link; + int Rup; + int Next = 0; + struct Map *MapP; + struct Host *HostP; + unsigned long flags; + + rio_dprintk(RIO_DEBUG_TABLE, "Generating a table to return to config.rio\n"); + + memset(&p->RIOConnectTable[0], 0, sizeof(struct Map) * TOTAL_MAP_ENTRIES); + + for (Host = 0; Host < RIO_HOSTS; Host++) { + rio_dprintk(RIO_DEBUG_TABLE, "Processing host %d\n", Host); + HostP = &p->RIOHosts[Host]; + rio_spin_lock_irqsave(&HostP->HostLock, flags); + + MapP = &p->RIOConnectTable[Next++]; + MapP->HostUniqueNum = HostP->UniqueNum; + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) { + rio_spin_unlock_irqrestore(&HostP->HostLock, flags); + continue; + } + MapP->RtaUniqueNum = 0; + MapP->ID = 0; + MapP->Flags = SLOT_IN_USE; + MapP->SysPort = NO_PORT; + for (link = 0; link < LINKS_PER_UNIT; link++) + MapP->Topology[link] = HostP->Topology[link]; + memcpy(MapP->Name, HostP->Name, MAX_NAME_LEN); + for (Rup = 0; Rup < MAX_RUP; Rup++) { + if (HostP->Mapping[Rup].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) { + p->RIOConnectTable[Next] = HostP->Mapping[Rup]; + if (HostP->Mapping[Rup].Flags & SLOT_IN_USE) + p->RIOConnectTable[Next].Flags |= SLOT_IN_USE; + if (HostP->Mapping[Rup].Flags & SLOT_TENTATIVE) + p->RIOConnectTable[Next].Flags |= SLOT_TENTATIVE; + if (HostP->Mapping[Rup].Flags & RTA16_SECOND_SLOT) + p->RIOConnectTable[Next].Flags |= RTA16_SECOND_SLOT; + Next++; + } + } + rio_spin_unlock_irqrestore(&HostP->HostLock, flags); + } + return 0; +} + +/* +** config.rio has taken a dislike to one of the gross maps entries. +** if the entry is suitably inactive, then we can gob on it and remove +** it from the table. +*/ +int RIODeleteRta(struct rio_info *p, struct Map *MapP) +{ + int host, entry, port, link; + int SysPort; + struct Host *HostP; + struct Map *HostMapP; + struct Port *PortP; + int work_done = 0; + unsigned long lock_flags, sem_flags; + + rio_dprintk(RIO_DEBUG_TABLE, "Delete entry on host %x, rta %x\n", MapP->HostUniqueNum, MapP->RtaUniqueNum); + + for (host = 0; host < p->RIONumHosts; host++) { + HostP = &p->RIOHosts[host]; + + rio_spin_lock_irqsave(&HostP->HostLock, lock_flags); + + if ((HostP->Flags & RUN_STATE) != RC_RUNNING) { + rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags); + continue; + } + + for (entry = 0; entry < MAX_RUP; entry++) { + if (MapP->RtaUniqueNum == HostP->Mapping[entry].RtaUniqueNum) { + HostMapP = &HostP->Mapping[entry]; + rio_dprintk(RIO_DEBUG_TABLE, "Found entry offset %d on host %s\n", entry, HostP->Name); + + /* + ** Check all four links of the unit are disconnected + */ + for (link = 0; link < LINKS_PER_UNIT; link++) { + if (HostMapP->Topology[link].Unit != ROUTE_DISCONNECT) { + rio_dprintk(RIO_DEBUG_TABLE, "Entry is in use and cannot be deleted!\n"); + p->RIOError.Error = UNIT_IS_IN_USE; + rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags); + return -EBUSY; + } + } + /* + ** Slot has been allocated, BUT not booted/routed/ + ** connected/selected or anything else-ed + */ + SysPort = HostMapP->SysPort; + + if (SysPort != NO_PORT) { + for (port = SysPort; port < SysPort + PORTS_PER_RTA; port++) { + PortP = p->RIOPortp[port]; + rio_dprintk(RIO_DEBUG_TABLE, "Unmap port\n"); + + rio_spin_lock_irqsave(&PortP->portSem, sem_flags); + + PortP->Mapped = 0; + + if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) { + + rio_dprintk(RIO_DEBUG_TABLE, "Gob on port\n"); + PortP->TxBufferIn = PortP->TxBufferOut = 0; + /* What should I do + wakeup( &PortP->TxBufferIn ); + wakeup( &PortP->TxBufferOut); + */ + PortP->InUse = NOT_INUSE; + /* What should I do + wakeup( &PortP->InUse ); + signal(PortP->TtyP->t_pgrp,SIGKILL); + ttyflush(PortP->TtyP,(FREAD|FWRITE)); + */ + PortP->State |= RIO_CLOSING | RIO_DELETED; + } + + /* + ** For the second slot of a 16 port RTA, the + ** driver needs to reset the changes made to + ** the phb to port mappings in RIORouteRup. + */ + if (PortP->SecondBlock) { + u16 dest_unit = HostMapP->ID; + u16 dest_port = port - SysPort; + u16 __iomem *TxPktP; + struct PKT __iomem *Pkt; + + for (TxPktP = PortP->TxStart; TxPktP <= PortP->TxEnd; TxPktP++) { + /* + ** *TxPktP is the pointer to the + ** transmit packet on the host card. + ** This needs to be translated into + ** a 32 bit pointer so it can be + ** accessed from the driver. + */ + Pkt = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(&*TxPktP)); + rio_dprintk(RIO_DEBUG_TABLE, "Tx packet (%x) destination: Old %x:%x New %x:%x\n", readw(TxPktP), readb(&Pkt->dest_unit), readb(&Pkt->dest_port), dest_unit, dest_port); + writew(dest_unit, &Pkt->dest_unit); + writew(dest_port, &Pkt->dest_port); + } + rio_dprintk(RIO_DEBUG_TABLE, "Port %d phb destination: Old %x:%x New %x:%x\n", port, readb(&PortP->PhbP->destination) & 0xff, (readb(&PortP->PhbP->destination) >> 8) & 0xff, dest_unit, dest_port); + writew(dest_unit + (dest_port << 8), &PortP->PhbP->destination); + } + rio_spin_unlock_irqrestore(&PortP->portSem, sem_flags); + } + } + rio_dprintk(RIO_DEBUG_TABLE, "Entry nulled.\n"); + memset(HostMapP, 0, sizeof(struct Map)); + work_done++; + } + } + rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags); + } + + /* XXXXX lock me up */ + for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) { + if (p->RIOSavedTable[entry].RtaUniqueNum == MapP->RtaUniqueNum) { + memset(&p->RIOSavedTable[entry], 0, sizeof(struct Map)); + work_done++; + } + if (p->RIOConnectTable[entry].RtaUniqueNum == MapP->RtaUniqueNum) { + memset(&p->RIOConnectTable[entry], 0, sizeof(struct Map)); + work_done++; + } + } + if (work_done) + return 0; + + rio_dprintk(RIO_DEBUG_TABLE, "Couldn't find entry to be deleted\n"); + p->RIOError.Error = COULDNT_FIND_ENTRY; + return -ENXIO; +} + +int RIOAssignRta(struct rio_info *p, struct Map *MapP) +{ + int host; + struct Map *HostMapP; + char *sptr; + int link; + + + rio_dprintk(RIO_DEBUG_TABLE, "Assign entry on host %x, rta %x, ID %d, Sysport %d\n", MapP->HostUniqueNum, MapP->RtaUniqueNum, MapP->ID, (int) MapP->SysPort); + + if ((MapP->ID != (u16) - 1) && ((int) MapP->ID < (int) 1 || (int) MapP->ID > MAX_RUP)) { + rio_dprintk(RIO_DEBUG_TABLE, "Bad ID in map entry!\n"); + p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + if (MapP->RtaUniqueNum == 0) { + rio_dprintk(RIO_DEBUG_TABLE, "Rta Unique number zero!\n"); + p->RIOError.Error = RTA_UNIQUE_NUMBER_ZERO; + return -EINVAL; + } + if ((MapP->SysPort != NO_PORT) && (MapP->SysPort % PORTS_PER_RTA)) { + rio_dprintk(RIO_DEBUG_TABLE, "Port %d not multiple of %d!\n", (int) MapP->SysPort, PORTS_PER_RTA); + p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + if ((MapP->SysPort != NO_PORT) && (MapP->SysPort >= RIO_PORTS)) { + rio_dprintk(RIO_DEBUG_TABLE, "Port %d not valid!\n", (int) MapP->SysPort); + p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + /* + ** Copy the name across to the map entry. + */ + MapP->Name[MAX_NAME_LEN - 1] = '\0'; + sptr = MapP->Name; + while (*sptr) { + if (*sptr < ' ' || *sptr > '~') { + rio_dprintk(RIO_DEBUG_TABLE, "Name entry contains non-printing characters!\n"); + p->RIOError.Error = BAD_CHARACTER_IN_NAME; + return -EINVAL; + } + sptr++; + } + + for (host = 0; host < p->RIONumHosts; host++) { + if (MapP->HostUniqueNum == p->RIOHosts[host].UniqueNum) { + if ((p->RIOHosts[host].Flags & RUN_STATE) != RC_RUNNING) { + p->RIOError.Error = HOST_NOT_RUNNING; + return -ENXIO; + } + + /* + ** Now we have a host we need to allocate an ID + ** if the entry does not already have one. + */ + if (MapP->ID == (u16) - 1) { + int nNewID; + + rio_dprintk(RIO_DEBUG_TABLE, "Attempting to get a new ID for rta \"%s\"\n", MapP->Name); + /* + ** The idea here is to allow RTA's to be assigned + ** before they actually appear on the network. + ** This allows the addition of RTA's without having + ** to plug them in. + ** What we do is: + ** - Find a free ID and allocate it to the RTA. + ** - If this map entry is the second half of a + ** 16 port entry then find the other half and + ** make sure the 2 cross reference each other. + */ + if (RIOFindFreeID(p, &p->RIOHosts[host], &nNewID, NULL) != 0) { + p->RIOError.Error = COULDNT_FIND_ENTRY; + return -EBUSY; + } + MapP->ID = (u16) nNewID + 1; + rio_dprintk(RIO_DEBUG_TABLE, "Allocated ID %d for this new RTA.\n", MapP->ID); + HostMapP = &p->RIOHosts[host].Mapping[nNewID]; + HostMapP->RtaUniqueNum = MapP->RtaUniqueNum; + HostMapP->HostUniqueNum = MapP->HostUniqueNum; + HostMapP->ID = MapP->ID; + for (link = 0; link < LINKS_PER_UNIT; link++) { + HostMapP->Topology[link].Unit = ROUTE_DISCONNECT; + HostMapP->Topology[link].Link = NO_LINK; + } + if (MapP->Flags & RTA16_SECOND_SLOT) { + int unit; + + for (unit = 0; unit < MAX_RUP; unit++) + if (p->RIOHosts[host].Mapping[unit].RtaUniqueNum == MapP->RtaUniqueNum) + break; + if (unit == MAX_RUP) { + p->RIOError.Error = COULDNT_FIND_ENTRY; + return -EBUSY; + } + HostMapP->Flags |= RTA16_SECOND_SLOT; + HostMapP->ID2 = MapP->ID2 = p->RIOHosts[host].Mapping[unit].ID; + p->RIOHosts[host].Mapping[unit].ID2 = MapP->ID; + rio_dprintk(RIO_DEBUG_TABLE, "Cross referenced id %d to ID %d.\n", MapP->ID, p->RIOHosts[host].Mapping[unit].ID); + } + } + + HostMapP = &p->RIOHosts[host].Mapping[MapP->ID - 1]; + + if (HostMapP->Flags & SLOT_IN_USE) { + rio_dprintk(RIO_DEBUG_TABLE, "Map table slot for ID %d is already in use.\n", MapP->ID); + p->RIOError.Error = ID_ALREADY_IN_USE; + return -EBUSY; + } + + /* + ** Assign the sys ports and the name, and mark the slot as + ** being in use. + */ + HostMapP->SysPort = MapP->SysPort; + if ((MapP->Flags & RTA16_SECOND_SLOT) == 0) + memcpy(HostMapP->Name, MapP->Name, MAX_NAME_LEN); + HostMapP->Flags = SLOT_IN_USE | RTA_BOOTED; +#ifdef NEED_TO_FIX + RIO_SV_BROADCAST(p->RIOHosts[host].svFlags[MapP->ID - 1]); +#endif + if (MapP->Flags & RTA16_SECOND_SLOT) + HostMapP->Flags |= RTA16_SECOND_SLOT; + + RIOReMapPorts(p, &p->RIOHosts[host], HostMapP); + /* + ** Adjust 2nd block of 8 phbs + */ + if (MapP->Flags & RTA16_SECOND_SLOT) + RIOFixPhbs(p, &p->RIOHosts[host], HostMapP->ID - 1); + + if (HostMapP->SysPort != NO_PORT) { + if (HostMapP->SysPort < p->RIOFirstPortsBooted) + p->RIOFirstPortsBooted = HostMapP->SysPort; + if (HostMapP->SysPort > p->RIOLastPortsBooted) + p->RIOLastPortsBooted = HostMapP->SysPort; + } + if (MapP->Flags & RTA16_SECOND_SLOT) + rio_dprintk(RIO_DEBUG_TABLE, "Second map of RTA %s added to configuration\n", p->RIOHosts[host].Mapping[MapP->ID2 - 1].Name); + else + rio_dprintk(RIO_DEBUG_TABLE, "RTA %s added to configuration\n", MapP->Name); + return 0; + } + } + p->RIOError.Error = UNKNOWN_HOST_NUMBER; + rio_dprintk(RIO_DEBUG_TABLE, "Unknown host %x\n", MapP->HostUniqueNum); + return -ENXIO; +} + + +int RIOReMapPorts(struct rio_info *p, struct Host *HostP, struct Map *HostMapP) +{ + struct Port *PortP; + unsigned int SubEnt; + unsigned int HostPort; + unsigned int SysPort; + u16 RtaType; + unsigned long flags; + + rio_dprintk(RIO_DEBUG_TABLE, "Mapping sysport %d to id %d\n", (int) HostMapP->SysPort, HostMapP->ID); + + /* + ** We need to tell the UnixRups which sysport the rup corresponds to + */ + HostP->UnixRups[HostMapP->ID - 1].BaseSysPort = HostMapP->SysPort; + + if (HostMapP->SysPort == NO_PORT) + return (0); + + RtaType = GetUnitType(HostMapP->RtaUniqueNum); + rio_dprintk(RIO_DEBUG_TABLE, "Mapping sysport %d-%d\n", (int) HostMapP->SysPort, (int) HostMapP->SysPort + PORTS_PER_RTA - 1); + + /* + ** now map each of its eight ports + */ + for (SubEnt = 0; SubEnt < PORTS_PER_RTA; SubEnt++) { + rio_dprintk(RIO_DEBUG_TABLE, "subent = %d, HostMapP->SysPort = %d\n", SubEnt, (int) HostMapP->SysPort); + SysPort = HostMapP->SysPort + SubEnt; /* portnumber within system */ + /* portnumber on host */ + + HostPort = (HostMapP->ID - 1) * PORTS_PER_RTA + SubEnt; + + rio_dprintk(RIO_DEBUG_TABLE, "c1 p = %p, p->rioPortp = %p\n", p, p->RIOPortp); + PortP = p->RIOPortp[SysPort]; + rio_dprintk(RIO_DEBUG_TABLE, "Map port\n"); + + /* + ** Point at all the real neat data structures + */ + rio_spin_lock_irqsave(&PortP->portSem, flags); + PortP->HostP = HostP; + PortP->Caddr = HostP->Caddr; + + /* + ** The PhbP cannot be filled in yet + ** unless the host has been booted + */ + if ((HostP->Flags & RUN_STATE) == RC_RUNNING) { + struct PHB __iomem *PhbP = PortP->PhbP = &HostP->PhbP[HostPort]; + PortP->TxAdd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_add)); + PortP->TxStart = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_start)); + PortP->TxEnd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_end)); + PortP->RxRemove = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_remove)); + PortP->RxStart = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_start)); + PortP->RxEnd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_end)); + } else + PortP->PhbP = NULL; + + /* + ** port related flags + */ + PortP->HostPort = HostPort; + /* + ** For each part of a 16 port RTA, RupNum is ID - 1. + */ + PortP->RupNum = HostMapP->ID - 1; + if (HostMapP->Flags & RTA16_SECOND_SLOT) { + PortP->ID2 = HostMapP->ID2 - 1; + PortP->SecondBlock = 1; + } else { + PortP->ID2 = 0; + PortP->SecondBlock = 0; + } + PortP->RtaUniqueNum = HostMapP->RtaUniqueNum; + + /* + ** If the port was already mapped then thats all we need to do. + */ + if (PortP->Mapped) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + continue; + } else + HostMapP->Flags &= ~RTA_NEWBOOT; + + PortP->State = 0; + PortP->Config = 0; + /* + ** Check out the module type - if it is special (read only etc.) + ** then we need to set flags in the PortP->Config. + ** Note: For 16 port RTA, all ports are of the same type. + */ + if (RtaType == TYPE_RTA16) { + PortP->Config |= p->RIOModuleTypes[HostP->UnixRups[HostMapP->ID - 1].ModTypes].Flags[SubEnt % PORTS_PER_MODULE]; + } else { + if (SubEnt < PORTS_PER_MODULE) + PortP->Config |= p->RIOModuleTypes[LONYBLE(HostP->UnixRups[HostMapP->ID - 1].ModTypes)].Flags[SubEnt % PORTS_PER_MODULE]; + else + PortP->Config |= p->RIOModuleTypes[HINYBLE(HostP->UnixRups[HostMapP->ID - 1].ModTypes)].Flags[SubEnt % PORTS_PER_MODULE]; + } + + /* + ** more port related flags + */ + PortP->PortState = 0; + PortP->ModemLines = 0; + PortP->ModemState = 0; + PortP->CookMode = COOK_WELL; + PortP->ParamSem = 0; + PortP->FlushCmdBodge = 0; + PortP->WflushFlag = 0; + PortP->MagicFlags = 0; + PortP->Lock = 0; + PortP->Store = 0; + PortP->FirstOpen = 1; + + /* + ** Buffers 'n things + */ + PortP->RxDataStart = 0; + PortP->Cor2Copy = 0; + PortP->Name = &HostMapP->Name[0]; + PortP->statsGather = 0; + PortP->txchars = 0; + PortP->rxchars = 0; + PortP->opens = 0; + PortP->closes = 0; + PortP->ioctls = 0; + if (PortP->TxRingBuffer) + memset(PortP->TxRingBuffer, 0, p->RIOBufferSize); + else if (p->RIOBufferSize) { + PortP->TxRingBuffer = kzalloc(p->RIOBufferSize, GFP_KERNEL); + } + PortP->TxBufferOut = 0; + PortP->TxBufferIn = 0; + PortP->Debug = 0; + /* + ** LastRxTgl stores the state of the rx toggle bit for this + ** port, to be compared with the state of the next pkt received. + ** If the same, we have received the same rx pkt from the RTA + ** twice. Initialise to a value not equal to PHB_RX_TGL or 0. + */ + PortP->LastRxTgl = ~(u8) PHB_RX_TGL; + + /* + ** and mark the port as usable + */ + PortP->Mapped = 1; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + } + if (HostMapP->SysPort < p->RIOFirstPortsMapped) + p->RIOFirstPortsMapped = HostMapP->SysPort; + if (HostMapP->SysPort > p->RIOLastPortsMapped) + p->RIOLastPortsMapped = HostMapP->SysPort; + + return 0; +} + +int RIOChangeName(struct rio_info *p, struct Map *MapP) +{ + int host; + struct Map *HostMapP; + char *sptr; + + rio_dprintk(RIO_DEBUG_TABLE, "Change name entry on host %x, rta %x, ID %d, Sysport %d\n", MapP->HostUniqueNum, MapP->RtaUniqueNum, MapP->ID, (int) MapP->SysPort); + + if (MapP->ID > MAX_RUP) { + rio_dprintk(RIO_DEBUG_TABLE, "Bad ID in map entry!\n"); + p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE; + return -EINVAL; + } + + MapP->Name[MAX_NAME_LEN - 1] = '\0'; + sptr = MapP->Name; + + while (*sptr) { + if (*sptr < ' ' || *sptr > '~') { + rio_dprintk(RIO_DEBUG_TABLE, "Name entry contains non-printing characters!\n"); + p->RIOError.Error = BAD_CHARACTER_IN_NAME; + return -EINVAL; + } + sptr++; + } + + for (host = 0; host < p->RIONumHosts; host++) { + if (MapP->HostUniqueNum == p->RIOHosts[host].UniqueNum) { + if ((p->RIOHosts[host].Flags & RUN_STATE) != RC_RUNNING) { + p->RIOError.Error = HOST_NOT_RUNNING; + return -ENXIO; + } + if (MapP->ID == 0) { + memcpy(p->RIOHosts[host].Name, MapP->Name, MAX_NAME_LEN); + return 0; + } + + HostMapP = &p->RIOHosts[host].Mapping[MapP->ID - 1]; + + if (HostMapP->RtaUniqueNum != MapP->RtaUniqueNum) { + p->RIOError.Error = RTA_NUMBER_WRONG; + return -ENXIO; + } + memcpy(HostMapP->Name, MapP->Name, MAX_NAME_LEN); + return 0; + } + } + p->RIOError.Error = UNKNOWN_HOST_NUMBER; + rio_dprintk(RIO_DEBUG_TABLE, "Unknown host %x\n", MapP->HostUniqueNum); + return -ENXIO; +} diff --git a/drivers/staging/generic_serial/rio/riotty.c b/drivers/staging/generic_serial/rio/riotty.c new file mode 100644 index 0000000..8a90393 --- /dev/null +++ b/drivers/staging/generic_serial/rio/riotty.c @@ -0,0 +1,654 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : riotty.c +** SID : 1.3 +** Last Modified : 11/6/98 10:33:47 +** Retrieved : 11/6/98 10:33:50 +** +** ident @(#)riotty.c 1.3 +** +** ----------------------------------------------------------------------------- +*/ + +#define __EXPLICIT_DEF_H__ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/string.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/string.h> +#include <asm/uaccess.h> + +#include <linux/termios.h> + +#include <linux/serial.h> + +#include <linux/generic_serial.h> + + +#include "linux_compat.h" +#include "rio_linux.h" +#include "pkt.h" +#include "daemon.h" +#include "rio.h" +#include "riospace.h" +#include "cmdpkt.h" +#include "map.h" +#include "rup.h" +#include "port.h" +#include "riodrvr.h" +#include "rioinfo.h" +#include "func.h" +#include "errors.h" +#include "pci.h" + +#include "parmmap.h" +#include "unixrup.h" +#include "board.h" +#include "host.h" +#include "phb.h" +#include "link.h" +#include "cmdblk.h" +#include "route.h" +#include "cirrus.h" +#include "rioioctl.h" +#include "param.h" + +static void RIOClearUp(struct Port *PortP); + +/* Below belongs in func.h */ +int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg); + + +extern struct rio_info *p; + + +int riotopen(struct tty_struct *tty, struct file *filp) +{ + unsigned int SysPort; + int repeat_this = 250; + struct Port *PortP; /* pointer to the port structure */ + unsigned long flags; + int retval = 0; + + func_enter(); + + /* Make sure driver_data is NULL in case the rio isn't booted jet. Else gs_close + is going to oops. + */ + tty->driver_data = NULL; + + SysPort = rio_minor(tty); + + if (p->RIOFailed) { + rio_dprintk(RIO_DEBUG_TTY, "System initialisation failed\n"); + func_exit(); + return -ENXIO; + } + + rio_dprintk(RIO_DEBUG_TTY, "port open SysPort %d (mapped:%d)\n", SysPort, p->RIOPortp[SysPort]->Mapped); + + /* + ** Validate that we have received a legitimate request. + ** Currently, just check that we are opening a port on + ** a host card that actually exists, and that the port + ** has been mapped onto a host. + */ + if (SysPort >= RIO_PORTS) { /* out of range ? */ + rio_dprintk(RIO_DEBUG_TTY, "Illegal port number %d\n", SysPort); + func_exit(); + return -ENXIO; + } + + /* + ** Grab pointer to the port stucture + */ + PortP = p->RIOPortp[SysPort]; /* Get control struc */ + rio_dprintk(RIO_DEBUG_TTY, "PortP: %p\n", PortP); + if (!PortP->Mapped) { /* we aren't mapped yet! */ + /* + ** The system doesn't know which RTA this port + ** corresponds to. + */ + rio_dprintk(RIO_DEBUG_TTY, "port not mapped into system\n"); + func_exit(); + return -ENXIO; + } + + tty->driver_data = PortP; + + PortP->gs.port.tty = tty; + PortP->gs.port.count++; + + rio_dprintk(RIO_DEBUG_TTY, "%d bytes in tx buffer\n", PortP->gs.xmit_cnt); + + retval = gs_init_port(&PortP->gs); + if (retval) { + PortP->gs.port.count--; + return -ENXIO; + } + /* + ** If the host hasn't been booted yet, then + ** fail + */ + if ((PortP->HostP->Flags & RUN_STATE) != RC_RUNNING) { + rio_dprintk(RIO_DEBUG_TTY, "Host not running\n"); + func_exit(); + return -ENXIO; + } + + /* + ** If the RTA has not booted yet and the user has choosen to block + ** until the RTA is present then we must spin here waiting for + ** the RTA to boot. + */ + /* I find the above code a bit hairy. I find the below code + easier to read and shorter. Now, if it works too that would + be great... -- REW + */ + rio_dprintk(RIO_DEBUG_TTY, "Checking if RTA has booted... \n"); + while (!(PortP->HostP->Mapping[PortP->RupNum].Flags & RTA_BOOTED)) { + if (!PortP->WaitUntilBooted) { + rio_dprintk(RIO_DEBUG_TTY, "RTA never booted\n"); + func_exit(); + return -ENXIO; + } + + /* Under Linux you'd normally use a wait instead of this + busy-waiting. I'll stick with the old implementation for + now. --REW + */ + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_TTY, "RTA_wait_for_boot: EINTR in delay \n"); + func_exit(); + return -EINTR; + } + if (repeat_this-- <= 0) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for RTA to boot timeout\n"); + func_exit(); + return -EIO; + } + } + rio_dprintk(RIO_DEBUG_TTY, "RTA has been booted\n"); + rio_spin_lock_irqsave(&PortP->portSem, flags); + if (p->RIOHalted) { + goto bombout; + } + + /* + ** If the port is in the final throws of being closed, + ** we should wait here (politely), waiting + ** for it to finish, so that it doesn't close us! + */ + while ((PortP->State & RIO_CLOSING) && !p->RIOHalted) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for RIO_CLOSING to go away\n"); + if (repeat_this-- <= 0) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n"); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + retval = -EINTR; + goto bombout; + } + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_spin_lock_irqsave(&PortP->portSem, flags); + retval = -EINTR; + goto bombout; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + + if (!PortP->Mapped) { + rio_dprintk(RIO_DEBUG_TTY, "Port unmapped while closing!\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + retval = -ENXIO; + func_exit(); + return retval; + } + + if (p->RIOHalted) { + goto bombout; + } + +/* +** 15.10.1998 ARG - ESIL 0761 part fix +** RIO has it's own CTSFLOW and RTSFLOW flags in 'Config' in the port structure, +** we need to make sure that the flags are clear when the port is opened. +*/ + /* Uh? Suppose I turn these on and then another process opens + the port again? The flags get cleared! Not good. -- REW */ + if (!(PortP->State & (RIO_LOPEN | RIO_MOPEN))) { + PortP->Config &= ~(RIO_CTSFLOW | RIO_RTSFLOW); + } + + if (!(PortP->firstOpen)) { /* First time ? */ + rio_dprintk(RIO_DEBUG_TTY, "First open for this port\n"); + + + PortP->firstOpen++; + PortP->CookMode = 0; /* XXX RIOCookMode(tp); */ + PortP->InUse = NOT_INUSE; + + /* Tentative fix for bug PR27. Didn't work. */ + /* PortP->gs.xmit_cnt = 0; */ + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + + /* Someone explain to me why this delay/config is + here. If I read the docs correctly the "open" + command piggybacks the parameters immediately. + -- REW */ + RIOParam(PortP, RIOC_OPEN, 1, OK_TO_SLEEP); /* Open the port */ + rio_spin_lock_irqsave(&PortP->portSem, flags); + + /* + ** wait for the port to be not closed. + */ + while (!(PortP->PortState & PORT_ISOPEN) && !p->RIOHalted) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for PORT_ISOPEN-currently %x\n", PortP->PortState); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for open to finish broken by signal\n"); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + func_exit(); + return -EINTR; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + + if (p->RIOHalted) { + retval = -EIO; + bombout: + /* RIOClearUp( PortP ); */ + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return retval; + } + rio_dprintk(RIO_DEBUG_TTY, "PORT_ISOPEN found\n"); + } + rio_dprintk(RIO_DEBUG_TTY, "Modem - test for carrier\n"); + /* + ** ACTION + ** insert test for carrier here. -- ??? + ** I already see that test here. What's the deal? -- REW + */ + if ((PortP->gs.port.tty->termios->c_cflag & CLOCAL) || + (PortP->ModemState & RIOC_MSVR1_CD)) { + rio_dprintk(RIO_DEBUG_TTY, "open(%d) Modem carr on\n", SysPort); + /* + tp->tm.c_state |= CARR_ON; + wakeup((caddr_t) &tp->tm.c_canq); + */ + PortP->State |= RIO_CARR_ON; + wake_up_interruptible(&PortP->gs.port.open_wait); + } else { /* no carrier - wait for DCD */ + /* + while (!(PortP->gs.port.tty->termios->c_state & CARR_ON) && + !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted ) + */ + while (!(PortP->State & RIO_CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted) { + rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr on\n", SysPort); + /* + PortP->gs.port.tty->termios->c_state |= WOPEN; + */ + PortP->State |= RIO_WOPEN; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_spin_lock_irqsave(&PortP->portSem, flags); + /* + ** ACTION: verify that this is a good thing + ** to do here. -- ??? + ** I think it's OK. -- REW + */ + rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr broken by signal\n", SysPort); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + /* + tp->tm.c_state &= ~WOPEN; + */ + PortP->State &= ~RIO_WOPEN; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + func_exit(); + return -EINTR; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + PortP->State &= ~RIO_WOPEN; + } + if (p->RIOHalted) + goto bombout; + rio_dprintk(RIO_DEBUG_TTY, "Setting RIO_MOPEN\n"); + PortP->State |= RIO_MOPEN; + + if (p->RIOHalted) + goto bombout; + + rio_dprintk(RIO_DEBUG_TTY, "high level open done\n"); + + /* + ** Count opens for port statistics reporting + */ + if (PortP->statsGather) + PortP->opens++; + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + rio_dprintk(RIO_DEBUG_TTY, "Returning from open\n"); + func_exit(); + return 0; +} + +/* +** RIOClose the port. +** The operating system thinks that this is last close for the device. +** As there are two interfaces to the port (Modem and tty), we need to +** check that both are closed before we close the device. +*/ +int riotclose(void *ptr) +{ + struct Port *PortP = ptr; /* pointer to the port structure */ + int deleted = 0; + int try = -1; /* Disable the timeouts by setting them to -1 */ + int repeat_this = -1; /* Congrats to those having 15 years of + uptime! (You get to break the driver.) */ + unsigned long end_time; + struct tty_struct *tty; + unsigned long flags; + int rv = 0; + + rio_dprintk(RIO_DEBUG_TTY, "port close SysPort %d\n", PortP->PortNum); + + /* PortP = p->RIOPortp[SysPort]; */ + rio_dprintk(RIO_DEBUG_TTY, "Port is at address %p\n", PortP); + /* tp = PortP->TtyP; *//* Get tty */ + tty = PortP->gs.port.tty; + rio_dprintk(RIO_DEBUG_TTY, "TTY is at address %p\n", tty); + + if (PortP->gs.closing_wait) + end_time = jiffies + PortP->gs.closing_wait; + else + end_time = jiffies + MAX_SCHEDULE_TIMEOUT; + + rio_spin_lock_irqsave(&PortP->portSem, flags); + + /* + ** Setting this flag will make any process trying to open + ** this port block until we are complete closing it. + */ + PortP->State |= RIO_CLOSING; + + if ((PortP->State & RIO_DELETED)) { + rio_dprintk(RIO_DEBUG_TTY, "Close on deleted RTA\n"); + deleted = 1; + } + + if (p->RIOHalted) { + RIOClearUp(PortP); + rv = -EIO; + goto close_end; + } + + rio_dprintk(RIO_DEBUG_TTY, "Clear bits\n"); + /* + ** clear the open bits for this device + */ + PortP->State &= ~RIO_MOPEN; + PortP->State &= ~RIO_CARR_ON; + PortP->ModemState &= ~RIOC_MSVR1_CD; + /* + ** If the device was open as both a Modem and a tty line + ** then we need to wimp out here, as the port has not really + ** been finally closed (gee, whizz!) The test here uses the + ** bit for the OTHER mode of operation, to see if THAT is + ** still active! + */ + if ((PortP->State & (RIO_LOPEN | RIO_MOPEN))) { + /* + ** The port is still open for the other task - + ** return, pretending that we are still active. + */ + rio_dprintk(RIO_DEBUG_TTY, "Channel %d still open !\n", PortP->PortNum); + PortP->State &= ~RIO_CLOSING; + if (PortP->firstOpen) + PortP->firstOpen--; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return -EIO; + } + + rio_dprintk(RIO_DEBUG_TTY, "Closing down - everything must go!\n"); + + PortP->State &= ~RIO_DYNOROD; + + /* + ** This is where we wait for the port + ** to drain down before closing. Bye-bye.... + ** (We never meant to do this) + */ + rio_dprintk(RIO_DEBUG_TTY, "Timeout 1 starts\n"); + + if (!deleted) + while ((PortP->InUse != NOT_INUSE) && !p->RIOHalted && (PortP->TxBufferIn != PortP->TxBufferOut)) { + if (repeat_this-- <= 0) { + rv = -EINTR; + rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n"); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + goto close_end; + } + rio_dprintk(RIO_DEBUG_TTY, "Calling timeout to flush in closing\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (RIODelay_ni(PortP, HUNDRED_MS * 10) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_TTY, "RTA EINTR in delay \n"); + rv = -EINTR; + rio_spin_lock_irqsave(&PortP->portSem, flags); + goto close_end; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + + PortP->TxBufferIn = PortP->TxBufferOut = 0; + repeat_this = 0xff; + + PortP->InUse = 0; + if ((PortP->State & (RIO_LOPEN | RIO_MOPEN))) { + /* + ** The port has been re-opened for the other task - + ** return, pretending that we are still active. + */ + rio_dprintk(RIO_DEBUG_TTY, "Channel %d re-open!\n", PortP->PortNum); + PortP->State &= ~RIO_CLOSING; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (PortP->firstOpen) + PortP->firstOpen--; + return -EIO; + } + + if (p->RIOHalted) { + RIOClearUp(PortP); + goto close_end; + } + + /* Can't call RIOShortCommand with the port locked. */ + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + + if (RIOShortCommand(p, PortP, RIOC_CLOSE, 1, 0) == RIO_FAIL) { + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + rio_spin_lock_irqsave(&PortP->portSem, flags); + goto close_end; + } + + if (!deleted) + while (try && (PortP->PortState & PORT_ISOPEN)) { + try--; + if (time_after(jiffies, end_time)) { + rio_dprintk(RIO_DEBUG_TTY, "Run out of tries - force the bugger shut!\n"); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + break; + } + rio_dprintk(RIO_DEBUG_TTY, "Close: PortState:ISOPEN is %d\n", PortP->PortState & PORT_ISOPEN); + + if (p->RIOHalted) { + RIOClearUp(PortP); + rio_spin_lock_irqsave(&PortP->portSem, flags); + goto close_end; + } + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_dprintk(RIO_DEBUG_TTY, "RTA EINTR in delay \n"); + RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); + break; + } + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + rio_dprintk(RIO_DEBUG_TTY, "Close: try was %d on completion\n", try); + + /* RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); */ + +/* +** 15.10.1998 ARG - ESIL 0761 part fix +** RIO has it's own CTSFLOW and RTSFLOW flags in 'Config' in the port structure,** we need to make sure that the flags are clear when the port is opened. +*/ + PortP->Config &= ~(RIO_CTSFLOW | RIO_RTSFLOW); + + /* + ** Count opens for port statistics reporting + */ + if (PortP->statsGather) + PortP->closes++; + +close_end: + /* XXX: Why would a "DELETED" flag be reset here? I'd have + thought that a "deleted" flag means that the port was + permanently gone, but here we can make it reappear by it + being in close during the "deletion". + */ + PortP->State &= ~(RIO_CLOSING | RIO_DELETED); + if (PortP->firstOpen) + PortP->firstOpen--; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + rio_dprintk(RIO_DEBUG_TTY, "Return from close\n"); + return rv; +} + + + +static void RIOClearUp(struct Port *PortP) +{ + rio_dprintk(RIO_DEBUG_TTY, "RIOHalted set\n"); + PortP->Config = 0; /* Direct semaphore */ + PortP->PortState = 0; + PortP->firstOpen = 0; + PortP->FlushCmdBodge = 0; + PortP->ModemState = PortP->CookMode = 0; + PortP->Mapped = 0; + PortP->WflushFlag = 0; + PortP->MagicFlags = 0; + PortP->RxDataStart = 0; + PortP->TxBufferIn = 0; + PortP->TxBufferOut = 0; +} + +/* +** Put a command onto a port. +** The PortPointer, command, length and arg are passed. +** The len is the length *inclusive* of the command byte, +** and so for a command that takes no data, len==1. +** The arg is a single byte, and is only used if len==2. +** Other values of len aren't allowed, and will cause +** a panic. +*/ +int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg) +{ + struct PKT __iomem *PacketP; + int retries = 20; /* at 10 per second -> 2 seconds */ + unsigned long flags; + + rio_dprintk(RIO_DEBUG_TTY, "entering shortcommand.\n"); + + if (PortP->State & RIO_DELETED) { + rio_dprintk(RIO_DEBUG_TTY, "Short command to deleted RTA ignored\n"); + return RIO_FAIL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + + /* + ** If the port is in use for pre-emptive command, then wait for it to + ** be free again. + */ + while ((PortP->InUse != NOT_INUSE) && !p->RIOHalted) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting for not in use (%d)\n", retries); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (retries-- <= 0) { + return RIO_FAIL; + } + if (RIODelay_ni(PortP, HUNDRED_MS) == RIO_FAIL) { + return RIO_FAIL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + if (PortP->State & RIO_DELETED) { + rio_dprintk(RIO_DEBUG_TTY, "Short command to deleted RTA ignored\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return RIO_FAIL; + } + + while (!can_add_transmit(&PacketP, PortP) && !p->RIOHalted) { + rio_dprintk(RIO_DEBUG_TTY, "Waiting to add short command to queue (%d)\n", retries); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (retries-- <= 0) { + rio_dprintk(RIO_DEBUG_TTY, "out of tries. Failing\n"); + return RIO_FAIL; + } + if (RIODelay_ni(PortP, HUNDRED_MS) == RIO_FAIL) { + return RIO_FAIL; + } + rio_spin_lock_irqsave(&PortP->portSem, flags); + } + + if (p->RIOHalted) { + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return RIO_FAIL; + } + + /* + ** set the command byte and the argument byte + */ + writeb(command, &PacketP->data[0]); + + if (len == 2) + writeb(arg, &PacketP->data[1]); + + /* + ** set the length of the packet and set the command bit. + */ + writeb(PKT_CMD_BIT | len, &PacketP->len); + + add_transmit(PortP); + /* + ** Count characters transmitted for port statistics reporting + */ + if (PortP->statsGather) + PortP->txchars += len; + + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + return p->RIOHalted ? RIO_FAIL : ~RIO_FAIL; +} + + diff --git a/drivers/staging/generic_serial/rio/route.h b/drivers/staging/generic_serial/rio/route.h new file mode 100644 index 0000000..46e9637 --- /dev/null +++ b/drivers/staging/generic_serial/rio/route.h @@ -0,0 +1,101 @@ +/**************************************************************************** + ******* ******* + ******* R O U T E H E A D E R + ******* ******* + **************************************************************************** + + Author : Ian Nandhra / Jeremy Rolls + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _route_h +#define _route_h + +#define MAX_LINKS 4 +#define MAX_NODES 17 /* Maximum nodes in a subnet */ +#define NODE_BYTES ((MAX_NODES / 8) + 1) /* Number of bytes needed for + 1 bit per node */ +#define ROUTE_DATA_SIZE (NODE_BYTES + 2) /* Number of bytes for complete + info about cost etc. */ +#define ROUTES_PER_PACKET ((PKT_MAX_DATA_LEN -2)/ ROUTE_DATA_SIZE) + /* Number of nodes we can squeeze + into one packet */ +#define MAX_TOPOLOGY_PACKETS (MAX_NODES / ROUTES_PER_PACKET + 1) +/************************************************ + * Define the types of command for the ROUTE RUP. + ************************************************/ +#define ROUTE_REQUEST 0 /* Request an ID */ +#define ROUTE_FOAD 1 /* Kill the RTA */ +#define ROUTE_ALREADY 2 /* ID given already */ +#define ROUTE_USED 3 /* All ID's used */ +#define ROUTE_ALLOCATE 4 /* Here it is */ +#define ROUTE_REQ_TOP 5 /* I bet you didn't expect.... + the Topological Inquisition */ +#define ROUTE_TOPOLOGY 6 /* Topology request answered FD */ +/******************************************************************* + * Define the Route Map Structure + * + * The route map gives a pointer to a Link Structure to use. + * This allows Disconnected Links to be checked quickly + ******************************************************************/ +typedef struct COST_ROUTE COST_ROUTE; +struct COST_ROUTE { + unsigned char cost; /* Cost down this link */ + unsigned char route[NODE_BYTES]; /* Nodes through this route */ +}; + +typedef struct ROUTE_STR ROUTE_STR; +struct ROUTE_STR { + COST_ROUTE cost_route[MAX_LINKS]; + /* cost / route for this link */ + ushort favoured; /* favoured link */ +}; + + +#define NO_LINK (short) 5 /* Link unattached */ +#define ROUTE_NO_ID (short) 100 /* No Id */ +#define ROUTE_DISCONNECT (ushort) 0xff /* Not connected */ +#define ROUTE_INTERCONNECT (ushort) 0x40 /* Sub-net interconnect */ + + +#define SYNC_RUP (ushort) 255 +#define COMMAND_RUP (ushort) 254 +#define ERROR_RUP (ushort) 253 +#define POLL_RUP (ushort) 252 +#define BOOT_RUP (ushort) 251 +#define ROUTE_RUP (ushort) 250 +#define STATUS_RUP (ushort) 249 +#define POWER_RUP (ushort) 248 + +#define HIGHEST_RUP (ushort) 255 /* Set to Top one */ +#define LOWEST_RUP (ushort) 248 /* Set to bottom one */ + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/rup.h b/drivers/staging/generic_serial/rio/rup.h new file mode 100644 index 0000000..4ae90cb --- /dev/null +++ b/drivers/staging/generic_serial/rio/rup.h @@ -0,0 +1,69 @@ +/**************************************************************************** + ******* ******* + ******* R U P S T R U C T U R E + ******* ******* + **************************************************************************** + + Author : Ian Nandhra + Date : + + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Version : 0.01 + + + Mods + ---------------------------------------------------------------------------- + Date By Description + ---------------------------------------------------------------------------- + + ***************************************************************************/ + +#ifndef _rup_h +#define _rup_h 1 + +#define MAX_RUP ((short) 16) +#define PKTS_PER_RUP ((short) 2) /* They are always used in pairs */ + +/************************************************* + * Define all the packet request stuff + ************************************************/ +#define TX_RUP_INACTIVE 0 /* Nothing to transmit */ +#define TX_PACKET_READY 1 /* Transmit packet ready */ +#define TX_LOCK_RUP 2 /* Transmit side locked */ + +#define RX_RUP_INACTIVE 0 /* Nothing received */ +#define RX_PACKET_READY 1 /* Packet received */ + +#define RUP_NO_OWNER 0xff /* RUP not owned by any process */ + +struct RUP { + u16 txpkt; /* Outgoing packet */ + u16 rxpkt; /* Incoming packet */ + u16 link; /* Which link to send down? */ + u8 rup_dest_unit[2]; /* Destination unit */ + u16 handshake; /* For handshaking */ + u16 timeout; /* Timeout */ + u16 status; /* Status */ + u16 txcontrol; /* Transmit control */ + u16 rxcontrol; /* Receive control */ +}; + +#endif + +/*********** end of file ***********/ diff --git a/drivers/staging/generic_serial/rio/unixrup.h b/drivers/staging/generic_serial/rio/unixrup.h new file mode 100644 index 0000000..7abf0cb --- /dev/null +++ b/drivers/staging/generic_serial/rio/unixrup.h @@ -0,0 +1,51 @@ +/* +** ----------------------------------------------------------------------------- +** +** Perle Specialix driver for Linux +** Ported from existing RIO Driver for SCO sources. + * + * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +** Module : unixrup.h +** SID : 1.2 +** Last Modified : 11/6/98 11:34:20 +** Retrieved : 11/6/98 11:34:22 +** +** ident @(#)unixrup.h 1.2 +** +** ----------------------------------------------------------------------------- +*/ + +#ifndef __rio_unixrup_h__ +#define __rio_unixrup_h__ + +/* +** UnixRup data structure. This contains pointers to actual RUPs on the +** host card, and all the command/boot control stuff. +*/ +struct UnixRup { + struct CmdBlk *CmdsWaitingP; /* Commands waiting to be done */ + struct CmdBlk *CmdPendingP; /* The command currently being sent */ + struct RUP __iomem *RupP; /* the Rup to send it to */ + unsigned int Id; /* Id number */ + unsigned int BaseSysPort; /* SysPort of first tty on this RTA */ + unsigned int ModTypes; /* Modules on this RTA */ + spinlock_t RupLock; /* Lock structure for MPX */ + /* struct lockb RupLock; *//* Lock structure for MPX */ +}; + +#endif /* __rio_unixrup_h__ */ diff --git a/drivers/staging/generic_serial/ser_a2232.c b/drivers/staging/generic_serial/ser_a2232.c new file mode 100644 index 0000000..3f47c2e --- /dev/null +++ b/drivers/staging/generic_serial/ser_a2232.c @@ -0,0 +1,831 @@ +/* drivers/char/ser_a2232.c */ + +/* $Id: ser_a2232.c,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */ + +/* Linux serial driver for the Amiga A2232 board */ + +/* This driver is MAINTAINED. Before applying any changes, please contact + * the author. + */ + +/* Copyright (c) 2000-2001 Enver Haase <ehaase@inf.fu-berlin.de> + * alias The A2232 driver project <A2232@gmx.net> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/***************************** Documentation ************************/ +/* + * This driver is in EXPERIMENTAL state. That means I could not find + * someone with five A2232 boards with 35 ports running at 19200 bps + * at the same time and test the machine's behaviour. + * However, I know that you can performance-tweak this driver (see + * the source code). + * One thing to consider is the time this driver consumes during the + * Amiga's vertical blank interrupt. Everything that is to be done + * _IS DONE_ when entering the vertical blank interrupt handler of + * this driver. + * However, it would be more sane to only do the job for only ONE card + * instead of ALL cards at a time; or, more generally, to handle only + * SOME ports instead of ALL ports at a time. + * However, as long as no-one runs into problems I guess I shouldn't + * change the driver as it runs fine for me :) . + * + * Version history of this file: + * 0.4 Resolved licensing issues. + * 0.3 Inclusion in the Linux/m68k tree, small fixes. + * 0.2 Added documentation, minor typo fixes. + * 0.1 Initial release. + * + * TO DO: + * - Handle incoming BREAK events. I guess "Stevens: Advanced + * Programming in the UNIX(R) Environment" is a good reference + * on what is to be done. + * - When installing as a module, don't simply 'printk' text, but + * send it to the TTY used by the user. + * + * THANKS TO: + * - Jukka Marin (65EC02 code). + * - The other NetBSD developers on whose A2232 driver I had a + * pretty close look. However, I didn't copy any code so it + * is okay to put my code under the GPL and include it into + * Linux. + */ +/***************************** End of Documentation *****************/ + +/***************************** Defines ******************************/ +/* + * Enables experimental 115200 (normal) 230400 (turbo) baud rate. + * The A2232 specification states it can only operate at speeds up to + * 19200 bits per second, and I was not able to send a file via + * "sz"/"rz" and a null-modem cable from one A2232 port to another + * at 115200 bits per second. + * However, this might work for you. + */ +#undef A2232_SPEEDHACK +/* + * Default is not to use RTS/CTS so you could be talked to death. + */ +#define A2232_SUPPRESS_RTSCTS_WARNING +/************************* End of Defines ***************************/ + +/***************************** Includes *****************************/ +#include <linux/module.h> + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> + +#include <asm/setup.h> +#include <asm/amigaints.h> +#include <asm/amigahw.h> +#include <linux/zorro.h> +#include <asm/irq.h> +#include <linux/mutex.h> + +#include <linux/delay.h> + +#include <linux/serial.h> +#include <linux/generic_serial.h> +#include <linux/tty_flip.h> + +#include "ser_a2232.h" +#include "ser_a2232fw.h" +/************************* End of Includes **************************/ + +/***************************** Prototypes ***************************/ +/* The interrupt service routine */ +static irqreturn_t a2232_vbl_inter(int irq, void *data); +/* Initialize the port structures */ +static void a2232_init_portstructs(void); +/* Initialize and register TTY drivers. */ +/* returns 0 IFF successful */ +static int a2232_init_drivers(void); + +/* BEGIN GENERIC_SERIAL PROTOTYPES */ +static void a2232_disable_tx_interrupts(void *ptr); +static void a2232_enable_tx_interrupts(void *ptr); +static void a2232_disable_rx_interrupts(void *ptr); +static void a2232_enable_rx_interrupts(void *ptr); +static int a2232_carrier_raised(struct tty_port *port); +static void a2232_shutdown_port(void *ptr); +static int a2232_set_real_termios(void *ptr); +static int a2232_chars_in_buffer(void *ptr); +static void a2232_close(void *ptr); +static void a2232_hungup(void *ptr); +/* static void a2232_getserial (void *ptr, struct serial_struct *sp); */ +/* END GENERIC_SERIAL PROTOTYPES */ + +/* Functions that the TTY driver struct expects */ +static int a2232_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg); +static void a2232_throttle(struct tty_struct *tty); +static void a2232_unthrottle(struct tty_struct *tty); +static int a2232_open(struct tty_struct * tty, struct file * filp); +/************************* End of Prototypes ************************/ + +/***************************** Global variables *********************/ +/*--------------------------------------------------------------------------- + * Interface from generic_serial.c back here + *--------------------------------------------------------------------------*/ +static struct real_driver a2232_real_driver = { + a2232_disable_tx_interrupts, + a2232_enable_tx_interrupts, + a2232_disable_rx_interrupts, + a2232_enable_rx_interrupts, + a2232_shutdown_port, + a2232_set_real_termios, + a2232_chars_in_buffer, + a2232_close, + a2232_hungup, + NULL /* a2232_getserial */ +}; + +static void *a2232_driver_ID = &a2232_driver_ID; // Some memory address WE own. + +/* Ports structs */ +static struct a2232_port a2232_ports[MAX_A2232_BOARDS*NUMLINES]; + +/* TTY driver structs */ +static struct tty_driver *a2232_driver; + +/* nr of cards completely (all ports) and correctly configured */ +static int nr_a2232; + +/* zorro_dev structs for the A2232's */ +static struct zorro_dev *zd_a2232[MAX_A2232_BOARDS]; +/***************************** End of Global variables **************/ + +/* Helper functions */ + +static inline volatile struct a2232memory *a2232mem(unsigned int board) +{ + return (volatile struct a2232memory *)ZTWO_VADDR(zd_a2232[board]->resource.start); +} + +static inline volatile struct a2232status *a2232stat(unsigned int board, + unsigned int portonboard) +{ + volatile struct a2232memory *mem = a2232mem(board); + return &(mem->Status[portonboard]); +} + +static inline void a2232_receive_char(struct a2232_port *port, int ch, int err) +{ +/* Mostly stolen from other drivers. + Maybe one could implement a more efficient version by not only + transferring one character at a time. +*/ + struct tty_struct *tty = port->gs.port.tty; + +#if 0 + switch(err) { + case TTY_BREAK: + break; + case TTY_PARITY: + break; + case TTY_OVERRUN: + break; + case TTY_FRAME: + break; + } +#endif + + tty_insert_flip_char(tty, ch, err); + tty_flip_buffer_push(tty); +} + +/***************************** Functions ****************************/ +/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/ + +static void a2232_disable_tx_interrupts(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + stat->OutDisable = -1; + + /* Does this here really have to be? */ + local_irq_save(flags); + port->gs.port.flags &= ~GS_TX_INTEN; + local_irq_restore(flags); +} + +static void a2232_enable_tx_interrupts(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + stat->OutDisable = 0; + + /* Does this here really have to be? */ + local_irq_save(flags); + port->gs.port.flags |= GS_TX_INTEN; + local_irq_restore(flags); +} + +static void a2232_disable_rx_interrupts(void *ptr) +{ + struct a2232_port *port; + port = ptr; + port->disable_rx = -1; +} + +static void a2232_enable_rx_interrupts(void *ptr) +{ + struct a2232_port *port; + port = ptr; + port->disable_rx = 0; +} + +static int a2232_carrier_raised(struct tty_port *port) +{ + struct a2232_port *ap = container_of(port, struct a2232_port, gs.port); + return ap->cd_status; +} + +static void a2232_shutdown_port(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + + local_irq_save(flags); + + port->gs.port.flags &= ~GS_ACTIVE; + + if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) { + /* Set DTR and RTS to Low, flush output. + The NetBSD driver "msc.c" does it this way. */ + stat->Command = ( (stat->Command & ~A2232CMD_CMask) | + A2232CMD_Close ); + stat->OutFlush = -1; + stat->Setup = -1; + } + + local_irq_restore(flags); + + /* After analyzing control flow, I think a2232_shutdown_port + is actually the last call from the system when at application + level someone issues a "echo Hello >>/dev/ttyY0". + Therefore I think the MOD_DEC_USE_COUNT should be here and + not in "a2232_close()". See the comment in "sx.c", too. + If you run into problems, compile this driver into the + kernel instead of compiling it as a module. */ +} + +static int a2232_set_real_termios(void *ptr) +{ + unsigned int cflag, baud, chsize, stopb, parity, softflow; + int rate; + int a2232_param, a2232_cmd; + unsigned long flags; + unsigned int i; + struct a2232_port *port = ptr; + volatile struct a2232status *status; + volatile struct a2232memory *mem; + + if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0; + + status = a2232stat(port->which_a2232, port->which_port_on_a2232); + mem = a2232mem(port->which_a2232); + + a2232_param = a2232_cmd = 0; + + // get baud rate + baud = port->gs.baud; + if (baud == 0) { + /* speed == 0 -> drop DTR, do nothing else */ + local_irq_save(flags); + // Clear DTR (and RTS... mhhh). + status->Command = ( (status->Command & ~A2232CMD_CMask) | + A2232CMD_Close ); + status->OutFlush = -1; + status->Setup = -1; + + local_irq_restore(flags); + return 0; + } + + rate = A2232_BAUD_TABLE_NOAVAIL; + for (i=0; i < A2232_BAUD_TABLE_NUM_RATES * 3; i += 3){ + if (a2232_baud_table[i] == baud){ + if (mem->Common.Crystal == A2232_TURBO) rate = a2232_baud_table[i+2]; + else rate = a2232_baud_table[i+1]; + } + } + if (rate == A2232_BAUD_TABLE_NOAVAIL){ + printk("a2232: Board %d Port %d unsupported baud rate: %d baud. Using another.\n",port->which_a2232,port->which_port_on_a2232,baud); + // This is useful for both (turbo or normal) Crystal versions. + rate = A2232PARAM_B9600; + } + a2232_param |= rate; + + cflag = port->gs.port.tty->termios->c_cflag; + + // get character size + chsize = cflag & CSIZE; + switch (chsize){ + case CS8: a2232_param |= A2232PARAM_8Bit; break; + case CS7: a2232_param |= A2232PARAM_7Bit; break; + case CS6: a2232_param |= A2232PARAM_6Bit; break; + case CS5: a2232_param |= A2232PARAM_5Bit; break; + default: printk("a2232: Board %d Port %d unsupported character size: %d. Using 8 data bits.\n", + port->which_a2232,port->which_port_on_a2232,chsize); + a2232_param |= A2232PARAM_8Bit; break; + } + + // get number of stop bits + stopb = cflag & CSTOPB; + if (stopb){ // two stop bits instead of one + printk("a2232: Board %d Port %d 2 stop bits unsupported. Using 1 stop bit.\n", + port->which_a2232,port->which_port_on_a2232); + } + + // Warn if RTS/CTS not wanted + if (!(cflag & CRTSCTS)){ +#ifndef A2232_SUPPRESS_RTSCTS_WARNING + printk("a2232: Board %d Port %d cannot switch off firmware-implemented RTS/CTS hardware flow control.\n", + port->which_a2232,port->which_port_on_a2232); +#endif + } + + /* I think this is correct. + However, IXOFF means _input_ flow control and I wonder + if one should care about IXON _output_ flow control, + too. If this makes problems, one should turn the A2232 + firmware XON/XOFF "SoftFlow" flow control off and use + the conventional way of inserting START/STOP characters + by hand in throttle()/unthrottle(). + */ + softflow = !!( port->gs.port.tty->termios->c_iflag & IXOFF ); + + // get Parity (Enabled/Disabled? If Enabled, Odd or Even?) + parity = cflag & (PARENB | PARODD); + if (parity & PARENB){ + if (parity & PARODD){ + a2232_cmd |= A2232CMD_OddParity; + } + else{ + a2232_cmd |= A2232CMD_EvenParity; + } + } + else a2232_cmd |= A2232CMD_NoParity; + + + /* Hmm. Maybe an own a2232_port structure + member would be cleaner? */ + if (cflag & CLOCAL) + port->gs.port.flags &= ~ASYNC_CHECK_CD; + else + port->gs.port.flags |= ASYNC_CHECK_CD; + + + /* Now we have all parameters and can go to set them: */ + local_irq_save(flags); + + status->Param = a2232_param | A2232PARAM_RcvBaud; + status->Command = a2232_cmd | A2232CMD_Open | A2232CMD_Enable; + status->SoftFlow = softflow; + status->OutDisable = 0; + status->Setup = -1; + + local_irq_restore(flags); + return 0; +} + +static int a2232_chars_in_buffer(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *status; + unsigned char ret; /* we need modulo-256 arithmetics */ + port = ptr; + status = a2232stat(port->which_a2232, port->which_port_on_a2232); +#if A2232_IOBUFLEN != 256 +#error "Re-Implement a2232_chars_in_buffer()!" +#endif + ret = (status->OutHead - status->OutTail); + return ret; +} + +static void a2232_close(void *ptr) +{ + a2232_disable_tx_interrupts(ptr); + a2232_disable_rx_interrupts(ptr); + /* see the comment in a2232_shutdown_port above. */ +} + +static void a2232_hungup(void *ptr) +{ + a2232_close(ptr); +} +/*** END OF REAL_DRIVER FUNCTIONS ***/ + +/*** BEGIN FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/ +static int a2232_ioctl( struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void a2232_throttle(struct tty_struct *tty) +{ +/* Throttle: System cannot take another chars: Drop RTS or + send the STOP char or whatever. + The A2232 firmware does RTS/CTS anyway, and XON/XOFF + if switched on. So the only thing we can do at this + layer here is not taking any characters out of the + A2232 buffer any more. */ + struct a2232_port *port = tty->driver_data; + port->throttle_input = -1; +} + +static void a2232_unthrottle(struct tty_struct *tty) +{ +/* Unthrottle: dual to "throttle()" above. */ + struct a2232_port *port = tty->driver_data; + port->throttle_input = 0; +} + +static int a2232_open(struct tty_struct * tty, struct file * filp) +{ +/* More or less stolen from other drivers. */ + int line; + int retval; + struct a2232_port *port; + + line = tty->index; + port = &a2232_ports[line]; + + tty->driver_data = port; + port->gs.port.tty = tty; + port->gs.port.count++; + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.port.count--; + return retval; + } + port->gs.port.flags |= GS_ACTIVE; + retval = gs_block_til_ready(port, filp); + + if (retval) { + port->gs.port.count--; + return retval; + } + + a2232_enable_rx_interrupts(port); + + return 0; +} +/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/ + +static irqreturn_t a2232_vbl_inter(int irq, void *data) +{ +#if A2232_IOBUFLEN != 256 +#error "Re-Implement a2232_vbl_inter()!" +#endif + +struct a2232_port *port; +volatile struct a2232memory *mem; +volatile struct a2232status *status; +unsigned char newhead; +unsigned char bufpos; /* Must be unsigned char. We need the modulo-256 arithmetics */ +unsigned char ncd, ocd, ccd; /* names consistent with the NetBSD driver */ +volatile u_char *ibuf, *cbuf, *obuf; +int ch, err, n, p; + for (n = 0; n < nr_a2232; n++){ /* for every completely initialized A2232 board */ + mem = a2232mem(n); + for (p = 0; p < NUMLINES; p++){ /* for every port on this board */ + err = 0; + port = &a2232_ports[n*NUMLINES+p]; + if ( port->gs.port.flags & GS_ACTIVE ){ /* if the port is used */ + + status = a2232stat(n,p); + + if (!port->disable_rx && !port->throttle_input){ /* If input is not disabled */ + newhead = status->InHead; /* 65EC02 write pointer */ + bufpos = status->InTail; + + /* check for input for this port */ + if (newhead != bufpos) { + /* buffer for input chars/events */ + ibuf = mem->InBuf[p]; + + /* data types of bytes in ibuf */ + cbuf = mem->InCtl[p]; + + /* do for all chars */ + while (bufpos != newhead) { + /* which type of input data? */ + switch (cbuf[bufpos]) { + /* switch on input event (CD, BREAK, etc.) */ + case A2232INCTL_EVENT: + switch (ibuf[bufpos++]) { + case A2232EVENT_Break: + /* TODO: Handle BREAK signal */ + break; + /* A2232EVENT_CarrierOn and A2232EVENT_CarrierOff are + handled in a separate queue and should not occur here. */ + case A2232EVENT_Sync: + printk("A2232: 65EC02 software sent SYNC event, don't know what to do. Ignoring."); + break; + default: + printk("A2232: 65EC02 software broken, unknown event type %d occurred.\n",ibuf[bufpos-1]); + } /* event type switch */ + break; + case A2232INCTL_CHAR: + /* Receive incoming char */ + a2232_receive_char(port, ibuf[bufpos], err); + bufpos++; + break; + default: + printk("A2232: 65EC02 software broken, unknown data type %d occurred.\n",cbuf[bufpos]); + bufpos++; + } /* switch on input data type */ + } /* while there's something in the buffer */ + + status->InTail = bufpos; /* tell 65EC02 what we've read */ + + } /* if there was something in the buffer */ + } /* If input is not disabled */ + + /* Now check if there's something to output */ + obuf = mem->OutBuf[p]; + bufpos = status->OutHead; + while ( (port->gs.xmit_cnt > 0) && + (!port->gs.port.tty->stopped) && + (!port->gs.port.tty->hw_stopped) ){ /* While there are chars to transmit */ + if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */ + ch = port->gs.xmit_buf[port->gs.xmit_tail]; /* get the next char to transmit */ + port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */ + obuf[bufpos++] = ch; /* put it into the A2232 buffer */ + port->gs.xmit_cnt--; + } + else{ /* If A2232 the buffer is full */ + break; /* simply stop filling it. */ + } + } + status->OutHead = bufpos; + + /* WakeUp if output buffer runs low */ + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) { + tty_wakeup(port->gs.port.tty); + } + } // if the port is used + } // for every port on the board + + /* Now check the CD message queue */ + newhead = mem->Common.CDHead; + bufpos = mem->Common.CDTail; + if (newhead != bufpos){ /* There are CD events in queue */ + ocd = mem->Common.CDStatus; /* get old status bits */ + while (newhead != bufpos){ /* read all events */ + ncd = mem->CDBuf[bufpos++]; /* get one event */ + ccd = ncd ^ ocd; /* mask of changed lines */ + ocd = ncd; /* save new status bits */ + for(p=0; p < NUMLINES; p++){ /* for all ports */ + if (ccd & 1){ /* this one changed */ + + struct a2232_port *port = &a2232_ports[n*7+p]; + port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */ + + if (!(port->gs.port.flags & ASYNC_CHECK_CD)) + ; /* Don't report DCD changes */ + else if (port->cd_status) { // if DCD on: DCD went UP! + + /* Are we blocking in open?*/ + wake_up_interruptible(&port->gs.port.open_wait); + } + else { // if DCD off: DCD went DOWN! + if (port->gs.port.tty) + tty_hangup (port->gs.port.tty); + } + + } // if CD changed for this port + ccd >>= 1; + ncd >>= 1; /* Shift bits for next line */ + } // for every port + } // while CD events in queue + mem->Common.CDStatus = ocd; /* save new status */ + mem->Common.CDTail = bufpos; /* remove events */ + } // if events in CD queue + + } // for every completely initialized A2232 board + return IRQ_HANDLED; +} + +static const struct tty_port_operations a2232_port_ops = { + .carrier_raised = a2232_carrier_raised, +}; + +static void a2232_init_portstructs(void) +{ + struct a2232_port *port; + int i; + + for (i = 0; i < MAX_A2232_BOARDS*NUMLINES; i++) { + port = a2232_ports + i; + tty_port_init(&port->gs.port); + port->gs.port.ops = &a2232_port_ops; + port->which_a2232 = i/NUMLINES; + port->which_port_on_a2232 = i%NUMLINES; + port->disable_rx = port->throttle_input = port->cd_status = 0; + port->gs.magic = A2232_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &a2232_real_driver; + } +} + +static const struct tty_operations a2232_ops = { + .open = a2232_open, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .flush_buffer = gs_flush_buffer, + .ioctl = a2232_ioctl, + .throttle = a2232_throttle, + .unthrottle = a2232_unthrottle, + .set_termios = gs_set_termios, + .stop = gs_stop, + .start = gs_start, + .hangup = gs_hangup, +}; + +static int a2232_init_drivers(void) +{ + int error; + + a2232_driver = alloc_tty_driver(NUMLINES * nr_a2232); + if (!a2232_driver) + return -ENOMEM; + a2232_driver->owner = THIS_MODULE; + a2232_driver->driver_name = "commodore_a2232"; + a2232_driver->name = "ttyY"; + a2232_driver->major = A2232_NORMAL_MAJOR; + a2232_driver->type = TTY_DRIVER_TYPE_SERIAL; + a2232_driver->subtype = SERIAL_TYPE_NORMAL; + a2232_driver->init_termios = tty_std_termios; + a2232_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + a2232_driver->init_termios.c_ispeed = 9600; + a2232_driver->init_termios.c_ospeed = 9600; + a2232_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(a2232_driver, &a2232_ops); + if ((error = tty_register_driver(a2232_driver))) { + printk(KERN_ERR "A2232: Couldn't register A2232 driver, error = %d\n", + error); + put_tty_driver(a2232_driver); + return 1; + } + return 0; +} + +static int __init a2232board_init(void) +{ + struct zorro_dev *z; + + unsigned int boardaddr; + int bcount; + short start; + u_char *from; + volatile u_char *to; + volatile struct a2232memory *mem; + int error, i; + +#ifdef CONFIG_SMP + return -ENODEV; /* This driver is not SMP aware. Is there an SMP ZorroII-bus-machine? */ +#endif + + if (!MACH_IS_AMIGA){ + return -ENODEV; + } + + printk("Commodore A2232 driver initializing.\n"); /* Say that we're alive. */ + + z = NULL; + nr_a2232 = 0; + while ( (z = zorro_find_device(ZORRO_WILDCARD, z)) ){ + if ( (z->id != ZORRO_PROD_CBM_A2232_PROTOTYPE) && + (z->id != ZORRO_PROD_CBM_A2232) ){ + continue; // The board found was no A2232 + } + if (!zorro_request_device(z,"A2232 driver")) + continue; + + printk("Commodore A2232 found (#%d).\n",nr_a2232); + + zd_a2232[nr_a2232] = z; + + boardaddr = ZTWO_VADDR( z->resource.start ); + printk("Board is located at address 0x%x, size is 0x%x.\n", boardaddr, (unsigned int) ((z->resource.end+1) - (z->resource.start))); + + mem = (volatile struct a2232memory *) boardaddr; + + (void) mem->Enable6502Reset; /* copy the code across to the board */ + to = (u_char *)mem; from = a2232_65EC02code; bcount = sizeof(a2232_65EC02code) - 2; + start = *(short *)from; + from += sizeof(start); + to += start; + while(bcount--) *to++ = *from++; + printk("65EC02 software uploaded to the A2232 memory.\n"); + + mem->Common.Crystal = A2232_UNKNOWN; /* use automatic speed check */ + + /* start 6502 running */ + (void) mem->ResetBoard; + printk("A2232's 65EC02 CPU up and running.\n"); + + /* wait until speed detector has finished */ + for (bcount = 0; bcount < 2000; bcount++) { + udelay(1000); + if (mem->Common.Crystal) + break; + } + printk((mem->Common.Crystal?"A2232 oscillator crystal detected by 65EC02 software: ":"65EC02 software could not determine A2232 oscillator crystal: ")); + switch (mem->Common.Crystal){ + case A2232_UNKNOWN: + printk("Unknown crystal.\n"); + break; + case A2232_NORMAL: + printk ("Normal crystal.\n"); + break; + case A2232_TURBO: + printk ("Turbo crystal.\n"); + break; + default: + printk ("0x%x. Huh?\n",mem->Common.Crystal); + } + + nr_a2232++; + + } + + printk("Total: %d A2232 boards initialized.\n", nr_a2232); /* Some status report if no card was found */ + + a2232_init_portstructs(); + + /* + a2232_init_drivers also registers the drivers. Must be here because all boards + have to be detected first. + */ + if (a2232_init_drivers()) return -ENODEV; // maybe we should use a different -Exxx? + + error = request_irq(IRQ_AMIGA_VERTB, a2232_vbl_inter, 0, + "A2232 serial VBL", a2232_driver_ID); + if (error) { + for (i = 0; i < nr_a2232; i++) + zorro_release_device(zd_a2232[i]); + tty_unregister_driver(a2232_driver); + put_tty_driver(a2232_driver); + } + return error; +} + +static void __exit a2232board_exit(void) +{ + int i; + + for (i = 0; i < nr_a2232; i++) { + zorro_release_device(zd_a2232[i]); + } + + tty_unregister_driver(a2232_driver); + put_tty_driver(a2232_driver); + free_irq(IRQ_AMIGA_VERTB, a2232_driver_ID); +} + +module_init(a2232board_init); +module_exit(a2232board_exit); + +MODULE_AUTHOR("Enver Haase"); +MODULE_DESCRIPTION("Amiga A2232 multi-serial board driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/generic_serial/ser_a2232.h b/drivers/staging/generic_serial/ser_a2232.h new file mode 100644 index 0000000..bc09eb9 --- /dev/null +++ b/drivers/staging/generic_serial/ser_a2232.h @@ -0,0 +1,202 @@ +/* drivers/char/ser_a2232.h */ + +/* $Id: ser_a2232.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */ + +/* Linux serial driver for the Amiga A2232 board */ + +/* This driver is MAINTAINED. Before applying any changes, please contact + * the author. + */ + +/* Copyright (c) 2000-2001 Enver Haase <ehaase@inf.fu-berlin.de> + * alias The A2232 driver project <A2232@gmx.net> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _SER_A2232_H_ +#define _SER_A2232_H_ + +/* + How many boards are to be supported at maximum; + "up to five A2232 Multiport Serial Cards may be installed in a + single Amiga 2000" states the A2232 User's Guide. If you have + more slots available, you might want to change the value below. +*/ +#define MAX_A2232_BOARDS 5 + +#ifndef A2232_NORMAL_MAJOR +/* This allows overriding on the compiler commandline, or in a "major.h" + include or something like that */ +#define A2232_NORMAL_MAJOR 224 /* /dev/ttyY* */ +#define A2232_CALLOUT_MAJOR 225 /* /dev/cuy* */ +#endif + +/* Some magic is always good - Who knows :) */ +#define A2232_MAGIC 0x000a2232 + +/* A2232 port structure to keep track of the + status of every single line used */ +struct a2232_port{ + struct gs_port gs; + unsigned int which_a2232; + unsigned int which_port_on_a2232; + short disable_rx; + short throttle_input; + short cd_status; +}; + +#define NUMLINES 7 /* number of lines per board */ +#define A2232_IOBUFLEN 256 /* number of bytes per buffer */ +#define A2232_IOBUFLENMASK 0xff /* mask for maximum number of bytes */ + + +#define A2232_UNKNOWN 0 /* crystal not known */ +#define A2232_NORMAL 1 /* normal A2232 (1.8432 MHz oscillator) */ +#define A2232_TURBO 2 /* turbo A2232 (3.6864 MHz oscillator) */ + + +struct a2232common { + char Crystal; /* normal (1) or turbo (2) board? */ + u_char Pad_a; + u_char TimerH; /* timer value after speed check */ + u_char TimerL; + u_char CDHead; /* head pointer for CD message queue */ + u_char CDTail; /* tail pointer for CD message queue */ + u_char CDStatus; + u_char Pad_b; +}; + +struct a2232status { + u_char InHead; /* input queue head */ + u_char InTail; /* input queue tail */ + u_char OutDisable; /* disables output */ + u_char OutHead; /* output queue head */ + u_char OutTail; /* output queue tail */ + u_char OutCtrl; /* soft flow control character to send */ + u_char OutFlush; /* flushes output buffer */ + u_char Setup; /* causes reconfiguration */ + u_char Param; /* parameter byte - see A2232PARAM */ + u_char Command; /* command byte - see A2232CMD */ + u_char SoftFlow; /* enables xon/xoff flow control */ + /* private 65EC02 fields: */ + u_char XonOff; /* stores XON/XOFF enable/disable */ +}; + +#define A2232_MEMPAD1 \ + (0x0200 - NUMLINES * sizeof(struct a2232status) - \ + sizeof(struct a2232common)) +#define A2232_MEMPAD2 (0x2000 - NUMLINES * A2232_IOBUFLEN - A2232_IOBUFLEN) + +struct a2232memory { + struct a2232status Status[NUMLINES]; /* 0x0000-0x006f status areas */ + struct a2232common Common; /* 0x0070-0x0077 common flags */ + u_char Dummy1[A2232_MEMPAD1]; /* 0x00XX-0x01ff */ + u_char OutBuf[NUMLINES][A2232_IOBUFLEN];/* 0x0200-0x08ff output bufs */ + u_char InBuf[NUMLINES][A2232_IOBUFLEN]; /* 0x0900-0x0fff input bufs */ + u_char InCtl[NUMLINES][A2232_IOBUFLEN]; /* 0x1000-0x16ff control data */ + u_char CDBuf[A2232_IOBUFLEN]; /* 0x1700-0x17ff CD event buffer */ + u_char Dummy2[A2232_MEMPAD2]; /* 0x1800-0x2fff */ + u_char Code[0x1000]; /* 0x3000-0x3fff code area */ + u_short InterruptAck; /* 0x4000 intr ack */ + u_char Dummy3[0x3ffe]; /* 0x4002-0x7fff */ + u_short Enable6502Reset; /* 0x8000 Stop board, */ + /* 6502 RESET line held low */ + u_char Dummy4[0x3ffe]; /* 0x8002-0xbfff */ + u_short ResetBoard; /* 0xc000 reset board & run, */ + /* 6502 RESET line held high */ +}; + +#undef A2232_MEMPAD1 +#undef A2232_MEMPAD2 + +#define A2232INCTL_CHAR 0 /* corresponding byte in InBuf is a character */ +#define A2232INCTL_EVENT 1 /* corresponding byte in InBuf is an event */ + +#define A2232EVENT_Break 1 /* break set */ +#define A2232EVENT_CarrierOn 2 /* carrier raised */ +#define A2232EVENT_CarrierOff 3 /* carrier dropped */ +#define A2232EVENT_Sync 4 /* don't know, defined in 2232.ax */ + +#define A2232CMD_Enable 0x1 /* enable/DTR bit */ +#define A2232CMD_Close 0x2 /* close the device */ +#define A2232CMD_Open 0xb /* open the device */ +#define A2232CMD_CMask 0xf /* command mask */ +#define A2232CMD_RTSOff 0x0 /* turn off RTS */ +#define A2232CMD_RTSOn 0x8 /* turn on RTS */ +#define A2232CMD_Break 0xd /* transmit a break */ +#define A2232CMD_RTSMask 0xc /* mask for RTS stuff */ +#define A2232CMD_NoParity 0x00 /* don't use parity */ +#define A2232CMD_OddParity 0x20 /* odd parity */ +#define A2232CMD_EvenParity 0x60 /* even parity */ +#define A2232CMD_ParityMask 0xe0 /* parity mask */ + +#define A2232PARAM_B115200 0x0 /* baud rates */ +#define A2232PARAM_B50 0x1 +#define A2232PARAM_B75 0x2 +#define A2232PARAM_B110 0x3 +#define A2232PARAM_B134 0x4 +#define A2232PARAM_B150 0x5 +#define A2232PARAM_B300 0x6 +#define A2232PARAM_B600 0x7 +#define A2232PARAM_B1200 0x8 +#define A2232PARAM_B1800 0x9 +#define A2232PARAM_B2400 0xa +#define A2232PARAM_B3600 0xb +#define A2232PARAM_B4800 0xc +#define A2232PARAM_B7200 0xd +#define A2232PARAM_B9600 0xe +#define A2232PARAM_B19200 0xf +#define A2232PARAM_BaudMask 0xf /* baud rate mask */ +#define A2232PARAM_RcvBaud 0x10 /* enable receive baud rate */ +#define A2232PARAM_8Bit 0x00 /* numbers of bits */ +#define A2232PARAM_7Bit 0x20 +#define A2232PARAM_6Bit 0x40 +#define A2232PARAM_5Bit 0x60 +#define A2232PARAM_BitMask 0x60 /* numbers of bits mask */ + + +/* Standard speeds tables, -1 means unavailable, -2 means 0 baud: switch off line */ +#define A2232_BAUD_TABLE_NOAVAIL -1 +#define A2232_BAUD_TABLE_NUM_RATES (18) +static int a2232_baud_table[A2232_BAUD_TABLE_NUM_RATES*3] = { + //Baud //Normal //Turbo + 50, A2232PARAM_B50, A2232_BAUD_TABLE_NOAVAIL, + 75, A2232PARAM_B75, A2232_BAUD_TABLE_NOAVAIL, + 110, A2232PARAM_B110, A2232_BAUD_TABLE_NOAVAIL, + 134, A2232PARAM_B134, A2232_BAUD_TABLE_NOAVAIL, + 150, A2232PARAM_B150, A2232PARAM_B75, + 200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, + 300, A2232PARAM_B300, A2232PARAM_B150, + 600, A2232PARAM_B600, A2232PARAM_B300, + 1200, A2232PARAM_B1200, A2232PARAM_B600, + 1800, A2232PARAM_B1800, A2232_BAUD_TABLE_NOAVAIL, + 2400, A2232PARAM_B2400, A2232PARAM_B1200, + 4800, A2232PARAM_B4800, A2232PARAM_B2400, + 9600, A2232PARAM_B9600, A2232PARAM_B4800, + 19200, A2232PARAM_B19200, A2232PARAM_B9600, + 38400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B19200, + 57600, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, +#ifdef A2232_SPEEDHACK + 115200, A2232PARAM_B115200, A2232_BAUD_TABLE_NOAVAIL, + 230400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B115200 +#else + 115200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, + 230400, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL +#endif +}; +#endif diff --git a/drivers/staging/generic_serial/ser_a2232fw.ax b/drivers/staging/generic_serial/ser_a2232fw.ax new file mode 100644 index 0000000..7364380 --- /dev/null +++ b/drivers/staging/generic_serial/ser_a2232fw.ax @@ -0,0 +1,529 @@ +;.lib "axm" +; +;begin +;title "A2232 serial board driver" +; +;set modules "2232" +;set executable "2232.bin" +; +;;;;set nolink +; +;set temporary directory "t:" +; +;set assembly options "-m6502 -l60:t:list" +;set link options "bin"; loadadr" +;;;bin2c 2232.bin msc6502.h msc6502code +;end +; +; +; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ### +; +; - Created 950501 by JM - +; +; +; Serial board driver software. +; +; +% Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>. +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions +% are met: +% 1. Redistributions of source code must retain the above copyright +% notice, and the entire permission notice in its entirety, +% including the disclaimer of warranties. +% 2. Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% 3. The name of the author may not be used to endorse or promote +% products derived from this software without specific prior +% written permission. +% +% ALTERNATIVELY, this product may be distributed under the terms of +% the GNU General Public License, in which case the provisions of the +% GPL are required INSTEAD OF the above restrictions. (This clause is +% necessary due to a potential bad interaction between the GPL and +% the restrictions contained in a BSD-style copyright.) +% +% THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED +% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +% DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +% STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +% OF THE POSSIBILITY OF SUCH DAMAGE. +; +; +; Bugs: +; +; - Can't send a break yet +; +; +; +; Edited: +; +; - 950501 by JM -> v0.1 - Created this file. +; - 951029 by JM -> v1.3 - Carrier Detect events now queued in a separate +; queue. +; +; + + +CODE equ $3800 ; start address for program code + + +CTL_CHAR equ $00 ; byte in ibuf is a character +CTL_EVENT equ $01 ; byte in ibuf is an event + +EVENT_BREAK equ $01 +EVENT_CDON equ $02 +EVENT_CDOFF equ $03 +EVENT_SYNC equ $04 + +XON equ $11 +XOFF equ $13 + + +VARBASE macro *starting_address ; was VARINIT +_varbase set \1 + endm + +VARDEF macro *name space_needs +\1 equ _varbase +_varbase set _varbase+\2 + endm + + +stz macro * address + db $64,\1 + endm + +stzax macro * address + db $9e,<\1,>\1 + endm + + +biti macro * immediate value + db $89,\1 + endm + +smb0 macro * address + db $87,\1 + endm +smb1 macro * address + db $97,\1 + endm +smb2 macro * address + db $a7,\1 + endm +smb3 macro * address + db $b7,\1 + endm +smb4 macro * address + db $c7,\1 + endm +smb5 macro * address + db $d7,\1 + endm +smb6 macro * address + db $e7,\1 + endm +smb7 macro * address + db $f7,\1 + endm + + + +;-----------------------------------------------------------------------; +; ; +; stuff common for all ports, non-critical (run once / loop) ; +; ; +DO_SLOW macro * port_number ; + .local ; ; + lda CIA+C_PA ; check all CD inputs ; + cmp CommonCDo ; changed from previous accptd? ; + beq =over ; nope, do nothing else here ; + ; ; + cmp CommonCDb ; bouncing? ; + beq =nobounce ; nope -> ; + ; ; + sta CommonCDb ; save current state ; + lda #64 ; reinitialize counter ; + sta CommonCDc ; ; + jmp =over ; skip CD save ; + ; ; +=nobounce dec CommonCDc ; no, decrement bounce counter ; + bpl =over ; not done yet, so skip CD save ; + ; ; +=saveCD ldx CDHead ; get write index ; + sta cdbuf,x ; save status in buffer ; + inx ; ; + cpx CDTail ; buffer full? ; + .if ne ; no: preserve status: ; + stx CDHead ; update index in RAM ; + sta CommonCDo ; save state for the next check ; + .end ; ; +=over .end local ; + endm ; + ; +;-----------------------------------------------------------------------; + + +; port specific stuff (no data transfer) + +DO_PORT macro * port_number + .local ; ; + lda SetUp\1 ; reconfiguration request? ; + .if ne ; yes: ; + lda SoftFlow\1 ; get XON/XOFF flag ; + sta XonOff\1 ; save it ; + lda Param\1 ; get parameter ; + ora #%00010000 ; use baud generator for Rx ; + sta ACIA\1+A_CTRL ; store in control register ; + stz OutDisable\1 ; enable transmit output ; + stz SetUp\1 ; no reconfiguration no more ; + .end ; ; + ; ; + lda InHead\1 ; get write index ; + sbc InTail\1 ; buffer full soon? ; + cmp #200 ; 200 chars or more in buffer? ; + lda Command\1 ; get Command reg value ; + and #%11110011 ; turn RTS OFF by default ; + .if cc ; still room in buffer: ; + ora #%00001000 ; turn RTS ON ; + .end ; ; + sta ACIA\1+A_CMD ; set/clear RTS ; + ; ; + lda OutFlush\1 ; request to flush output buffer; + .if ne ; yessh! ; + lda OutHead\1 ; get head ; + sta OutTail\1 ; save as tail ; + stz OutDisable\1 ; enable transmit output ; + stz OutFlush\1 ; clear request ; + .end + .end local + endm + + +DO_DATA macro * port number + .local + lda ACIA\1+A_SR ; read ACIA status register ; + biti [1<<3] ; something received? ; + .if ne ; yes: ; + biti [1<<1] ; framing error? ; + .if ne ; yes: ; + lda ACIA\1+A_DATA ; read received character ; + bne =SEND ; not break -> ignore it ; + ldx InHead\1 ; get write pointer ; + lda #CTL_EVENT ; get type of byte ; + sta ictl\1,x ; save it in InCtl buffer ; + lda #EVENT_BREAK ; event code ; + sta ibuf\1,x ; save it as well ; + inx ; ; + cpx InTail\1 ; still room in buffer? ; + .if ne ; absolutely: ; + stx InHead\1 ; update index in memory ; + .end ; ; + jmp =SEND ; go check if anything to send ; + .end ; ; + ; normal char received: ; + ldx InHead\1 ; get write index ; + lda ACIA\1+A_DATA ; read received character ; + sta ibuf\1,x ; save char in buffer ; + stzax ictl\1 ; set type to CTL_CHAR ; + inx ; ; + cpx InTail\1 ; buffer full? ; + .if ne ; no: preserve character: ; + stx InHead\1 ; update index in RAM ; + .end ; ; + and #$7f ; mask off parity if any ; + cmp #XOFF ; XOFF from remote host? ; + .if eq ; yes: ; + lda XonOff\1 ; if XON/XOFF handshaking.. ; + sta OutDisable\1 ; ..disable transmitter ; + .end ; ; + .end ; ; + ; ; + ; BUFFER FULL CHECK WAS HERE ; + ; ; +=SEND lda ACIA\1+A_SR ; transmit register empty? ; + and #[1<<4] ; ; + .if ne ; yes: ; + ldx OutCtrl\1 ; sending out XON/XOFF? ; + .if ne ; yes: ; + lda CIA+C_PB ; check CTS signal ; + and #[1<<\1] ; (for this port only) ; + bne =DONE ; not allowed to send -> done ; + stx ACIA\1+A_DATA ; transmit control char ; + stz OutCtrl\1 ; clear flag ; + jmp =DONE ; and we're done ; + .end ; ; + ; ; + ldx OutTail\1 ; anything to transmit? ; + cpx OutHead\1 ; ; + .if ne ; yes: ; + lda OutDisable\1 ; allowed to transmit? ; + .if eq ; yes: ; + lda CIA+C_PB ; check CTS signal ; + and #[1<<\1] ; (for this port only) ; + bne =DONE ; not allowed to send -> done ; + lda obuf\1,x ; get a char from buffer ; + sta ACIA\1+A_DATA ; send it away ; + inc OutTail\1 ; update read index ; + .end ; ; + .end ; ; + .end ; ; +=DONE .end local + endm + + + +PORTVAR macro * port number + VARDEF InHead\1 1 + VARDEF InTail\1 1 + VARDEF OutDisable\1 1 + VARDEF OutHead\1 1 + VARDEF OutTail\1 1 + VARDEF OutCtrl\1 1 + VARDEF OutFlush\1 1 + VARDEF SetUp\1 1 + VARDEF Param\1 1 + VARDEF Command\1 1 + VARDEF SoftFlow\1 1 + ; private: + VARDEF XonOff\1 1 + endm + + + VARBASE 0 ; start variables at address $0000 + PORTVAR 0 ; define variables for port 0 + PORTVAR 1 ; define variables for port 1 + PORTVAR 2 ; define variables for port 2 + PORTVAR 3 ; define variables for port 3 + PORTVAR 4 ; define variables for port 4 + PORTVAR 5 ; define variables for port 5 + PORTVAR 6 ; define variables for port 6 + + + + VARDEF Crystal 1 ; 0 = unknown, 1 = normal, 2 = turbo + VARDEF Pad_a 1 + VARDEF TimerH 1 + VARDEF TimerL 1 + VARDEF CDHead 1 + VARDEF CDTail 1 + VARDEF CDStatus 1 + VARDEF Pad_b 1 + + VARDEF CommonCDo 1 ; for carrier detect optimization + VARDEF CommonCDc 1 ; for carrier detect debouncing + VARDEF CommonCDb 1 ; for carrier detect debouncing + + + VARBASE $0200 + VARDEF obuf0 256 ; output data (characters only) + VARDEF obuf1 256 + VARDEF obuf2 256 + VARDEF obuf3 256 + VARDEF obuf4 256 + VARDEF obuf5 256 + VARDEF obuf6 256 + + VARDEF ibuf0 256 ; input data (characters, events etc - see ictl) + VARDEF ibuf1 256 + VARDEF ibuf2 256 + VARDEF ibuf3 256 + VARDEF ibuf4 256 + VARDEF ibuf5 256 + VARDEF ibuf6 256 + + VARDEF ictl0 256 ; input control information (type of data in ibuf) + VARDEF ictl1 256 + VARDEF ictl2 256 + VARDEF ictl3 256 + VARDEF ictl4 256 + VARDEF ictl5 256 + VARDEF ictl6 256 + + VARDEF cdbuf 256 ; CD event queue + + +ACIA0 equ $4400 +ACIA1 equ $4c00 +ACIA2 equ $5400 +ACIA3 equ $5c00 +ACIA4 equ $6400 +ACIA5 equ $6c00 +ACIA6 equ $7400 + +A_DATA equ $00 +A_SR equ $02 +A_CMD equ $04 +A_CTRL equ $06 +; 00 write transmit data read received data +; 02 reset ACIA read status register +; 04 write command register read command register +; 06 write control register read control register + +CIA equ $7c00 ; 8520 CIA +C_PA equ $00 ; port A data register +C_PB equ $02 ; port B data register +C_DDRA equ $04 ; data direction register for port A +C_DDRB equ $06 ; data direction register for port B +C_TAL equ $08 ; timer A +C_TAH equ $0a +C_TBL equ $0c ; timer B +C_TBH equ $0e +C_TODL equ $10 ; TOD LSB +C_TODM equ $12 ; TOD middle byte +C_TODH equ $14 ; TOD MSB +C_DATA equ $18 ; serial data register +C_INTCTRL equ $1a ; interrupt control register +C_CTRLA equ $1c ; control register A +C_CTRLB equ $1e ; control register B + + + + + + section main,code,CODE-2 + + db >CODE,<CODE + +;-----------------------------------------------------------------------; +; here's the initialization code: ; +; ; +R_RESET ldx #$ff ; + txs ; initialize stack pointer ; + cld ; in case a 6502 is used... ; + ldx #0 ; ; + lda #0 ; ; + ldy #Crystal ; this many bytes to clear ; +clr_loop sta 0,x ; clear zero page variables ; + inx ; ; + dey ; ; + bne clr_loop ; ; + ; ; + stz CommonCDo ; force CD test at boot ; + stz CommonCDb ; ; + stz CDHead ; clear queue ; + stz CDTail ; ; + ; ; + lda #0 ; ; + sta Pad_a ; ; + lda #170 ; test cmp ; + cmp #100 ; ; + .if cs ; ; + inc Pad_a ; C was set ; + .end ; ; + ; +;-----------------------------------------------------------------------; +; Speed check ; +;-----------------------------------------------------------------------; + ; + lda Crystal ; speed already set? ; + beq DoSpeedy ; ; + jmp LOOP ; yes, skip speed test ; + ; ; +DoSpeedy lda #%10011000 ; 8N1, 1200/2400 bps ; + sta ACIA0+A_CTRL ; ; + lda #%00001011 ; enable DTR ; + sta ACIA0+A_CMD ; ; + lda ACIA0+A_SR ; read status register ; + ; ; + lda #%10000000 ; disable all ints (unnecessary); + sta CIA+C_INTCTRL ; ; + lda #255 ; program the timer ; + sta CIA+C_TAL ; ; + sta CIA+C_TAH ; ; + ; ; + ldx #0 ; ; + stx ACIA0+A_DATA ; transmit a zero ; + nop ; ; + nop ; ; + lda ACIA0+A_SR ; read status ; + nop ; ; + nop ; ; + stx ACIA0+A_DATA ; transmit a zero ; +Speedy1 lda ACIA0+A_SR ; read status ; + and #[1<<4] ; transmit data reg empty? ; + beq Speedy1 ; not yet, wait more ; + ; ; + lda #%00010001 ; load & start the timer ; + stx ACIA0+A_DATA ; transmit one more zero ; + sta CIA+C_CTRLA ; ; +Speedy2 lda ACIA0+A_SR ; read status ; + and #[1<<4] ; transmit data reg empty? ; + beq Speedy2 ; not yet, wait more ; + stx CIA+C_CTRLA ; stop the timer ; + ; ; + lda CIA+C_TAL ; copy timer value for 68k ; + sta TimerL ; ; + lda CIA+C_TAH ; ; + sta TimerH ; ; + cmp #$d0 ; turbo or normal? ; + .if cs ; ; + lda #2 ; turbo! :-) ; + .else ; ; + lda #1 ; normal :-( ; + .end ; ; + sta Crystal ; ; + lda #0 ; ; + sta ACIA0+A_SR ; ; + sta ACIA0+A_CTRL ; reset UART ; + sta ACIA0+A_CMD ; ; + ; + jmp LOOP ; + ; +; ; +;-----------------------------------------------------------------------; +; ; +; The Real Thing: ; +; ; +LOOP DO_SLOW ; do non-critical things ; + jsr do_input ; check for received data + DO_PORT 0 + jsr do_input + DO_PORT 1 + jsr do_input + DO_PORT 2 + jsr do_input + DO_PORT 3 + jsr do_input + DO_PORT 4 + jsr do_input + DO_PORT 5 + jsr do_input + DO_PORT 6 + jsr do_input + jmp LOOP + + +do_input DO_DATA 0 + DO_DATA 1 + DO_DATA 2 + DO_DATA 3 + DO_DATA 4 + DO_DATA 5 + DO_DATA 6 + rts + + +;-----------------------------------------------------------------------; + section vectors,data,$3ffa + dw $d0d0 + dw R_RESET + dw $c0ce +;-----------------------------------------------------------------------; + + + + end + + + diff --git a/drivers/staging/generic_serial/ser_a2232fw.h b/drivers/staging/generic_serial/ser_a2232fw.h new file mode 100644 index 0000000..e09a30a --- /dev/null +++ b/drivers/staging/generic_serial/ser_a2232fw.h @@ -0,0 +1,306 @@ +/* drivers/char/ser_a2232fw.h */ + +/* $Id: ser_a2232fw.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */ + +/* + * Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* This is the 65EC02 code by Jukka Marin that is executed by + the A2232's 65EC02 processor (base address: 0x3800) + Source file: ser_a2232fw.ax + Version: 1.3 (951029) + Known Bugs: Cannot send a break yet +*/ +static unsigned char a2232_65EC02code[] = { + 0x38, 0x00, 0xA2, 0xFF, 0x9A, 0xD8, 0xA2, 0x00, + 0xA9, 0x00, 0xA0, 0x54, 0x95, 0x00, 0xE8, 0x88, + 0xD0, 0xFA, 0x64, 0x5C, 0x64, 0x5E, 0x64, 0x58, + 0x64, 0x59, 0xA9, 0x00, 0x85, 0x55, 0xA9, 0xAA, + 0xC9, 0x64, 0x90, 0x02, 0xE6, 0x55, 0xA5, 0x54, + 0xF0, 0x03, 0x4C, 0x92, 0x38, 0xA9, 0x98, 0x8D, + 0x06, 0x44, 0xA9, 0x0B, 0x8D, 0x04, 0x44, 0xAD, + 0x02, 0x44, 0xA9, 0x80, 0x8D, 0x1A, 0x7C, 0xA9, + 0xFF, 0x8D, 0x08, 0x7C, 0x8D, 0x0A, 0x7C, 0xA2, + 0x00, 0x8E, 0x00, 0x44, 0xEA, 0xEA, 0xAD, 0x02, + 0x44, 0xEA, 0xEA, 0x8E, 0x00, 0x44, 0xAD, 0x02, + 0x44, 0x29, 0x10, 0xF0, 0xF9, 0xA9, 0x11, 0x8E, + 0x00, 0x44, 0x8D, 0x1C, 0x7C, 0xAD, 0x02, 0x44, + 0x29, 0x10, 0xF0, 0xF9, 0x8E, 0x1C, 0x7C, 0xAD, + 0x08, 0x7C, 0x85, 0x57, 0xAD, 0x0A, 0x7C, 0x85, + 0x56, 0xC9, 0xD0, 0x90, 0x05, 0xA9, 0x02, 0x4C, + 0x82, 0x38, 0xA9, 0x01, 0x85, 0x54, 0xA9, 0x00, + 0x8D, 0x02, 0x44, 0x8D, 0x06, 0x44, 0x8D, 0x04, + 0x44, 0x4C, 0x92, 0x38, 0xAD, 0x00, 0x7C, 0xC5, + 0x5C, 0xF0, 0x1F, 0xC5, 0x5E, 0xF0, 0x09, 0x85, + 0x5E, 0xA9, 0x40, 0x85, 0x5D, 0x4C, 0xB8, 0x38, + 0xC6, 0x5D, 0x10, 0x0E, 0xA6, 0x58, 0x9D, 0x00, + 0x17, 0xE8, 0xE4, 0x59, 0xF0, 0x04, 0x86, 0x58, + 0x85, 0x5C, 0x20, 0x23, 0x3A, 0xA5, 0x07, 0xF0, + 0x0F, 0xA5, 0x0A, 0x85, 0x0B, 0xA5, 0x08, 0x09, + 0x10, 0x8D, 0x06, 0x44, 0x64, 0x02, 0x64, 0x07, + 0xA5, 0x00, 0xE5, 0x01, 0xC9, 0xC8, 0xA5, 0x09, + 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, + 0x44, 0xA5, 0x06, 0xF0, 0x08, 0xA5, 0x03, 0x85, + 0x04, 0x64, 0x02, 0x64, 0x06, 0x20, 0x23, 0x3A, + 0xA5, 0x13, 0xF0, 0x0F, 0xA5, 0x16, 0x85, 0x17, + 0xA5, 0x14, 0x09, 0x10, 0x8D, 0x06, 0x4C, 0x64, + 0x0E, 0x64, 0x13, 0xA5, 0x0C, 0xE5, 0x0D, 0xC9, + 0xC8, 0xA5, 0x15, 0x29, 0xF3, 0xB0, 0x02, 0x09, + 0x08, 0x8D, 0x04, 0x4C, 0xA5, 0x12, 0xF0, 0x08, + 0xA5, 0x0F, 0x85, 0x10, 0x64, 0x0E, 0x64, 0x12, + 0x20, 0x23, 0x3A, 0xA5, 0x1F, 0xF0, 0x0F, 0xA5, + 0x22, 0x85, 0x23, 0xA5, 0x20, 0x09, 0x10, 0x8D, + 0x06, 0x54, 0x64, 0x1A, 0x64, 0x1F, 0xA5, 0x18, + 0xE5, 0x19, 0xC9, 0xC8, 0xA5, 0x21, 0x29, 0xF3, + 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x54, 0xA5, + 0x1E, 0xF0, 0x08, 0xA5, 0x1B, 0x85, 0x1C, 0x64, + 0x1A, 0x64, 0x1E, 0x20, 0x23, 0x3A, 0xA5, 0x2B, + 0xF0, 0x0F, 0xA5, 0x2E, 0x85, 0x2F, 0xA5, 0x2C, + 0x09, 0x10, 0x8D, 0x06, 0x5C, 0x64, 0x26, 0x64, + 0x2B, 0xA5, 0x24, 0xE5, 0x25, 0xC9, 0xC8, 0xA5, + 0x2D, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, + 0x04, 0x5C, 0xA5, 0x2A, 0xF0, 0x08, 0xA5, 0x27, + 0x85, 0x28, 0x64, 0x26, 0x64, 0x2A, 0x20, 0x23, + 0x3A, 0xA5, 0x37, 0xF0, 0x0F, 0xA5, 0x3A, 0x85, + 0x3B, 0xA5, 0x38, 0x09, 0x10, 0x8D, 0x06, 0x64, + 0x64, 0x32, 0x64, 0x37, 0xA5, 0x30, 0xE5, 0x31, + 0xC9, 0xC8, 0xA5, 0x39, 0x29, 0xF3, 0xB0, 0x02, + 0x09, 0x08, 0x8D, 0x04, 0x64, 0xA5, 0x36, 0xF0, + 0x08, 0xA5, 0x33, 0x85, 0x34, 0x64, 0x32, 0x64, + 0x36, 0x20, 0x23, 0x3A, 0xA5, 0x43, 0xF0, 0x0F, + 0xA5, 0x46, 0x85, 0x47, 0xA5, 0x44, 0x09, 0x10, + 0x8D, 0x06, 0x6C, 0x64, 0x3E, 0x64, 0x43, 0xA5, + 0x3C, 0xE5, 0x3D, 0xC9, 0xC8, 0xA5, 0x45, 0x29, + 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x6C, + 0xA5, 0x42, 0xF0, 0x08, 0xA5, 0x3F, 0x85, 0x40, + 0x64, 0x3E, 0x64, 0x42, 0x20, 0x23, 0x3A, 0xA5, + 0x4F, 0xF0, 0x0F, 0xA5, 0x52, 0x85, 0x53, 0xA5, + 0x50, 0x09, 0x10, 0x8D, 0x06, 0x74, 0x64, 0x4A, + 0x64, 0x4F, 0xA5, 0x48, 0xE5, 0x49, 0xC9, 0xC8, + 0xA5, 0x51, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, + 0x8D, 0x04, 0x74, 0xA5, 0x4E, 0xF0, 0x08, 0xA5, + 0x4B, 0x85, 0x4C, 0x64, 0x4A, 0x64, 0x4E, 0x20, + 0x23, 0x3A, 0x4C, 0x92, 0x38, 0xAD, 0x02, 0x44, + 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, + 0xAD, 0x00, 0x44, 0xD0, 0x32, 0xA6, 0x00, 0xA9, + 0x01, 0x9D, 0x00, 0x10, 0xA9, 0x01, 0x9D, 0x00, + 0x09, 0xE8, 0xE4, 0x01, 0xF0, 0x02, 0x86, 0x00, + 0x4C, 0x65, 0x3A, 0xA6, 0x00, 0xAD, 0x00, 0x44, + 0x9D, 0x00, 0x09, 0x9E, 0x00, 0x10, 0xE8, 0xE4, + 0x01, 0xF0, 0x02, 0x86, 0x00, 0x29, 0x7F, 0xC9, + 0x13, 0xD0, 0x04, 0xA5, 0x0B, 0x85, 0x02, 0xAD, + 0x02, 0x44, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x05, + 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, 0xD0, + 0x21, 0x8E, 0x00, 0x44, 0x64, 0x05, 0x4C, 0x98, + 0x3A, 0xA6, 0x04, 0xE4, 0x03, 0xF0, 0x13, 0xA5, + 0x02, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, + 0xD0, 0x08, 0xBD, 0x00, 0x02, 0x8D, 0x00, 0x44, + 0xE6, 0x04, 0xAD, 0x02, 0x4C, 0x89, 0x08, 0xF0, + 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x4C, + 0xD0, 0x32, 0xA6, 0x0C, 0xA9, 0x01, 0x9D, 0x00, + 0x11, 0xA9, 0x01, 0x9D, 0x00, 0x0A, 0xE8, 0xE4, + 0x0D, 0xF0, 0x02, 0x86, 0x0C, 0x4C, 0xDA, 0x3A, + 0xA6, 0x0C, 0xAD, 0x00, 0x4C, 0x9D, 0x00, 0x0A, + 0x9E, 0x00, 0x11, 0xE8, 0xE4, 0x0D, 0xF0, 0x02, + 0x86, 0x0C, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, + 0xA5, 0x17, 0x85, 0x0E, 0xAD, 0x02, 0x4C, 0x29, + 0x10, 0xF0, 0x2C, 0xA6, 0x11, 0xF0, 0x0F, 0xAD, + 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x21, 0x8E, 0x00, + 0x4C, 0x64, 0x11, 0x4C, 0x0D, 0x3B, 0xA6, 0x10, + 0xE4, 0x0F, 0xF0, 0x13, 0xA5, 0x0E, 0xD0, 0x0F, + 0xAD, 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x08, 0xBD, + 0x00, 0x03, 0x8D, 0x00, 0x4C, 0xE6, 0x10, 0xAD, + 0x02, 0x54, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, + 0xF0, 0x1B, 0xAD, 0x00, 0x54, 0xD0, 0x32, 0xA6, + 0x18, 0xA9, 0x01, 0x9D, 0x00, 0x12, 0xA9, 0x01, + 0x9D, 0x00, 0x0B, 0xE8, 0xE4, 0x19, 0xF0, 0x02, + 0x86, 0x18, 0x4C, 0x4F, 0x3B, 0xA6, 0x18, 0xAD, + 0x00, 0x54, 0x9D, 0x00, 0x0B, 0x9E, 0x00, 0x12, + 0xE8, 0xE4, 0x19, 0xF0, 0x02, 0x86, 0x18, 0x29, + 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x23, 0x85, + 0x1A, 0xAD, 0x02, 0x54, 0x29, 0x10, 0xF0, 0x2C, + 0xA6, 0x1D, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, + 0x04, 0xD0, 0x21, 0x8E, 0x00, 0x54, 0x64, 0x1D, + 0x4C, 0x82, 0x3B, 0xA6, 0x1C, 0xE4, 0x1B, 0xF0, + 0x13, 0xA5, 0x1A, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, + 0x29, 0x04, 0xD0, 0x08, 0xBD, 0x00, 0x04, 0x8D, + 0x00, 0x54, 0xE6, 0x1C, 0xAD, 0x02, 0x5C, 0x89, + 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, + 0x00, 0x5C, 0xD0, 0x32, 0xA6, 0x24, 0xA9, 0x01, + 0x9D, 0x00, 0x13, 0xA9, 0x01, 0x9D, 0x00, 0x0C, + 0xE8, 0xE4, 0x25, 0xF0, 0x02, 0x86, 0x24, 0x4C, + 0xC4, 0x3B, 0xA6, 0x24, 0xAD, 0x00, 0x5C, 0x9D, + 0x00, 0x0C, 0x9E, 0x00, 0x13, 0xE8, 0xE4, 0x25, + 0xF0, 0x02, 0x86, 0x24, 0x29, 0x7F, 0xC9, 0x13, + 0xD0, 0x04, 0xA5, 0x2F, 0x85, 0x26, 0xAD, 0x02, + 0x5C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x29, 0xF0, + 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, 0x21, + 0x8E, 0x00, 0x5C, 0x64, 0x29, 0x4C, 0xF7, 0x3B, + 0xA6, 0x28, 0xE4, 0x27, 0xF0, 0x13, 0xA5, 0x26, + 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, + 0x08, 0xBD, 0x00, 0x05, 0x8D, 0x00, 0x5C, 0xE6, + 0x28, 0xAD, 0x02, 0x64, 0x89, 0x08, 0xF0, 0x3B, + 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x64, 0xD0, + 0x32, 0xA6, 0x30, 0xA9, 0x01, 0x9D, 0x00, 0x14, + 0xA9, 0x01, 0x9D, 0x00, 0x0D, 0xE8, 0xE4, 0x31, + 0xF0, 0x02, 0x86, 0x30, 0x4C, 0x39, 0x3C, 0xA6, + 0x30, 0xAD, 0x00, 0x64, 0x9D, 0x00, 0x0D, 0x9E, + 0x00, 0x14, 0xE8, 0xE4, 0x31, 0xF0, 0x02, 0x86, + 0x30, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, + 0x3B, 0x85, 0x32, 0xAD, 0x02, 0x64, 0x29, 0x10, + 0xF0, 0x2C, 0xA6, 0x35, 0xF0, 0x0F, 0xAD, 0x02, + 0x7C, 0x29, 0x10, 0xD0, 0x21, 0x8E, 0x00, 0x64, + 0x64, 0x35, 0x4C, 0x6C, 0x3C, 0xA6, 0x34, 0xE4, + 0x33, 0xF0, 0x13, 0xA5, 0x32, 0xD0, 0x0F, 0xAD, + 0x02, 0x7C, 0x29, 0x10, 0xD0, 0x08, 0xBD, 0x00, + 0x06, 0x8D, 0x00, 0x64, 0xE6, 0x34, 0xAD, 0x02, + 0x6C, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, + 0x1B, 0xAD, 0x00, 0x6C, 0xD0, 0x32, 0xA6, 0x3C, + 0xA9, 0x01, 0x9D, 0x00, 0x15, 0xA9, 0x01, 0x9D, + 0x00, 0x0E, 0xE8, 0xE4, 0x3D, 0xF0, 0x02, 0x86, + 0x3C, 0x4C, 0xAE, 0x3C, 0xA6, 0x3C, 0xAD, 0x00, + 0x6C, 0x9D, 0x00, 0x0E, 0x9E, 0x00, 0x15, 0xE8, + 0xE4, 0x3D, 0xF0, 0x02, 0x86, 0x3C, 0x29, 0x7F, + 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x47, 0x85, 0x3E, + 0xAD, 0x02, 0x6C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, + 0x41, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x20, + 0xD0, 0x21, 0x8E, 0x00, 0x6C, 0x64, 0x41, 0x4C, + 0xE1, 0x3C, 0xA6, 0x40, 0xE4, 0x3F, 0xF0, 0x13, + 0xA5, 0x3E, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, + 0x20, 0xD0, 0x08, 0xBD, 0x00, 0x07, 0x8D, 0x00, + 0x6C, 0xE6, 0x40, 0xAD, 0x02, 0x74, 0x89, 0x08, + 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, + 0x74, 0xD0, 0x32, 0xA6, 0x48, 0xA9, 0x01, 0x9D, + 0x00, 0x16, 0xA9, 0x01, 0x9D, 0x00, 0x0F, 0xE8, + 0xE4, 0x49, 0xF0, 0x02, 0x86, 0x48, 0x4C, 0x23, + 0x3D, 0xA6, 0x48, 0xAD, 0x00, 0x74, 0x9D, 0x00, + 0x0F, 0x9E, 0x00, 0x16, 0xE8, 0xE4, 0x49, 0xF0, + 0x02, 0x86, 0x48, 0x29, 0x7F, 0xC9, 0x13, 0xD0, + 0x04, 0xA5, 0x53, 0x85, 0x4A, 0xAD, 0x02, 0x74, + 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x4D, 0xF0, 0x0F, + 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x21, 0x8E, + 0x00, 0x74, 0x64, 0x4D, 0x4C, 0x56, 0x3D, 0xA6, + 0x4C, 0xE4, 0x4B, 0xF0, 0x13, 0xA5, 0x4A, 0xD0, + 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x08, + 0xBD, 0x00, 0x08, 0x8D, 0x00, 0x74, 0xE6, 0x4C, + 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xD0, 0x00, 0x38, + 0xCE, 0xC0, +}; diff --git a/drivers/staging/generic_serial/sx.c b/drivers/staging/generic_serial/sx.c new file mode 100644 index 0000000..1291462 --- /dev/null +++ b/drivers/staging/generic_serial/sx.c @@ -0,0 +1,2894 @@ +/* sx.c -- driver for the Specialix SX series cards. + * + * This driver will also support the older SI, and XIO cards. + * + * + * (C) 1998 - 2004 R.E.Wolff@BitWizard.nl + * + * Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous + * version of this driver. Some fragments may have been copied. (none + * yet :-) + * + * Specialix pays for the development and support of this driver. + * Please DO contact support@specialix.co.uk if you require + * support. But please read the documentation (sx.txt) first. + * + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * Revision history: + * Revision 1.33 2000/03/09 10:00:00 pvdl,wolff + * - Fixed module and port counting + * - Fixed signal handling + * - Fixed an Ooops + * + * Revision 1.32 2000/03/07 09:00:00 wolff,pvdl + * - Fixed some sx_dprintk typos + * - added detection for an invalid board/module configuration + * + * Revision 1.31 2000/03/06 12:00:00 wolff,pvdl + * - Added support for EISA + * + * Revision 1.30 2000/01/21 17:43:06 wolff + * - Added support for SX+ + * + * Revision 1.26 1999/08/05 15:22:14 wolff + * - Port to 2.3.x + * - Reformatted to Linus' liking. + * + * Revision 1.25 1999/07/30 14:24:08 wolff + * Had accidentally left "gs_debug" set to "-1" instead of "off" (=0). + * + * Revision 1.24 1999/07/28 09:41:52 wolff + * - I noticed the remark about use-count straying in sx.txt. I checked + * sx_open, and found a few places where that could happen. I hope it's + * fixed now. + * + * Revision 1.23 1999/07/28 08:56:06 wolff + * - Fixed crash when sx_firmware run twice. + * - Added sx_slowpoll as a module parameter (I guess nobody really wanted + * to change it from the default... ) + * - Fixed a stupid editing problem I introduced in 1.22. + * - Fixed dropping characters on a termios change. + * + * Revision 1.22 1999/07/26 21:01:43 wolff + * Russell Brown noticed that I had overlooked 4 out of six modem control + * signals in sx_getsignals. Ooops. + * + * Revision 1.21 1999/07/23 09:11:33 wolff + * I forgot to free dynamically allocated memory when the driver is unloaded. + * + * Revision 1.20 1999/07/20 06:25:26 wolff + * The "closing wait" wasn't honoured. Thanks to James Griffiths for + * reporting this. + * + * Revision 1.19 1999/07/11 08:59:59 wolff + * Fixed an oops in close, when an open was pending. Changed the memtest + * a bit. Should also test the board in word-mode, however my card fails the + * memtest then. I still have to figure out what is wrong... + * + * Revision 1.18 1999/06/10 09:38:42 wolff + * Changed the format of the firmware revision from %04x to %x.%02x . + * + * Revision 1.17 1999/06/04 09:44:35 wolff + * fixed problem: reference to pci stuff when config_pci was off... + * Thanks to Jorge Novo for noticing this. + * + * Revision 1.16 1999/06/02 08:30:15 wolff + * added/removed the workaround for the DCD bug in the Firmware. + * A bit more debugging code to locate that... + * + * Revision 1.15 1999/06/01 11:35:30 wolff + * when DCD is left low (floating?), on TA's the firmware first tells us + * that DCD is high, but after a short while suddenly comes to the + * conclusion that it is low. All this would be fine, if it weren't that + * Unix requires us to send a "hangup" signal in that case. This usually + * all happens BEFORE the program has had a chance to ioctl the device + * into clocal mode.. + * + * Revision 1.14 1999/05/25 11:18:59 wolff + * Added PCI-fix. + * Added checks for return code of sx_sendcommand. + * Don't issue "reconfig" if port isn't open yet. (bit us on TA modules...) + * + * Revision 1.13 1999/04/29 15:18:01 wolff + * Fixed an "oops" that showed on SuSE 6.0 systems. + * Activate DTR again after stty 0. + * + * Revision 1.12 1999/04/29 07:49:52 wolff + * Improved "stty 0" handling a bit. (used to change baud to 9600 assuming + * the connection would be dropped anyway. That is not always the case, + * and confuses people). + * Told the card to always monitor the modem signals. + * Added support for dynamic gs_debug adjustments. + * Now tells the rest of the system the number of ports. + * + * Revision 1.11 1999/04/24 11:11:30 wolff + * Fixed two stupid typos in the memory test. + * + * Revision 1.10 1999/04/24 10:53:39 wolff + * Added some of Christian's suggestions. + * Fixed an HW_COOK_IN bug (ISIG was not in I_OTHER. We used to trust the + * card to send the signal to the process.....) + * + * Revision 1.9 1999/04/23 07:26:38 wolff + * Included Christian Lademann's 2.0 compile-warning fixes and interrupt + * assignment redesign. + * Cleanup of some other stuff. + * + * Revision 1.8 1999/04/16 13:05:30 wolff + * fixed a DCD change unnoticed bug. + * + * Revision 1.7 1999/04/14 22:19:51 wolff + * Fixed typo that showed up in 2.0.x builds (get_user instead of Get_user!) + * + * Revision 1.6 1999/04/13 18:40:20 wolff + * changed misc-minor to 161, as assigned by HPA. + * + * Revision 1.5 1999/04/13 15:12:25 wolff + * Fixed use-count leak when "hangup" occurred. + * Added workaround for a stupid-PCIBIOS bug. + * + * + * Revision 1.4 1999/04/01 22:47:40 wolff + * Fixed < 1M linux-2.0 problem. + * (vremap isn't compatible with ioremap in that case) + * + * Revision 1.3 1999/03/31 13:45:45 wolff + * Firmware loading is now done through a separate IOCTL. + * + * Revision 1.2 1999/03/28 12:22:29 wolff + * rcs cleanup + * + * Revision 1.1 1999/03/28 12:10:34 wolff + * Readying for release on 2.0.x (sorry David, 1.01 becomes 1.1 for RCS). + * + * Revision 0.12 1999/03/28 09:20:10 wolff + * Fixed problem in 0.11, continueing cleanup. + * + * Revision 0.11 1999/03/28 08:46:44 wolff + * cleanup. Not good. + * + * Revision 0.10 1999/03/28 08:09:43 wolff + * Fixed loosing characters on close. + * + * Revision 0.9 1999/03/21 22:52:01 wolff + * Ported back to 2.2.... (minor things) + * + * Revision 0.8 1999/03/21 22:40:33 wolff + * Port to 2.0 + * + * Revision 0.7 1999/03/21 19:06:34 wolff + * Fixed hangup processing. + * + * Revision 0.6 1999/02/05 08:45:14 wolff + * fixed real_raw problems. Inclusion into kernel imminent. + * + * Revision 0.5 1998/12/21 23:51:06 wolff + * Snatched a nasty bug: sx_transmit_chars was getting re-entered, and it + * shouldn't have. THATs why I want to have transmit interrupts even when + * the buffer is empty. + * + * Revision 0.4 1998/12/17 09:34:46 wolff + * PPP works. ioctl works. Basically works! + * + * Revision 0.3 1998/12/15 13:05:18 wolff + * It works! Wow! Gotta start implementing IOCTL and stuff.... + * + * Revision 0.2 1998/12/01 08:33:53 wolff + * moved over to 2.1.130 + * + * Revision 0.1 1998/11/03 21:23:51 wolff + * Initial revision. Detects SX card. + * + * */ + +#define SX_VERSION 1.33 + +#include <linux/module.h> +#include <linux/kdev_t.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/eisa.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + +/* The 3.0.0 version of sxboards/sxwindow.h uses BYTE and WORD.... */ +#define BYTE u8 +#define WORD u16 + +/* .... but the 3.0.4 version uses _u8 and _u16. */ +#define _u8 u8 +#define _u16 u16 + +#include "sxboards.h" +#include "sxwindow.h" + +#include <linux/generic_serial.h> +#include "sx.h" + +/* I don't think that this driver can handle more than 256 ports on + one machine. You'll have to increase the number of boards in sx.h + if you want more than 4 boards. */ + +#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 +#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000 +#endif + +/* Configurable options: + (Don't be too sure that it'll work if you toggle them) */ + +/* Am I paranoid or not ? ;-) */ +#undef SX_PARANOIA_CHECK + +/* 20 -> 2000 per second. The card should rate-limit interrupts at 100 + Hz, but it is user configurable. I don't recommend going above 1000 + Hz. The interrupt ratelimit might trigger if the interrupt is + shared with a very active other device. */ +#define IRQ_RATE_LIMIT 20 + +/* Sharing interrupts is possible now. If the other device wants more + than 2000 interrupts per second, we'd gracefully decline further + interrupts. That's not what we want. On the other hand, if the + other device interrupts 2000 times a second, don't use the SX + interrupt. Use polling. */ +#undef IRQ_RATE_LIMIT + +#if 0 +/* Not implemented */ +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#define SX_REPORT_FIFO +#define SX_REPORT_OVERRUN +#endif + +/* Function prototypes */ +static void sx_disable_tx_interrupts(void *ptr); +static void sx_enable_tx_interrupts(void *ptr); +static void sx_disable_rx_interrupts(void *ptr); +static void sx_enable_rx_interrupts(void *ptr); +static int sx_carrier_raised(struct tty_port *port); +static void sx_shutdown_port(void *ptr); +static int sx_set_real_termios(void *ptr); +static void sx_close(void *ptr); +static int sx_chars_in_buffer(void *ptr); +static int sx_init_board(struct sx_board *board); +static int sx_init_portstructs(int nboards, int nports); +static long sx_fw_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); +static int sx_init_drivers(void); + +static struct tty_driver *sx_driver; + +static DEFINE_MUTEX(sx_boards_lock); +static struct sx_board boards[SX_NBOARDS]; +static struct sx_port *sx_ports; +static int sx_initialized; +static int sx_nports; +static int sx_debug; + +/* You can have the driver poll your card. + - Set sx_poll to 1 to poll every timer tick (10ms on Intel). + This is used when the card cannot use an interrupt for some reason. + + - set sx_slowpoll to 100 to do an extra poll once a second (on Intel). If + the driver misses an interrupt (report this if it DOES happen to you!) + everything will continue to work.... + */ +static int sx_poll = 1; +static int sx_slowpoll; + +/* The card limits the number of interrupts per second. + At 115k2 "100" should be sufficient. + If you're using higher baudrates, you can increase this... + */ + +static int sx_maxints = 100; + +#ifdef CONFIG_ISA + +/* These are the only open spaces in my computer. Yours may have more + or less.... -- REW + duh: Card at 0xa0000 is possible on HP Netserver?? -- pvdl +*/ +static int sx_probe_addrs[] = { + 0xc0000, 0xd0000, 0xe0000, + 0xc8000, 0xd8000, 0xe8000 +}; +static int si_probe_addrs[] = { + 0xc0000, 0xd0000, 0xe0000, + 0xc8000, 0xd8000, 0xe8000, 0xa0000 +}; +static int si1_probe_addrs[] = { + 0xd0000 +}; + +#define NR_SX_ADDRS ARRAY_SIZE(sx_probe_addrs) +#define NR_SI_ADDRS ARRAY_SIZE(si_probe_addrs) +#define NR_SI1_ADDRS ARRAY_SIZE(si1_probe_addrs) + +module_param_array(sx_probe_addrs, int, NULL, 0); +module_param_array(si_probe_addrs, int, NULL, 0); +#endif + +/* Set the mask to all-ones. This alas, only supports 32 interrupts. + Some architectures may need more. */ +static int sx_irqmask = -1; + +module_param(sx_poll, int, 0); +module_param(sx_slowpoll, int, 0); +module_param(sx_maxints, int, 0); +module_param(sx_debug, int, 0); +module_param(sx_irqmask, int, 0); + +MODULE_LICENSE("GPL"); + +static struct real_driver sx_real_driver = { + sx_disable_tx_interrupts, + sx_enable_tx_interrupts, + sx_disable_rx_interrupts, + sx_enable_rx_interrupts, + sx_shutdown_port, + sx_set_real_termios, + sx_chars_in_buffer, + sx_close, +}; + +/* + 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 sx_dprintk(f, str...) if (sx_debug & f) printk (str) +#else +#define sx_dprintk(f, str...) /* nothing */ +#endif + +#define func_enter() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s\n",__func__) +#define func_exit() sx_dprintk(SX_DEBUG_FLOW, "sx: exit %s\n",__func__) + +#define func_enter2() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s (port %d)\n", \ + __func__, port->line) + +/* + * Firmware loader driver specific routines + * + */ + +static const struct file_operations sx_fw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = sx_fw_ioctl, + .llseek = noop_llseek, +}; + +static struct miscdevice sx_fw_device = { + SXCTL_MISC_MINOR, "sxctl", &sx_fw_fops +}; + +#ifdef SX_PARANOIA_CHECK + +/* This doesn't work. Who's paranoid around here? Not me! */ + +static inline int sx_paranoia_check(struct sx_port const *port, + char *name, const char *routine) +{ + static const char *badmagic = KERN_ERR "sx: Warning: bad sx port magic " + "number for device %s in %s\n"; + static const char *badinfo = KERN_ERR "sx: Warning: null sx port for " + "device %s in %s\n"; + + if (!port) { + printk(badinfo, name, routine); + return 1; + } + if (port->magic != SX_MAGIC) { + printk(badmagic, name, routine); + return 1; + } + + return 0; +} +#else +#define sx_paranoia_check(a,b,c) 0 +#endif + +/* The timeouts. First try 30 times as fast as possible. Then give + the card some time to breathe between accesses. (Otherwise the + processor on the card might not be able to access its OWN bus... */ + +#define TIMEOUT_1 30 +#define TIMEOUT_2 1000000 + +#ifdef DEBUG +static void my_hd_io(void __iomem *p, int len) +{ + int i, j, ch; + unsigned char __iomem *addr = p; + + for (i = 0; i < len; i += 16) { + printk("%p ", addr + i); + for (j = 0; j < 16; j++) { + printk("%02x %s", readb(addr + j + i), + (j == 7) ? " " : ""); + } + for (j = 0; j < 16; j++) { + ch = readb(addr + j + i); + printk("%c", (ch < 0x20) ? '.' : + ((ch > 0x7f) ? '.' : ch)); + } + printk("\n"); + } +} +static void my_hd(void *p, int len) +{ + int i, j, ch; + unsigned char *addr = p; + + for (i = 0; i < len; i += 16) { + printk("%p ", 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 + +/* This needs redoing for Alpha -- REW -- Done. */ + +static inline void write_sx_byte(struct sx_board *board, int offset, u8 byte) +{ + writeb(byte, board->base + offset); +} + +static inline u8 read_sx_byte(struct sx_board *board, int offset) +{ + return readb(board->base + offset); +} + +static inline void write_sx_word(struct sx_board *board, int offset, u16 word) +{ + writew(word, board->base + offset); +} + +static inline u16 read_sx_word(struct sx_board *board, int offset) +{ + return readw(board->base + offset); +} + +static int sx_busy_wait_eq(struct sx_board *board, + int offset, int mask, int correctval) +{ + int i; + + func_enter(); + + for (i = 0; i < TIMEOUT_1; i++) + if ((read_sx_byte(board, offset) & mask) == correctval) { + func_exit(); + return 1; + } + + for (i = 0; i < TIMEOUT_2; i++) { + if ((read_sx_byte(board, offset) & mask) == correctval) { + func_exit(); + return 1; + } + udelay(1); + } + + func_exit(); + return 0; +} + +static int sx_busy_wait_neq(struct sx_board *board, + int offset, int mask, int badval) +{ + int i; + + func_enter(); + + for (i = 0; i < TIMEOUT_1; i++) + if ((read_sx_byte(board, offset) & mask) != badval) { + func_exit(); + return 1; + } + + for (i = 0; i < TIMEOUT_2; i++) { + if ((read_sx_byte(board, offset) & mask) != badval) { + func_exit(); + return 1; + } + udelay(1); + } + + func_exit(); + return 0; +} + +/* 5.6.4 of 6210028 r2.3 */ +static int sx_reset(struct sx_board *board) +{ + func_enter(); + + if (IS_SX_BOARD(board)) { + + write_sx_byte(board, SX_CONFIG, 0); + write_sx_byte(board, SX_RESET, 1); /* Value doesn't matter */ + + if (!sx_busy_wait_eq(board, SX_RESET_STATUS, 1, 0)) { + printk(KERN_INFO "sx: Card doesn't respond to " + "reset...\n"); + return 0; + } + } else if (IS_EISA_BOARD(board)) { + outb(board->irq << 4, board->eisa_base + 0xc02); + } else if (IS_SI1_BOARD(board)) { + write_sx_byte(board, SI1_ISA_RESET, 0); /*value doesn't matter*/ + } else { + /* Gory details of the SI/ISA board */ + write_sx_byte(board, SI2_ISA_RESET, SI2_ISA_RESET_SET); + write_sx_byte(board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_CLEAR); + write_sx_byte(board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_CLEAR); + write_sx_byte(board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_CLEAR); + write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR); + write_sx_byte(board, SI2_ISA_IRQSET, SI2_ISA_IRQSET_CLEAR); + } + + func_exit(); + return 1; +} + +/* This doesn't work on machines where "NULL" isn't 0 */ +/* If you have one of those, someone will need to write + the equivalent of this, which will amount to about 3 lines. I don't + want to complicate this right now. -- REW + (See, I do write comments every now and then :-) */ +#define OFFSETOF(strct, elem) ((long)&(((struct strct *)NULL)->elem)) + +#define CHAN_OFFSET(port,elem) (port->ch_base + OFFSETOF (_SXCHANNEL, elem)) +#define MODU_OFFSET(board,addr,elem) (addr + OFFSETOF (_SXMODULE, elem)) +#define BRD_OFFSET(board,elem) (OFFSETOF (_SXCARD, elem)) + +#define sx_write_channel_byte(port, elem, val) \ + write_sx_byte (port->board, CHAN_OFFSET (port, elem), val) + +#define sx_read_channel_byte(port, elem) \ + read_sx_byte (port->board, CHAN_OFFSET (port, elem)) + +#define sx_write_channel_word(port, elem, val) \ + write_sx_word (port->board, CHAN_OFFSET (port, elem), val) + +#define sx_read_channel_word(port, elem) \ + read_sx_word (port->board, CHAN_OFFSET (port, elem)) + +#define sx_write_module_byte(board, addr, elem, val) \ + write_sx_byte (board, MODU_OFFSET (board, addr, elem), val) + +#define sx_read_module_byte(board, addr, elem) \ + read_sx_byte (board, MODU_OFFSET (board, addr, elem)) + +#define sx_write_module_word(board, addr, elem, val) \ + write_sx_word (board, MODU_OFFSET (board, addr, elem), val) + +#define sx_read_module_word(board, addr, elem) \ + read_sx_word (board, MODU_OFFSET (board, addr, elem)) + +#define sx_write_board_byte(board, elem, val) \ + write_sx_byte (board, BRD_OFFSET (board, elem), val) + +#define sx_read_board_byte(board, elem) \ + read_sx_byte (board, BRD_OFFSET (board, elem)) + +#define sx_write_board_word(board, elem, val) \ + write_sx_word (board, BRD_OFFSET (board, elem), val) + +#define sx_read_board_word(board, elem) \ + read_sx_word (board, BRD_OFFSET (board, elem)) + +static int sx_start_board(struct sx_board *board) +{ + if (IS_SX_BOARD(board)) { + write_sx_byte(board, SX_CONFIG, SX_CONF_BUSEN); + } else if (IS_EISA_BOARD(board)) { + write_sx_byte(board, SI2_EISA_OFF, SI2_EISA_VAL); + outb((board->irq << 4) | 4, board->eisa_base + 0xc02); + } else if (IS_SI1_BOARD(board)) { + write_sx_byte(board, SI1_ISA_RESET_CLEAR, 0); + write_sx_byte(board, SI1_ISA_INTCL, 0); + } else { + /* Don't bug me about the clear_set. + I haven't the foggiest idea what it's about -- REW */ + write_sx_byte(board, SI2_ISA_RESET, SI2_ISA_RESET_CLEAR); + write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET); + } + return 1; +} + +#define SX_IRQ_REG_VAL(board) \ + ((board->flags & SX_ISA_BOARD) ? (board->irq << 4) : 0) + +/* Note. The SX register is write-only. Therefore, we have to enable the + bus too. This is a no-op, if you don't mess with this driver... */ +static int sx_start_interrupts(struct sx_board *board) +{ + + /* Don't call this with board->irq == 0 */ + + if (IS_SX_BOARD(board)) { + write_sx_byte(board, SX_CONFIG, SX_IRQ_REG_VAL(board) | + SX_CONF_BUSEN | SX_CONF_HOSTIRQ); + } else if (IS_EISA_BOARD(board)) { + inb(board->eisa_base + 0xc03); + } else if (IS_SI1_BOARD(board)) { + write_sx_byte(board, SI1_ISA_INTCL, 0); + write_sx_byte(board, SI1_ISA_INTCL_CLEAR, 0); + } else { + switch (board->irq) { + case 11: + write_sx_byte(board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET); + break; + case 12: + write_sx_byte(board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_SET); + break; + case 15: + write_sx_byte(board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_SET); + break; + default: + printk(KERN_INFO "sx: SI/XIO card doesn't support " + "interrupt %d.\n", board->irq); + return 0; + } + write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET); + } + + return 1; +} + +static int sx_send_command(struct sx_port *port, + int command, int mask, int newstat) +{ + func_enter2(); + write_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat), command); + func_exit(); + return sx_busy_wait_eq(port->board, CHAN_OFFSET(port, hi_hstat), mask, + newstat); +} + +static char *mod_type_s(int module_type) +{ + switch (module_type) { + case TA4: + return "TA4"; + case TA8: + return "TA8"; + case TA4_ASIC: + return "TA4_ASIC"; + case TA8_ASIC: + return "TA8_ASIC"; + case MTA_CD1400: + return "MTA_CD1400"; + case SXDC: + return "SXDC"; + default: + return "Unknown/invalid"; + } +} + +static char *pan_type_s(int pan_type) +{ + switch (pan_type) { + case MOD_RS232DB25: + return "MOD_RS232DB25"; + case MOD_RS232RJ45: + return "MOD_RS232RJ45"; + case MOD_RS422DB25: + return "MOD_RS422DB25"; + case MOD_PARALLEL: + return "MOD_PARALLEL"; + case MOD_2_RS232DB25: + return "MOD_2_RS232DB25"; + case MOD_2_RS232RJ45: + return "MOD_2_RS232RJ45"; + case MOD_2_RS422DB25: + return "MOD_2_RS422DB25"; + case MOD_RS232DB25MALE: + return "MOD_RS232DB25MALE"; + case MOD_2_PARALLEL: + return "MOD_2_PARALLEL"; + case MOD_BLANK: + return "empty"; + default: + return "invalid"; + } +} + +static int mod_compat_type(int module_type) +{ + return module_type >> 4; +} + +static void sx_reconfigure_port(struct sx_port *port) +{ + if (sx_read_channel_byte(port, hi_hstat) == HS_IDLE_OPEN) { + if (sx_send_command(port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) { + printk(KERN_WARNING "sx: Sent reconfigure command, but " + "card didn't react.\n"); + } + } else { + sx_dprintk(SX_DEBUG_TERMIOS, "sx: Not sending reconfigure: " + "port isn't open (%02x).\n", + sx_read_channel_byte(port, hi_hstat)); + } +} + +static void sx_setsignals(struct sx_port *port, int dtr, int rts) +{ + int t; + func_enter2(); + + t = sx_read_channel_byte(port, hi_op); + if (dtr >= 0) + t = dtr ? (t | OP_DTR) : (t & ~OP_DTR); + if (rts >= 0) + t = rts ? (t | OP_RTS) : (t & ~OP_RTS); + sx_write_channel_byte(port, hi_op, t); + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "setsignals: %d/%d\n", dtr, rts); + + func_exit(); +} + +static int sx_getsignals(struct sx_port *port) +{ + int i_stat, o_stat; + + o_stat = sx_read_channel_byte(port, hi_op); + i_stat = sx_read_channel_byte(port, hi_ip); + + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "getsignals: %d/%d (%d/%d) " + "%02x/%02x\n", + (o_stat & OP_DTR) != 0, (o_stat & OP_RTS) != 0, + port->c_dcd, tty_port_carrier_raised(&port->gs.port), + sx_read_channel_byte(port, hi_ip), + sx_read_channel_byte(port, hi_state)); + + return (((o_stat & OP_DTR) ? TIOCM_DTR : 0) | + ((o_stat & OP_RTS) ? TIOCM_RTS : 0) | + ((i_stat & IP_CTS) ? TIOCM_CTS : 0) | + ((i_stat & IP_DCD) ? TIOCM_CAR : 0) | + ((i_stat & IP_DSR) ? TIOCM_DSR : 0) | + ((i_stat & IP_RI) ? TIOCM_RNG : 0)); +} + +static void sx_set_baud(struct sx_port *port) +{ + int t; + + if (port->board->ta_type == MOD_SXDC) { + switch (port->gs.baud) { + /* Save some typing work... */ +#define e(x) case x: t = BAUD_ ## x; break + e(50); + e(75); + e(110); + e(150); + e(200); + e(300); + e(600); + e(1200); + e(1800); + e(2000); + e(2400); + e(4800); + e(7200); + e(9600); + e(14400); + e(19200); + e(28800); + e(38400); + e(56000); + e(57600); + e(64000); + e(76800); + e(115200); + e(128000); + e(150000); + e(230400); + e(256000); + e(460800); + e(921600); + case 134: + t = BAUD_134_5; + break; + case 0: + t = -1; + break; + default: + /* Can I return "invalid"? */ + t = BAUD_9600; + printk(KERN_INFO "sx: unsupported baud rate: %d.\n", + port->gs.baud); + break; + } +#undef e + if (t > 0) { +/* The baud rate is not set to 0, so we're enabeling DTR... -- REW */ + sx_setsignals(port, 1, -1); + /* XXX This is not TA & MTA compatible */ + sx_write_channel_byte(port, hi_csr, 0xff); + + sx_write_channel_byte(port, hi_txbaud, t); + sx_write_channel_byte(port, hi_rxbaud, t); + } else { + sx_setsignals(port, 0, -1); + } + } else { + switch (port->gs.baud) { +#define e(x) case x: t = CSR_ ## x; break + e(75); + e(150); + e(300); + e(600); + e(1200); + e(2400); + e(4800); + e(1800); + e(9600); + e(19200); + e(57600); + e(38400); +/* TA supports 110, but not 115200, MTA supports 115200, but not 110 */ + case 110: + if (port->board->ta_type == MOD_TA) { + t = CSR_110; + break; + } else { + t = CSR_9600; + printk(KERN_INFO "sx: Unsupported baud rate: " + "%d.\n", port->gs.baud); + break; + } + case 115200: + if (port->board->ta_type == MOD_TA) { + t = CSR_9600; + printk(KERN_INFO "sx: Unsupported baud rate: " + "%d.\n", port->gs.baud); + break; + } else { + t = CSR_110; + break; + } + case 0: + t = -1; + break; + default: + t = CSR_9600; + printk(KERN_INFO "sx: Unsupported baud rate: %d.\n", + port->gs.baud); + break; + } +#undef e + if (t >= 0) { + sx_setsignals(port, 1, -1); + sx_write_channel_byte(port, hi_csr, t * 0x11); + } else { + sx_setsignals(port, 0, -1); + } + } +} + +/* Simon Allen's version of this routine was 225 lines long. 85 is a lot + better. -- REW */ + +static int sx_set_real_termios(void *ptr) +{ + struct sx_port *port = ptr; + + func_enter2(); + + if (!port->gs.port.tty) + return 0; + + /* What is this doing here? -- REW + Ha! figured it out. It is to allow you to get DTR active again + if you've dropped it with stty 0. Moved to set_baud, where it + belongs (next to the drop dtr if baud == 0) -- REW */ + /* sx_setsignals (port, 1, -1); */ + + sx_set_baud(port); + +#define CFLAG port->gs.port.tty->termios->c_cflag + sx_write_channel_byte(port, hi_mr1, + (C_PARENB(port->gs.port.tty) ? MR1_WITH : MR1_NONE) | + (C_PARODD(port->gs.port.tty) ? MR1_ODD : MR1_EVEN) | + (C_CRTSCTS(port->gs.port.tty) ? MR1_RTS_RXFLOW : 0) | + (((CFLAG & CSIZE) == CS8) ? MR1_8_BITS : 0) | + (((CFLAG & CSIZE) == CS7) ? MR1_7_BITS : 0) | + (((CFLAG & CSIZE) == CS6) ? MR1_6_BITS : 0) | + (((CFLAG & CSIZE) == CS5) ? MR1_5_BITS : 0)); + + sx_write_channel_byte(port, hi_mr2, + (C_CRTSCTS(port->gs.port.tty) ? MR2_CTS_TXFLOW : 0) | + (C_CSTOPB(port->gs.port.tty) ? MR2_2_STOP : + MR2_1_STOP)); + + switch (CFLAG & CSIZE) { + case CS8: + sx_write_channel_byte(port, hi_mask, 0xff); + break; + case CS7: + sx_write_channel_byte(port, hi_mask, 0x7f); + break; + case CS6: + sx_write_channel_byte(port, hi_mask, 0x3f); + break; + case CS5: + sx_write_channel_byte(port, hi_mask, 0x1f); + break; + default: + printk(KERN_INFO "sx: Invalid wordsize: %u\n", + (unsigned int)CFLAG & CSIZE); + break; + } + + sx_write_channel_byte(port, hi_prtcl, + (I_IXON(port->gs.port.tty) ? SP_TXEN : 0) | + (I_IXOFF(port->gs.port.tty) ? SP_RXEN : 0) | + (I_IXANY(port->gs.port.tty) ? SP_TANY : 0) | SP_DCEN); + + sx_write_channel_byte(port, hi_break, + (I_IGNBRK(port->gs.port.tty) ? BR_IGN : 0 | + I_BRKINT(port->gs.port.tty) ? BR_INT : 0)); + + sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.port.tty)); + + sx_reconfigure_port(port); + + /* Tell line discipline whether we will do input cooking */ + if (I_OTHER(port->gs.port.tty)) { + clear_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); + } else { + set_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); + } + sx_dprintk(SX_DEBUG_TERMIOS, "iflags: %x(%d) ", + (unsigned int)port->gs.port.tty->termios->c_iflag, + I_OTHER(port->gs.port.tty)); + +/* Tell line discipline whether we will do output cooking. + * If OPOST is set and no other output flags are set then we can do output + * processing. Even if only *one* other flag in the O_OTHER group is set + * we do cooking in software. + */ + if (O_OPOST(port->gs.port.tty) && !O_OTHER(port->gs.port.tty)) { + set_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); + } else { + clear_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); + } + sx_dprintk(SX_DEBUG_TERMIOS, "oflags: %x(%d)\n", + (unsigned int)port->gs.port.tty->termios->c_oflag, + O_OTHER(port->gs.port.tty)); + /* port->c_dcd = sx_get_CD (port); */ + func_exit(); + return 0; +} + +/* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + +/* Note: + Other drivers use the macro "MIN" to calculate how much to copy. + This has the disadvantage that it will evaluate parts twice. That's + expensive when it's IO (and the compiler cannot optimize those away!). + Moreover, I'm not sure that you're race-free. + + I assign a value, and then only allow the value to decrease. This + is always safe. This makes the code a few lines longer, and you + know I'm dead against that, but I think it is required in this + case. */ + +static void sx_transmit_chars(struct sx_port *port) +{ + int c; + int tx_ip; + int txroom; + + func_enter2(); + sx_dprintk(SX_DEBUG_TRANSMIT, "Port %p: transmit %d chars\n", + port, port->gs.xmit_cnt); + + if (test_and_set_bit(SX_PORT_TRANSMIT_LOCK, &port->locks)) { + return; + } + + while (1) { + c = port->gs.xmit_cnt; + + sx_dprintk(SX_DEBUG_TRANSMIT, "Copying %d ", c); + tx_ip = sx_read_channel_byte(port, hi_txipos); + + /* Took me 5 minutes to deduce this formula. + Luckily it is literally in the manual in section 6.5.4.3.5 */ + txroom = (sx_read_channel_byte(port, hi_txopos) - tx_ip - 1) & + 0xff; + + /* Don't copy more bytes than there is room for in the buffer */ + if (c > txroom) + c = txroom; + sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%d) ", c, txroom); + + /* Don't copy past the end of the hardware transmit buffer */ + if (c > 0x100 - tx_ip) + c = 0x100 - tx_ip; + + sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%d) ", c, 0x100 - tx_ip); + + /* Don't copy pas the end of the source buffer */ + if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail) + c = SERIAL_XMIT_SIZE - port->gs.xmit_tail; + + sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%ld) \n", + c, SERIAL_XMIT_SIZE - port->gs.xmit_tail); + + /* If for one reason or another, we can't copy more data, we're + done! */ + if (c == 0) + break; + + memcpy_toio(port->board->base + CHAN_OFFSET(port, hi_txbuf) + + tx_ip, port->gs.xmit_buf + port->gs.xmit_tail, c); + + /* Update the pointer in the card */ + sx_write_channel_byte(port, hi_txipos, (tx_ip + c) & 0xff); + + /* Update the kernel buffer end */ + port->gs.xmit_tail = (port->gs.xmit_tail + c) & + (SERIAL_XMIT_SIZE - 1); + + /* This one last. (this is essential) + It would allow others to start putting more data into the + buffer! */ + port->gs.xmit_cnt -= c; + } + + if (port->gs.xmit_cnt == 0) { + sx_disable_tx_interrupts(port); + } + + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) { + tty_wakeup(port->gs.port.tty); + sx_dprintk(SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", + port->gs.wakeup_chars); + } + + clear_bit(SX_PORT_TRANSMIT_LOCK, &port->locks); + func_exit(); +} + +/* Note the symmetry between receiving chars and transmitting them! + Note: The kernel should have implemented both a receive buffer and + a transmit buffer. */ + +/* Inlined: Called only once. Remove the inline when you add another call */ +static inline void sx_receive_chars(struct sx_port *port) +{ + int c; + int rx_op; + struct tty_struct *tty; + int copied = 0; + unsigned char *rp; + + func_enter2(); + tty = port->gs.port.tty; + while (1) { + rx_op = sx_read_channel_byte(port, hi_rxopos); + c = (sx_read_channel_byte(port, hi_rxipos) - rx_op) & 0xff; + + sx_dprintk(SX_DEBUG_RECEIVE, "rxop=%d, c = %d.\n", rx_op, c); + + /* Don't copy past the end of the hardware receive buffer */ + if (rx_op + c > 0x100) + c = 0x100 - rx_op; + + sx_dprintk(SX_DEBUG_RECEIVE, "c = %d.\n", c); + + /* Don't copy more bytes than there is room for in the buffer */ + + c = tty_prepare_flip_string(tty, &rp, c); + + sx_dprintk(SX_DEBUG_RECEIVE, "c = %d.\n", c); + + /* If for one reason or another, we can't copy more data, we're done! */ + if (c == 0) + break; + + sx_dprintk(SX_DEBUG_RECEIVE, "Copying over %d chars. First is " + "%d at %lx\n", c, read_sx_byte(port->board, + CHAN_OFFSET(port, hi_rxbuf) + rx_op), + CHAN_OFFSET(port, hi_rxbuf)); + memcpy_fromio(rp, port->board->base + + CHAN_OFFSET(port, hi_rxbuf) + rx_op, c); + + /* This one last. ( Not essential.) + It allows the card to start putting more data into the + buffer! + Update the pointer in the card */ + sx_write_channel_byte(port, hi_rxopos, (rx_op + c) & 0xff); + + copied += c; + } + if (copied) { + struct timeval tv; + + do_gettimeofday(&tv); + sx_dprintk(SX_DEBUG_RECEIVE, "pushing flipq port %d (%3d " + "chars): %d.%06d (%d/%d)\n", port->line, + copied, (int)(tv.tv_sec % 60), (int)tv.tv_usec, + tty->raw, tty->real_raw); + + /* Tell the rest of the system the news. Great news. New + characters! */ + tty_flip_buffer_push(tty); + /* tty_schedule_flip (tty); */ + } + + func_exit(); +} + +/* Inlined: it is called only once. Remove the inline if you add another + call */ +static inline void sx_check_modem_signals(struct sx_port *port) +{ + int hi_state; + int c_dcd; + + hi_state = sx_read_channel_byte(port, hi_state); + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "Checking modem signals (%d/%d)\n", + port->c_dcd, tty_port_carrier_raised(&port->gs.port)); + + if (hi_state & ST_BREAK) { + hi_state &= ~ST_BREAK; + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "got a break.\n"); + sx_write_channel_byte(port, hi_state, hi_state); + gs_got_break(&port->gs); + } + if (hi_state & ST_DCD) { + hi_state &= ~ST_DCD; + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "got a DCD change.\n"); + sx_write_channel_byte(port, hi_state, hi_state); + c_dcd = tty_port_carrier_raised(&port->gs.port); + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD is now %d\n", c_dcd); + if (c_dcd != port->c_dcd) { + port->c_dcd = c_dcd; + if (tty_port_carrier_raised(&port->gs.port)) { + /* DCD went UP */ + if ((sx_read_channel_byte(port, hi_hstat) != + HS_IDLE_CLOSED) && + !(port->gs.port.tty->termios-> + c_cflag & CLOCAL)) { + /* Are we blocking in open? */ + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " + "active, unblocking open\n"); + wake_up_interruptible(&port->gs.port. + open_wait); + } else { + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " + "raised. Ignoring.\n"); + } + } else { + /* DCD went down! */ + if (!(port->gs.port.tty->termios->c_cflag & CLOCAL)){ + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " + "dropped. hanging up....\n"); + tty_hangup(port->gs.port.tty); + } else { + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " + "dropped. ignoring.\n"); + } + } + } else { + sx_dprintk(SX_DEBUG_MODEMSIGNALS, "Hmmm. card told us " + "DCD changed, but it didn't.\n"); + } + } +} + +/* This is what an interrupt routine should look like. + * Small, elegant, clear. + */ + +static irqreturn_t sx_interrupt(int irq, void *ptr) +{ + struct sx_board *board = ptr; + struct sx_port *port; + int i; + + func_enter(); + sx_dprintk(SX_DEBUG_FLOW, "sx: enter sx_interrupt (%d/%d)\n", irq, + board->irq); + + /* AAargh! The order in which to do these things is essential and + not trivial. + + - Rate limit goes before "recursive". Otherwise a series of + recursive calls will hang the machine in the interrupt routine. + + - hardware twiddling goes before "recursive". Otherwise when we + poll the card, and a recursive interrupt happens, we won't + ack the card, so it might keep on interrupting us. (especially + level sensitive interrupt systems like PCI). + + - Rate limit goes before hardware twiddling. Otherwise we won't + catch a card that has gone bonkers. + + - The "initialized" test goes after the hardware twiddling. Otherwise + the card will stick us in the interrupt routine again. + + - The initialized test goes before recursive. + */ + +#ifdef IRQ_RATE_LIMIT + /* Aaargh! I'm ashamed. This costs more lines-of-code than the + actual interrupt routine!. (Well, used to when I wrote that + comment) */ + { + static int lastjif; + static int nintr = 0; + + if (lastjif == jiffies) { + if (++nintr > IRQ_RATE_LIMIT) { + free_irq(board->irq, board); + printk(KERN_ERR "sx: Too many interrupts. " + "Turning off interrupt %d.\n", + board->irq); + } + } else { + lastjif = jiffies; + nintr = 0; + } + } +#endif + + if (board->irq == irq) { + /* Tell the card we've noticed the interrupt. */ + + sx_write_board_word(board, cc_int_pending, 0); + if (IS_SX_BOARD(board)) { + write_sx_byte(board, SX_RESET_IRQ, 1); + } else if (IS_EISA_BOARD(board)) { + inb(board->eisa_base + 0xc03); + write_sx_word(board, 8, 0); + } else { + write_sx_byte(board, SI2_ISA_INTCLEAR, + SI2_ISA_INTCLEAR_CLEAR); + write_sx_byte(board, SI2_ISA_INTCLEAR, + SI2_ISA_INTCLEAR_SET); + } + } + + if (!sx_initialized) + return IRQ_HANDLED; + if (!(board->flags & SX_BOARD_INITIALIZED)) + return IRQ_HANDLED; + + if (test_and_set_bit(SX_BOARD_INTR_LOCK, &board->locks)) { + printk(KERN_ERR "Recursive interrupt! (%d)\n", board->irq); + return IRQ_HANDLED; + } + + for (i = 0; i < board->nports; i++) { + port = &board->ports[i]; + if (port->gs.port.flags & GS_ACTIVE) { + if (sx_read_channel_byte(port, hi_state)) { + sx_dprintk(SX_DEBUG_INTERRUPTS, "Port %d: " + "modem signal change?... \n",i); + sx_check_modem_signals(port); + } + if (port->gs.xmit_cnt) { + sx_transmit_chars(port); + } + if (!(port->gs.port.flags & SX_RX_THROTTLE)) { + sx_receive_chars(port); + } + } + } + + clear_bit(SX_BOARD_INTR_LOCK, &board->locks); + + sx_dprintk(SX_DEBUG_FLOW, "sx: exit sx_interrupt (%d/%d)\n", irq, + board->irq); + func_exit(); + return IRQ_HANDLED; +} + +static void sx_pollfunc(unsigned long data) +{ + struct sx_board *board = (struct sx_board *)data; + + func_enter(); + + sx_interrupt(0, board); + + mod_timer(&board->timer, jiffies + sx_poll); + func_exit(); +} + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the generic_serial driver * + * ********************************************************************** */ + +/* Ehhm. I don't know how to fiddle with interrupts on the SX card. --REW */ +/* Hmm. Ok I figured it out. You don't. */ + +static void sx_disable_tx_interrupts(void *ptr) +{ + struct sx_port *port = ptr; + func_enter2(); + + port->gs.port.flags &= ~GS_TX_INTEN; + + func_exit(); +} + +static void sx_enable_tx_interrupts(void *ptr) +{ + struct sx_port *port = ptr; + int data_in_buffer; + func_enter2(); + + /* First transmit the characters that we're supposed to */ + sx_transmit_chars(port); + + /* The sx card will never interrupt us if we don't fill the buffer + past 25%. So we keep considering interrupts off if that's the case. */ + data_in_buffer = (sx_read_channel_byte(port, hi_txipos) - + sx_read_channel_byte(port, hi_txopos)) & 0xff; + + /* XXX Must be "HIGH_WATER" for SI card according to doc. */ + if (data_in_buffer < LOW_WATER) + port->gs.port.flags &= ~GS_TX_INTEN; + + func_exit(); +} + +static void sx_disable_rx_interrupts(void *ptr) +{ + /* struct sx_port *port = ptr; */ + func_enter(); + + func_exit(); +} + +static void sx_enable_rx_interrupts(void *ptr) +{ + /* struct sx_port *port = ptr; */ + func_enter(); + + func_exit(); +} + +/* Jeez. Isn't this simple? */ +static int sx_carrier_raised(struct tty_port *port) +{ + struct sx_port *sp = container_of(port, struct sx_port, gs.port); + return ((sx_read_channel_byte(sp, hi_ip) & IP_DCD) != 0); +} + +/* Jeez. Isn't this simple? */ +static int sx_chars_in_buffer(void *ptr) +{ + struct sx_port *port = ptr; + func_enter2(); + + func_exit(); + return ((sx_read_channel_byte(port, hi_txipos) - + sx_read_channel_byte(port, hi_txopos)) & 0xff); +} + +static void sx_shutdown_port(void *ptr) +{ + struct sx_port *port = ptr; + + func_enter(); + + port->gs.port.flags &= ~GS_ACTIVE; + if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { + sx_setsignals(port, 0, 0); + sx_reconfigure_port(port); + } + + func_exit(); +} + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the rest of the system * + * ********************************************************************** */ + +static int sx_open(struct tty_struct *tty, struct file *filp) +{ + struct sx_port *port; + int retval, line; + unsigned long flags; + + func_enter(); + + if (!sx_initialized) { + return -EIO; + } + + line = tty->index; + sx_dprintk(SX_DEBUG_OPEN, "%d: opening line %d. tty=%p ctty=%p, " + "np=%d)\n", task_pid_nr(current), line, tty, + current->signal->tty, sx_nports); + + if ((line < 0) || (line >= SX_NPORTS) || (line >= sx_nports)) + return -ENODEV; + + port = &sx_ports[line]; + port->c_dcd = 0; /* Make sure that the first interrupt doesn't detect a + 1 -> 0 transition. */ + + sx_dprintk(SX_DEBUG_OPEN, "port = %p c_dcd = %d\n", port, port->c_dcd); + + spin_lock_irqsave(&port->gs.driver_lock, flags); + + tty->driver_data = port; + port->gs.port.tty = tty; + port->gs.port.count++; + spin_unlock_irqrestore(&port->gs.driver_lock, flags); + + sx_dprintk(SX_DEBUG_OPEN, "starting port\n"); + + /* + * Start up serial port + */ + retval = gs_init_port(&port->gs); + sx_dprintk(SX_DEBUG_OPEN, "done gs_init\n"); + if (retval) { + port->gs.port.count--; + return retval; + } + + port->gs.port.flags |= GS_ACTIVE; + if (port->gs.port.count <= 1) + sx_setsignals(port, 1, 1); + +#if 0 + if (sx_debug & SX_DEBUG_OPEN) + my_hd(port, sizeof(*port)); +#else + if (sx_debug & SX_DEBUG_OPEN) + my_hd_io(port->board->base + port->ch_base, sizeof(*port)); +#endif + + if (port->gs.port.count <= 1) { + if (sx_send_command(port, HS_LOPEN, -1, HS_IDLE_OPEN) != 1) { + printk(KERN_ERR "sx: Card didn't respond to LOPEN " + "command.\n"); + spin_lock_irqsave(&port->gs.driver_lock, flags); + port->gs.port.count--; + spin_unlock_irqrestore(&port->gs.driver_lock, flags); + return -EIO; + } + } + + retval = gs_block_til_ready(port, filp); + sx_dprintk(SX_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n", + retval, port->gs.port.count); + + if (retval) { +/* + * Don't lower gs.port.count here because sx_close() will be called later + */ + + return retval; + } + /* tty->low_latency = 1; */ + + port->c_dcd = sx_carrier_raised(&port->gs.port); + sx_dprintk(SX_DEBUG_OPEN, "at open: cd=%d\n", port->c_dcd); + + func_exit(); + return 0; + +} + +static void sx_close(void *ptr) +{ + struct sx_port *port = ptr; + /* Give the port 5 seconds to close down. */ + int to = 5 * HZ; + + func_enter(); + + sx_setsignals(port, 0, 0); + sx_reconfigure_port(port); + sx_send_command(port, HS_CLOSE, 0, 0); + + while (to-- && (sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED)) + if (msleep_interruptible(10)) + break; + if (sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) { + if (sx_send_command(port, HS_FORCE_CLOSED, -1, HS_IDLE_CLOSED) + != 1) { + printk(KERN_ERR "sx: sent the force_close command, but " + "card didn't react\n"); + } else + sx_dprintk(SX_DEBUG_CLOSE, "sent the force_close " + "command.\n"); + } + + sx_dprintk(SX_DEBUG_CLOSE, "waited %d jiffies for close. count=%d\n", + 5 * HZ - to - 1, port->gs.port.count); + + if (port->gs.port.count) { + sx_dprintk(SX_DEBUG_CLOSE, "WARNING port count:%d\n", + port->gs.port.count); + /*printk("%s SETTING port count to zero: %p count: %d\n", + __func__, port, port->gs.port.count); + port->gs.port.count = 0;*/ + } + + func_exit(); +} + +/* This is relatively thorough. But then again it is only 20 lines. */ +#define MARCHUP for (i = min; i < max; i++) +#define MARCHDOWN for (i = max - 1; i >= min; i--) +#define W0 write_sx_byte(board, i, 0x55) +#define W1 write_sx_byte(board, i, 0xaa) +#define R0 if (read_sx_byte(board, i) != 0x55) return 1 +#define R1 if (read_sx_byte(board, i) != 0xaa) return 1 + +/* This memtest takes a human-noticable time. You normally only do it + once a boot, so I guess that it is worth it. */ +static int do_memtest(struct sx_board *board, int min, int max) +{ + int i; + + /* This is a marchb. Theoretically, marchb catches much more than + simpler tests. In practise, the longer test just catches more + intermittent errors. -- REW + (For the theory behind memory testing see: + Testing Semiconductor Memories by A.J. van de Goor.) */ + MARCHUP { + W0; + } + MARCHUP { + R0; + W1; + R1; + W0; + R0; + W1; + } + MARCHUP { + R1; + W0; + W1; + } + MARCHDOWN { + R1; + W0; + W1; + W0; + } + MARCHDOWN { + R0; + W1; + W0; + } + + return 0; +} + +#undef MARCHUP +#undef MARCHDOWN +#undef W0 +#undef W1 +#undef R0 +#undef R1 + +#define MARCHUP for (i = min; i < max; i += 2) +#define MARCHDOWN for (i = max - 1; i >= min; i -= 2) +#define W0 write_sx_word(board, i, 0x55aa) +#define W1 write_sx_word(board, i, 0xaa55) +#define R0 if (read_sx_word(board, i) != 0x55aa) return 1 +#define R1 if (read_sx_word(board, i) != 0xaa55) return 1 + +#if 0 +/* This memtest takes a human-noticable time. You normally only do it + once a boot, so I guess that it is worth it. */ +static int do_memtest_w(struct sx_board *board, int min, int max) +{ + int i; + + MARCHUP { + W0; + } + MARCHUP { + R0; + W1; + R1; + W0; + R0; + W1; + } + MARCHUP { + R1; + W0; + W1; + } + MARCHDOWN { + R1; + W0; + W1; + W0; + } + MARCHDOWN { + R0; + W1; + W0; + } + + return 0; +} +#endif + +static long sx_fw_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + long rc = 0; + int __user *descr = (int __user *)arg; + int i; + static struct sx_board *board = NULL; + int nbytes, offset; + unsigned long data; + char *tmp; + + func_enter(); + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + tty_lock(); + + sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg); + + if (!board) + board = &boards[0]; + if (board->flags & SX_BOARD_PRESENT) { + sx_dprintk(SX_DEBUG_FIRMWARE, "Board present! (%x)\n", + board->flags); + } else { + sx_dprintk(SX_DEBUG_FIRMWARE, "Board not present! (%x) all:", + board->flags); + for (i = 0; i < SX_NBOARDS; i++) + sx_dprintk(SX_DEBUG_FIRMWARE, "<%x> ", boards[i].flags); + sx_dprintk(SX_DEBUG_FIRMWARE, "\n"); + rc = -EIO; + goto out; + } + + switch (cmd) { + case SXIO_SET_BOARD: + sx_dprintk(SX_DEBUG_FIRMWARE, "set board to %ld\n", arg); + rc = -EIO; + if (arg >= SX_NBOARDS) + break; + sx_dprintk(SX_DEBUG_FIRMWARE, "not out of range\n"); + if (!(boards[arg].flags & SX_BOARD_PRESENT)) + break; + sx_dprintk(SX_DEBUG_FIRMWARE, ".. and present!\n"); + board = &boards[arg]; + rc = 0; + /* FIXME: And this does ... nothing?? */ + break; + case SXIO_GET_TYPE: + rc = -ENOENT; /* If we manage to miss one, return error. */ + if (IS_SX_BOARD(board)) + rc = SX_TYPE_SX; + if (IS_CF_BOARD(board)) + rc = SX_TYPE_CF; + if (IS_SI_BOARD(board)) + rc = SX_TYPE_SI; + if (IS_SI1_BOARD(board)) + rc = SX_TYPE_SI; + if (IS_EISA_BOARD(board)) + rc = SX_TYPE_SI; + sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc); + break; + case SXIO_DO_RAMTEST: + if (sx_initialized) { /* Already initialized: better not ramtest the board. */ + rc = -EPERM; + break; + } + if (IS_SX_BOARD(board)) { + rc = do_memtest(board, 0, 0x7000); + if (!rc) + rc = do_memtest(board, 0, 0x7000); + /*if (!rc) rc = do_memtest_w (board, 0, 0x7000); */ + } else { + rc = do_memtest(board, 0, 0x7ff8); + /* if (!rc) rc = do_memtest_w (board, 0, 0x7ff8); */ + } + sx_dprintk(SX_DEBUG_FIRMWARE, + "returning memtest result= %ld\n", rc); + break; + case SXIO_DOWNLOAD: + if (sx_initialized) {/* Already initialized */ + rc = -EEXIST; + break; + } + if (!sx_reset(board)) { + rc = -EIO; + break; + } + sx_dprintk(SX_DEBUG_INIT, "reset the board...\n"); + + tmp = kmalloc(SX_CHUNK_SIZE, GFP_USER); + if (!tmp) { + rc = -ENOMEM; + break; + } + /* FIXME: check returns */ + get_user(nbytes, descr++); + get_user(offset, descr++); + get_user(data, descr++); + while (nbytes && data) { + for (i = 0; i < nbytes; i += SX_CHUNK_SIZE) { + if (copy_from_user(tmp, (char __user *)data + i, + (i + SX_CHUNK_SIZE > nbytes) ? + nbytes - i : SX_CHUNK_SIZE)) { + kfree(tmp); + rc = -EFAULT; + goto out; + } + memcpy_toio(board->base2 + offset + i, tmp, + (i + SX_CHUNK_SIZE > nbytes) ? + nbytes - i : SX_CHUNK_SIZE); + } + + get_user(nbytes, descr++); + get_user(offset, descr++); + get_user(data, descr++); + } + kfree(tmp); + sx_nports += sx_init_board(board); + rc = sx_nports; + break; + case SXIO_INIT: + if (sx_initialized) { /* Already initialized */ + rc = -EEXIST; + break; + } + /* This is not allowed until all boards are initialized... */ + for (i = 0; i < SX_NBOARDS; i++) { + if ((boards[i].flags & SX_BOARD_PRESENT) && + !(boards[i].flags & SX_BOARD_INITIALIZED)) { + rc = -EIO; + break; + } + } + for (i = 0; i < SX_NBOARDS; i++) + if (!(boards[i].flags & SX_BOARD_PRESENT)) + break; + + sx_dprintk(SX_DEBUG_FIRMWARE, "initing portstructs, %d boards, " + "%d channels, first board: %d ports\n", + i, sx_nports, boards[0].nports); + rc = sx_init_portstructs(i, sx_nports); + sx_init_drivers(); + if (rc >= 0) + sx_initialized++; + break; + case SXIO_SETDEBUG: + sx_debug = arg; + break; + case SXIO_GETDEBUG: + rc = sx_debug; + break; + case SXIO_GETGSDEBUG: + case SXIO_SETGSDEBUG: + rc = -EINVAL; + break; + case SXIO_GETNPORTS: + rc = sx_nports; + break; + default: + rc = -ENOTTY; + break; + } +out: + tty_unlock(); + func_exit(); + return rc; +} + +static int sx_break(struct tty_struct *tty, int flag) +{ + struct sx_port *port = tty->driver_data; + int rv; + + func_enter(); + tty_lock(); + + if (flag) + rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK); + else + rv = sx_send_command(port, HS_STOP, -1, HS_IDLE_OPEN); + if (rv != 1) + printk(KERN_ERR "sx: couldn't send break (%x).\n", + read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); + tty_unlock(); + func_exit(); + return 0; +} + +static int sx_tiocmget(struct tty_struct *tty) +{ + struct sx_port *port = tty->driver_data; + return sx_getsignals(port); +} + +static int sx_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct sx_port *port = tty->driver_data; + int rts = -1, dtr = -1; + + if (set & TIOCM_RTS) + rts = 1; + if (set & TIOCM_DTR) + dtr = 1; + if (clear & TIOCM_RTS) + rts = 0; + if (clear & TIOCM_DTR) + dtr = 0; + + sx_setsignals(port, dtr, rts); + sx_reconfigure_port(port); + return 0; +} + +static int sx_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + int rc; + struct sx_port *port = tty->driver_data; + void __user *argp = (void __user *)arg; + + /* func_enter2(); */ + + rc = 0; + tty_lock(); + switch (cmd) { + case TIOCGSERIAL: + rc = gs_getserial(&port->gs, argp); + break; + case TIOCSSERIAL: + rc = gs_setserial(&port->gs, argp); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + tty_unlock(); + + /* func_exit(); */ + return rc; +} + +/* The throttle/unthrottle scheme for the Specialix card is different + * from other drivers and deserves some explanation. + * The Specialix hardware takes care of XON/XOFF + * and CTS/RTS flow control itself. This means that all we have to + * do when signalled by the upper tty layer to throttle/unthrottle is + * to make a note of it here. When we come to read characters from the + * rx buffers on the card (sx_receive_chars()) we look to see if the + * upper layer can accept more (as noted here in sx_rx_throt[]). + * If it can't we simply don't remove chars from the cards buffer. + * When the tty layer can accept chars, we again note that here and when + * sx_receive_chars() is called it will remove them from the cards buffer. + * The card will notice that a ports buffer has drained below some low + * water mark and will unflow control the line itself, using whatever + * flow control scheme is in use for that port. -- Simon Allen + */ + +static void sx_throttle(struct tty_struct *tty) +{ + struct sx_port *port = tty->driver_data; + + func_enter2(); + /* If the port is using any type of input flow + * control then throttle the port. + */ + if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) { + port->gs.port.flags |= SX_RX_THROTTLE; + } + func_exit(); +} + +static void sx_unthrottle(struct tty_struct *tty) +{ + struct sx_port *port = tty->driver_data; + + func_enter2(); + /* Always unthrottle even if flow control is not enabled on + * this port in case we disabled flow control while the port + * was throttled + */ + port->gs.port.flags &= ~SX_RX_THROTTLE; + func_exit(); + return; +} + +/* ********************************************************************** * + * Here are the initialization routines. * + * ********************************************************************** */ + +static int sx_init_board(struct sx_board *board) +{ + int addr; + int chans; + int type; + + func_enter(); + + /* This is preceded by downloading the download code. */ + + board->flags |= SX_BOARD_INITIALIZED; + + if (read_sx_byte(board, 0)) + /* CF boards may need this. */ + write_sx_byte(board, 0, 0); + + /* This resets the processor again, to make sure it didn't do any + foolish things while we were downloading the image */ + if (!sx_reset(board)) + return 0; + + sx_start_board(board); + udelay(10); + if (!sx_busy_wait_neq(board, 0, 0xff, 0)) { + printk(KERN_ERR "sx: Ooops. Board won't initialize.\n"); + return 0; + } + + /* Ok. So now the processor on the card is running. It gathered + some info for us... */ + sx_dprintk(SX_DEBUG_INIT, "The sxcard structure:\n"); + if (sx_debug & SX_DEBUG_INIT) + my_hd_io(board->base, 0x10); + sx_dprintk(SX_DEBUG_INIT, "the first sx_module structure:\n"); + if (sx_debug & SX_DEBUG_INIT) + my_hd_io(board->base + 0x80, 0x30); + + sx_dprintk(SX_DEBUG_INIT, "init_status: %x, %dk memory, firmware " + "V%x.%02x,\n", + read_sx_byte(board, 0), read_sx_byte(board, 1), + read_sx_byte(board, 5), read_sx_byte(board, 4)); + + if (read_sx_byte(board, 0) == 0xff) { + printk(KERN_INFO "sx: No modules found. Sorry.\n"); + board->nports = 0; + return 0; + } + + chans = 0; + + if (IS_SX_BOARD(board)) { + sx_write_board_word(board, cc_int_count, sx_maxints); + } else { + if (sx_maxints) + sx_write_board_word(board, cc_int_count, + SI_PROCESSOR_CLOCK / 8 / sx_maxints); + } + + /* grab the first module type... */ + /* board->ta_type = mod_compat_type (read_sx_byte (board, 0x80 + 0x08)); */ + board->ta_type = mod_compat_type(sx_read_module_byte(board, 0x80, + mc_chip)); + + /* XXX byteorder */ + for (addr = 0x80; addr != 0; addr = read_sx_word(board, addr) & 0x7fff){ + type = sx_read_module_byte(board, addr, mc_chip); + sx_dprintk(SX_DEBUG_INIT, "Module at %x: %d channels\n", + addr, read_sx_byte(board, addr + 2)); + + chans += sx_read_module_byte(board, addr, mc_type); + + sx_dprintk(SX_DEBUG_INIT, "module is an %s, which has %s/%s " + "panels\n", + mod_type_s(type), + pan_type_s(sx_read_module_byte(board, addr, + mc_mods) & 0xf), + pan_type_s(sx_read_module_byte(board, addr, + mc_mods) >> 4)); + + sx_dprintk(SX_DEBUG_INIT, "CD1400 versions: %x/%x, ASIC " + "version: %x\n", + sx_read_module_byte(board, addr, mc_rev1), + sx_read_module_byte(board, addr, mc_rev2), + sx_read_module_byte(board, addr, mc_mtaasic_rev)); + + /* The following combinations are illegal: It should theoretically + work, but timing problems make the bus HANG. */ + + if (mod_compat_type(type) != board->ta_type) { + printk(KERN_ERR "sx: This is an invalid " + "configuration.\nDon't mix TA/MTA/SXDC on the " + "same hostadapter.\n"); + chans = 0; + break; + } + if ((IS_EISA_BOARD(board) || + IS_SI_BOARD(board)) && + (mod_compat_type(type) == 4)) { + printk(KERN_ERR "sx: This is an invalid " + "configuration.\nDon't use SXDCs on an SI/XIO " + "adapter.\n"); + chans = 0; + break; + } +#if 0 /* Problem fixed: firmware 3.05 */ + if (IS_SX_BOARD(board) && (type == TA8)) { + /* There are some issues with the firmware and the DCD/RTS + lines. It might work if you tie them together or something. + It might also work if you get a newer sx_firmware. Therefore + this is just a warning. */ + printk(KERN_WARNING + "sx: The SX host doesn't work too well " + "with the TA8 adapters.\nSpecialix is working on it.\n"); + } +#endif + } + + if (chans) { + if (board->irq > 0) { + /* fixed irq, probably PCI */ + if (sx_irqmask & (1 << board->irq)) { /* may we use this irq? */ + if (request_irq(board->irq, sx_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "sx", board)) { + printk(KERN_ERR "sx: Cannot allocate " + "irq %d.\n", board->irq); + board->irq = 0; + } + } else + board->irq = 0; + } else if (board->irq < 0 && sx_irqmask) { + /* auto-allocate irq */ + int irqnr; + int irqmask = sx_irqmask & (IS_SX_BOARD(board) ? + SX_ISA_IRQ_MASK : SI2_ISA_IRQ_MASK); + for (irqnr = 15; irqnr > 0; irqnr--) + if (irqmask & (1 << irqnr)) + if (!request_irq(irqnr, sx_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "sx", board)) + break; + if (!irqnr) + printk(KERN_ERR "sx: Cannot allocate IRQ.\n"); + board->irq = irqnr; + } else + board->irq = 0; + + if (board->irq) { + /* Found a valid interrupt, start up interrupts! */ + sx_dprintk(SX_DEBUG_INIT, "Using irq %d.\n", + board->irq); + sx_start_interrupts(board); + board->poll = sx_slowpoll; + board->flags |= SX_IRQ_ALLOCATED; + } else { + /* no irq: setup board for polled operation */ + board->poll = sx_poll; + sx_dprintk(SX_DEBUG_INIT, "Using poll-interval %d.\n", + board->poll); + } + + /* The timer should be initialized anyway: That way we can + safely del_timer it when the module is unloaded. */ + setup_timer(&board->timer, sx_pollfunc, (unsigned long)board); + + if (board->poll) + mod_timer(&board->timer, jiffies + board->poll); + } else { + board->irq = 0; + } + + board->nports = chans; + sx_dprintk(SX_DEBUG_INIT, "returning %d ports.", board->nports); + + func_exit(); + return chans; +} + +static void __devinit printheader(void) +{ + static int header_printed; + + if (!header_printed) { + printk(KERN_INFO "Specialix SX driver " + "(C) 1998/1999 R.E.Wolff@BitWizard.nl\n"); + printk(KERN_INFO "sx: version " __stringify(SX_VERSION) "\n"); + header_printed = 1; + } +} + +static int __devinit probe_sx(struct sx_board *board) +{ + struct vpd_prom vpdp; + char *p; + int i; + + func_enter(); + + if (!IS_CF_BOARD(board)) { + sx_dprintk(SX_DEBUG_PROBE, "Going to verify vpd prom at %p.\n", + board->base + SX_VPD_ROM); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd_io(board->base + SX_VPD_ROM, 0x40); + + p = (char *)&vpdp; + for (i = 0; i < sizeof(struct vpd_prom); i++) + *p++ = read_sx_byte(board, SX_VPD_ROM + i * 2); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd(&vpdp, 0x20); + + sx_dprintk(SX_DEBUG_PROBE, "checking identifier...\n"); + + if (strncmp(vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) { + sx_dprintk(SX_DEBUG_PROBE, "Got non-SX identifier: " + "'%s'\n", vpdp.identifier); + return 0; + } + } + + printheader(); + + if (!IS_CF_BOARD(board)) { + printk(KERN_DEBUG "sx: Found an SX board at %lx\n", + board->hw_base); + printk(KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, " + "uniq ID:%08x, ", + vpdp.hwrev, vpdp.hwass, vpdp.uniqid); + printk("Manufactured: %d/%d\n", 1970 + vpdp.myear, vpdp.mweek); + + if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != + SX_PCI_UNIQUEID1) && (((vpdp.uniqid >> 24) & + SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) { + /* This might be a bit harsh. This was the primary + reason the SX/ISA card didn't work at first... */ + printk(KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA " + "card. Sorry: giving up.\n"); + return (0); + } + + if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == + SX_ISA_UNIQUEID1) { + if (((unsigned long)board->hw_base) & 0x8000) { + printk(KERN_WARNING "sx: Warning: There may be " + "hardware problems with the card at " + "%lx.\n", board->hw_base); + printk(KERN_WARNING "sx: Read sx.txt for more " + "info.\n"); + } + } + } + + board->nports = -1; + + /* This resets the processor, and keeps it off the bus. */ + if (!sx_reset(board)) + return 0; + sx_dprintk(SX_DEBUG_INIT, "reset the board...\n"); + + func_exit(); + return 1; +} + +#if defined(CONFIG_ISA) || defined(CONFIG_EISA) + +/* Specialix probes for this card at 32k increments from 640k to 16M. + I consider machines with less than 16M unlikely nowadays, so I'm + not probing above 1Mb. Also, 0xa0000, 0xb0000, are taken by the VGA + card. 0xe0000 and 0xf0000 are taken by the BIOS. That only leaves + 0xc0000, 0xc8000, 0xd0000 and 0xd8000 . */ + +static int __devinit probe_si(struct sx_board *board) +{ + int i; + + func_enter(); + sx_dprintk(SX_DEBUG_PROBE, "Going to verify SI signature hw %lx at " + "%p.\n", board->hw_base, board->base + SI2_ISA_ID_BASE); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd_io(board->base + SI2_ISA_ID_BASE, 0x8); + + if (!IS_EISA_BOARD(board)) { + if (IS_SI1_BOARD(board)) { + for (i = 0; i < 8; i++) { + write_sx_byte(board, SI2_ISA_ID_BASE + 7 - i,i); + } + } + for (i = 0; i < 8; i++) { + if ((read_sx_byte(board, SI2_ISA_ID_BASE + 7 - i) & 7) + != i) { + func_exit(); + return 0; + } + } + } + + /* Now we're pretty much convinced that there is an SI board here, + but to prevent trouble, we'd better double check that we don't + have an SI1 board when we're probing for an SI2 board.... */ + + write_sx_byte(board, SI2_ISA_ID_BASE, 0x10); + if (IS_SI1_BOARD(board)) { + /* This should be an SI1 board, which has this + location writable... */ + if (read_sx_byte(board, SI2_ISA_ID_BASE) != 0x10) { + func_exit(); + return 0; + } + } else { + /* This should be an SI2 board, which has the bottom + 3 bits non-writable... */ + if (read_sx_byte(board, SI2_ISA_ID_BASE) == 0x10) { + func_exit(); + return 0; + } + } + + /* Now we're pretty much convinced that there is an SI board here, + but to prevent trouble, we'd better double check that we don't + have an SI1 board when we're probing for an SI2 board.... */ + + write_sx_byte(board, SI2_ISA_ID_BASE, 0x10); + if (IS_SI1_BOARD(board)) { + /* This should be an SI1 board, which has this + location writable... */ + if (read_sx_byte(board, SI2_ISA_ID_BASE) != 0x10) { + func_exit(); + return 0; + } + } else { + /* This should be an SI2 board, which has the bottom + 3 bits non-writable... */ + if (read_sx_byte(board, SI2_ISA_ID_BASE) == 0x10) { + func_exit(); + return 0; + } + } + + printheader(); + + printk(KERN_DEBUG "sx: Found an SI board at %lx\n", board->hw_base); + /* Compared to the SX boards, it is a complete guess as to what + this card is up to... */ + + board->nports = -1; + + /* This resets the processor, and keeps it off the bus. */ + if (!sx_reset(board)) + return 0; + sx_dprintk(SX_DEBUG_INIT, "reset the board...\n"); + + func_exit(); + return 1; +} +#endif + +static const struct tty_operations sx_ops = { + .break_ctl = sx_break, + .open = sx_open, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .flush_buffer = gs_flush_buffer, + .ioctl = sx_ioctl, + .throttle = sx_throttle, + .unthrottle = sx_unthrottle, + .set_termios = gs_set_termios, + .stop = gs_stop, + .start = gs_start, + .hangup = gs_hangup, + .tiocmget = sx_tiocmget, + .tiocmset = sx_tiocmset, +}; + +static const struct tty_port_operations sx_port_ops = { + .carrier_raised = sx_carrier_raised, +}; + +static int sx_init_drivers(void) +{ + int error; + + func_enter(); + + sx_driver = alloc_tty_driver(sx_nports); + if (!sx_driver) + return 1; + sx_driver->owner = THIS_MODULE; + sx_driver->driver_name = "specialix_sx"; + sx_driver->name = "ttyX"; + sx_driver->major = SX_NORMAL_MAJOR; + sx_driver->type = TTY_DRIVER_TYPE_SERIAL; + sx_driver->subtype = SERIAL_TYPE_NORMAL; + sx_driver->init_termios = tty_std_termios; + sx_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + sx_driver->init_termios.c_ispeed = 9600; + sx_driver->init_termios.c_ospeed = 9600; + sx_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(sx_driver, &sx_ops); + + if ((error = tty_register_driver(sx_driver))) { + put_tty_driver(sx_driver); + printk(KERN_ERR "sx: Couldn't register sx driver, error = %d\n", + error); + return 1; + } + func_exit(); + return 0; +} + +static int sx_init_portstructs(int nboards, int nports) +{ + struct sx_board *board; + struct sx_port *port; + int i, j; + int addr, chans; + int portno; + + func_enter(); + + /* Many drivers statically allocate the maximum number of ports + There is no reason not to allocate them dynamically. + Is there? -- REW */ + sx_ports = kcalloc(nports, sizeof(struct sx_port), GFP_KERNEL); + if (!sx_ports) + return -ENOMEM; + + port = sx_ports; + for (i = 0; i < nboards; i++) { + board = &boards[i]; + board->ports = port; + for (j = 0; j < boards[i].nports; j++) { + sx_dprintk(SX_DEBUG_INIT, "initing port %d\n", j); + tty_port_init(&port->gs.port); + port->gs.port.ops = &sx_port_ops; + port->gs.magic = SX_MAGIC; + port->gs.close_delay = HZ / 2; + port->gs.closing_wait = 30 * HZ; + port->board = board; + port->gs.rd = &sx_real_driver; +#ifdef NEW_WRITE_LOCKING + port->gs.port_write_mutex = MUTEX; +#endif + spin_lock_init(&port->gs.driver_lock); + /* + * Initializing wait queue + */ + port++; + } + } + + port = sx_ports; + portno = 0; + for (i = 0; i < nboards; i++) { + board = &boards[i]; + board->port_base = portno; + /* Possibly the configuration was rejected. */ + sx_dprintk(SX_DEBUG_PROBE, "Board has %d channels\n", + board->nports); + if (board->nports <= 0) + continue; + /* XXX byteorder ?? */ + for (addr = 0x80; addr != 0; + addr = read_sx_word(board, addr) & 0x7fff) { + chans = sx_read_module_byte(board, addr, mc_type); + sx_dprintk(SX_DEBUG_PROBE, "Module at %x: %d " + "channels\n", addr, chans); + sx_dprintk(SX_DEBUG_PROBE, "Port at"); + for (j = 0; j < chans; j++) { + /* The "sx-way" is the way it SHOULD be done. + That way in the future, the firmware may for + example pack the structures a bit more + efficient. Neil tells me it isn't going to + happen anytime soon though. */ + if (IS_SX_BOARD(board)) + port->ch_base = sx_read_module_word( + board, addr + j * 2, + mc_chan_pointer); + else + port->ch_base = addr + 0x100 + 0x300 *j; + + sx_dprintk(SX_DEBUG_PROBE, " %x", + port->ch_base); + port->line = portno++; + port++; + } + sx_dprintk(SX_DEBUG_PROBE, "\n"); + } + /* This has to be done earlier. */ + /* board->flags |= SX_BOARD_INITIALIZED; */ + } + + func_exit(); + return 0; +} + +static unsigned int sx_find_free_board(void) +{ + unsigned int i; + + for (i = 0; i < SX_NBOARDS; i++) + if (!(boards[i].flags & SX_BOARD_PRESENT)) + break; + + return i; +} + +static void __exit sx_release_drivers(void) +{ + func_enter(); + tty_unregister_driver(sx_driver); + put_tty_driver(sx_driver); + func_exit(); +} + +static void __devexit sx_remove_card(struct sx_board *board, + struct pci_dev *pdev) +{ + if (board->flags & SX_BOARD_INITIALIZED) { + /* The board should stop messing with us. (actually I mean the + interrupt) */ + sx_reset(board); + if ((board->irq) && (board->flags & SX_IRQ_ALLOCATED)) + free_irq(board->irq, board); + + /* It is safe/allowed to del_timer a non-active timer */ + del_timer(&board->timer); + if (pdev) { +#ifdef CONFIG_PCI + iounmap(board->base2); + pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2); +#endif + } else { + iounmap(board->base); + release_region(board->hw_base, board->hw_len); + } + + board->flags &= ~(SX_BOARD_INITIALIZED | SX_BOARD_PRESENT); + } +} + +#ifdef CONFIG_EISA + +static int __devinit sx_eisa_probe(struct device *dev) +{ + struct eisa_device *edev = to_eisa_device(dev); + struct sx_board *board; + unsigned long eisa_slot = edev->base_addr; + unsigned int i; + int retval = -EIO; + + mutex_lock(&sx_boards_lock); + i = sx_find_free_board(); + if (i == SX_NBOARDS) { + mutex_unlock(&sx_boards_lock); + goto err; + } + board = &boards[i]; + board->flags |= SX_BOARD_PRESENT; + mutex_unlock(&sx_boards_lock); + + dev_info(dev, "XIO : Signature found in EISA slot %lu, " + "Product %d Rev %d (REPORT THIS TO LKLM)\n", + eisa_slot >> 12, + inb(eisa_slot + EISA_VENDOR_ID_OFFSET + 2), + inb(eisa_slot + EISA_VENDOR_ID_OFFSET + 3)); + + board->eisa_base = eisa_slot; + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SI_EISA_BOARD; + + board->hw_base = ((inb(eisa_slot + 0xc01) << 8) + + inb(eisa_slot + 0xc00)) << 16; + board->hw_len = SI2_EISA_WINDOW_LEN; + if (!request_region(board->hw_base, board->hw_len, "sx")) { + dev_err(dev, "can't request region\n"); + goto err_flag; + } + board->base2 = + board->base = ioremap_nocache(board->hw_base, SI2_EISA_WINDOW_LEN); + if (!board->base) { + dev_err(dev, "can't remap memory\n"); + goto err_reg; + } + + sx_dprintk(SX_DEBUG_PROBE, "IO hw_base address: %lx\n", board->hw_base); + sx_dprintk(SX_DEBUG_PROBE, "base: %p\n", board->base); + board->irq = inb(eisa_slot + 0xc02) >> 4; + sx_dprintk(SX_DEBUG_PROBE, "IRQ: %d\n", board->irq); + + if (!probe_si(board)) + goto err_unmap; + + dev_set_drvdata(dev, board); + + return 0; +err_unmap: + iounmap(board->base); +err_reg: + release_region(board->hw_base, board->hw_len); +err_flag: + board->flags &= ~SX_BOARD_PRESENT; +err: + return retval; +} + +static int __devexit sx_eisa_remove(struct device *dev) +{ + struct sx_board *board = dev_get_drvdata(dev); + + sx_remove_card(board, NULL); + + return 0; +} + +static struct eisa_device_id sx_eisa_tbl[] = { + { "SLX" }, + { "" } +}; + +MODULE_DEVICE_TABLE(eisa, sx_eisa_tbl); + +static struct eisa_driver sx_eisadriver = { + .id_table = sx_eisa_tbl, + .driver = { + .name = "sx", + .probe = sx_eisa_probe, + .remove = __devexit_p(sx_eisa_remove), + } +}; + +#endif + +#ifdef CONFIG_PCI + /******************************************************** + * Setting bit 17 in the CNTRL register of the PLX 9050 * + * chip forces a retry on writes while a read is pending.* + * This is to prevent the card locking up on Intel Xeon * + * multiprocessor systems with the NX chipset. -- NV * + ********************************************************/ + +/* Newer cards are produced with this bit set from the configuration + EEprom. As the bit is read/write for the CPU, we can fix it here, + if we detect that it isn't set correctly. -- REW */ + +static void __devinit fix_sx_pci(struct pci_dev *pdev, struct sx_board *board) +{ + unsigned int hwbase; + void __iomem *rebase; + unsigned int t; + +#define CNTRL_REG_OFFSET 0x50 +#define CNTRL_REG_GOODVALUE 0x18260000 + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase); + hwbase &= PCI_BASE_ADDRESS_MEM_MASK; + rebase = ioremap_nocache(hwbase, 0x80); + t = readl(rebase + CNTRL_REG_OFFSET); + if (t != CNTRL_REG_GOODVALUE) { + printk(KERN_DEBUG "sx: performing cntrl reg fix: %08x -> " + "%08x\n", t, CNTRL_REG_GOODVALUE); + writel(CNTRL_REG_GOODVALUE, rebase + CNTRL_REG_OFFSET); + } + iounmap(rebase); +} +#endif + +static int __devinit sx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ +#ifdef CONFIG_PCI + struct sx_board *board; + unsigned int i, reg; + int retval = -EIO; + + mutex_lock(&sx_boards_lock); + i = sx_find_free_board(); + if (i == SX_NBOARDS) { + mutex_unlock(&sx_boards_lock); + goto err; + } + board = &boards[i]; + board->flags |= SX_BOARD_PRESENT; + mutex_unlock(&sx_boards_lock); + + retval = pci_enable_device(pdev); + if (retval) + goto err_flag; + + board->flags &= ~SX_BOARD_TYPE; + board->flags |= (pdev->subsystem_vendor == 0x200) ? SX_PCI_BOARD : + SX_CFPCI_BOARD; + + /* CF boards use base address 3.... */ + reg = IS_CF_BOARD(board) ? 3 : 2; + retval = pci_request_region(pdev, reg, "sx"); + if (retval) { + dev_err(&pdev->dev, "can't request region\n"); + goto err_flag; + } + board->hw_base = pci_resource_start(pdev, reg); + board->base2 = + board->base = ioremap_nocache(board->hw_base, WINDOW_LEN(board)); + if (!board->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err_reg; + } + + /* Most of the stuff on the CF board is offset by 0x18000 .... */ + if (IS_CF_BOARD(board)) + board->base += 0x18000; + + board->irq = pdev->irq; + + dev_info(&pdev->dev, "Got a specialix card: %p(%d) %x.\n", board->base, + board->irq, board->flags); + + if (!probe_sx(board)) { + retval = -EIO; + goto err_unmap; + } + + fix_sx_pci(pdev, board); + + pci_set_drvdata(pdev, board); + + return 0; +err_unmap: + iounmap(board->base2); +err_reg: + pci_release_region(pdev, reg); +err_flag: + board->flags &= ~SX_BOARD_PRESENT; +err: + return retval; +#else + return -ENODEV; +#endif +} + +static void __devexit sx_pci_remove(struct pci_dev *pdev) +{ + struct sx_board *board = pci_get_drvdata(pdev); + + sx_remove_card(board, pdev); +} + +/* Specialix has a whole bunch of cards with 0x2000 as the device ID. They say + its because the standard requires it. So check for SUBVENDOR_ID. */ +static struct pci_device_id sx_pci_tbl[] = { + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, + .subvendor = PCI_ANY_ID, .subdevice = 0x0200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, + .subvendor = PCI_ANY_ID, .subdevice = 0x0300 }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, sx_pci_tbl); + +static struct pci_driver sx_pcidriver = { + .name = "sx", + .id_table = sx_pci_tbl, + .probe = sx_pci_probe, + .remove = __devexit_p(sx_pci_remove) +}; + +static int __init sx_init(void) +{ +#ifdef CONFIG_EISA + int retval1; +#endif +#ifdef CONFIG_ISA + struct sx_board *board; + unsigned int i; +#endif + unsigned int found = 0; + int retval; + + func_enter(); + sx_dprintk(SX_DEBUG_INIT, "Initing sx module... (sx_debug=%d)\n", + sx_debug); + if (abs((long)(&sx_debug) - sx_debug) < 0x10000) { + printk(KERN_WARNING "sx: sx_debug is an address, instead of a " + "value. Assuming -1.\n(%p)\n", &sx_debug); + sx_debug = -1; + } + + if (misc_register(&sx_fw_device) < 0) { + printk(KERN_ERR "SX: Unable to register firmware loader " + "driver.\n"); + return -EIO; + } +#ifdef CONFIG_ISA + for (i = 0; i < NR_SX_ADDRS; i++) { + board = &boards[found]; + board->hw_base = sx_probe_addrs[i]; + board->hw_len = SX_WINDOW_LEN; + if (!request_region(board->hw_base, board->hw_len, "sx")) + continue; + board->base2 = + board->base = ioremap_nocache(board->hw_base, board->hw_len); + if (!board->base) + goto err_sx_reg; + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SX_ISA_BOARD; + board->irq = sx_irqmask ? -1 : 0; + + if (probe_sx(board)) { + board->flags |= SX_BOARD_PRESENT; + found++; + } else { + iounmap(board->base); +err_sx_reg: + release_region(board->hw_base, board->hw_len); + } + } + + for (i = 0; i < NR_SI_ADDRS; i++) { + board = &boards[found]; + board->hw_base = si_probe_addrs[i]; + board->hw_len = SI2_ISA_WINDOW_LEN; + if (!request_region(board->hw_base, board->hw_len, "sx")) + continue; + board->base2 = + board->base = ioremap_nocache(board->hw_base, board->hw_len); + if (!board->base) + goto err_si_reg; + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SI_ISA_BOARD; + board->irq = sx_irqmask ? -1 : 0; + + if (probe_si(board)) { + board->flags |= SX_BOARD_PRESENT; + found++; + } else { + iounmap(board->base); +err_si_reg: + release_region(board->hw_base, board->hw_len); + } + } + for (i = 0; i < NR_SI1_ADDRS; i++) { + board = &boards[found]; + board->hw_base = si1_probe_addrs[i]; + board->hw_len = SI1_ISA_WINDOW_LEN; + if (!request_region(board->hw_base, board->hw_len, "sx")) + continue; + board->base2 = + board->base = ioremap_nocache(board->hw_base, board->hw_len); + if (!board->base) + goto err_si1_reg; + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SI1_ISA_BOARD; + board->irq = sx_irqmask ? -1 : 0; + + if (probe_si(board)) { + board->flags |= SX_BOARD_PRESENT; + found++; + } else { + iounmap(board->base); +err_si1_reg: + release_region(board->hw_base, board->hw_len); + } + } +#endif +#ifdef CONFIG_EISA + retval1 = eisa_driver_register(&sx_eisadriver); +#endif + retval = pci_register_driver(&sx_pcidriver); + + if (found) { + printk(KERN_INFO "sx: total of %d boards detected.\n", found); + retval = 0; + } else if (retval) { +#ifdef CONFIG_EISA + retval = retval1; + if (retval1) +#endif + misc_deregister(&sx_fw_device); + } + + func_exit(); + return retval; +} + +static void __exit sx_exit(void) +{ + int i; + + func_enter(); +#ifdef CONFIG_EISA + eisa_driver_unregister(&sx_eisadriver); +#endif + pci_unregister_driver(&sx_pcidriver); + + for (i = 0; i < SX_NBOARDS; i++) + sx_remove_card(&boards[i], NULL); + + if (misc_deregister(&sx_fw_device) < 0) { + printk(KERN_INFO "sx: couldn't deregister firmware loader " + "device\n"); + } + sx_dprintk(SX_DEBUG_CLEANUP, "Cleaning up drivers (%d)\n", + sx_initialized); + if (sx_initialized) + sx_release_drivers(); + + kfree(sx_ports); + func_exit(); +} + +module_init(sx_init); +module_exit(sx_exit); diff --git a/drivers/staging/generic_serial/sx.h b/drivers/staging/generic_serial/sx.h new file mode 100644 index 0000000..87c2def --- /dev/null +++ b/drivers/staging/generic_serial/sx.h @@ -0,0 +1,201 @@ + +/* + * sx.h + * + * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl + * + * SX serial driver. + * -- Supports SI, XIO and SX host cards. + * -- Supports TAs, MTAs and SXDCs. + * + * Version 1.3 -- March, 1999. + * + */ + +#define SX_NBOARDS 4 +#define SX_PORTSPERBOARD 32 +#define SX_NPORTS (SX_NBOARDS * SX_PORTSPERBOARD) + +#ifdef __KERNEL__ + +#define SX_MAGIC 0x12345678 + +struct sx_port { + struct gs_port gs; + struct wait_queue *shutdown_wait; + int ch_base; + int c_dcd; + struct sx_board *board; + int line; + unsigned long locks; +}; + +struct sx_board { + int magic; + void __iomem *base; + void __iomem *base2; + unsigned long hw_base; + resource_size_t hw_len; + int eisa_base; + int port_base; /* Number of the first port */ + struct sx_port *ports; + int nports; + int flags; + int irq; + int poll; + int ta_type; + struct timer_list timer; + unsigned long locks; +}; + +struct vpd_prom { + unsigned short id; + char hwrev; + char hwass; + int uniqid; + char myear; + char mweek; + char hw_feature[5]; + char oem_id; + char identifier[16]; +}; + +#ifndef MOD_RS232DB25MALE +#define MOD_RS232DB25MALE 0x0a +#endif + +#define SI_ISA_BOARD 0x00000001 +#define SX_ISA_BOARD 0x00000002 +#define SX_PCI_BOARD 0x00000004 +#define SX_CFPCI_BOARD 0x00000008 +#define SX_CFISA_BOARD 0x00000010 +#define SI_EISA_BOARD 0x00000020 +#define SI1_ISA_BOARD 0x00000040 + +#define SX_BOARD_PRESENT 0x00001000 +#define SX_BOARD_INITIALIZED 0x00002000 +#define SX_IRQ_ALLOCATED 0x00004000 + +#define SX_BOARD_TYPE 0x000000ff + +#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_CFPCI_BOARD | \ + SX_ISA_BOARD | SX_CFISA_BOARD)) + +#define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD) +#define IS_SI1_BOARD(board) (board->flags & SI1_ISA_BOARD) + +#define IS_EISA_BOARD(board) (board->flags & SI_EISA_BOARD) + +#define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD)) + +/* The SI processor clock is required to calculate the cc_int_count register + value for the SI cards. */ +#define SI_PROCESSOR_CLOCK 25000000 + + +/* port flags */ +/* Make sure these don't clash with gs flags or async flags */ +#define SX_RX_THROTTLE 0x0000001 + + + +#define SX_PORT_TRANSMIT_LOCK 0 +#define SX_BOARD_INTR_LOCK 0 + + + +/* Debug flags. Add these together to get more debug info. */ + +#define SX_DEBUG_OPEN 0x00000001 +#define SX_DEBUG_SETTING 0x00000002 +#define SX_DEBUG_FLOW 0x00000004 +#define SX_DEBUG_MODEMSIGNALS 0x00000008 +#define SX_DEBUG_TERMIOS 0x00000010 +#define SX_DEBUG_TRANSMIT 0x00000020 +#define SX_DEBUG_RECEIVE 0x00000040 +#define SX_DEBUG_INTERRUPTS 0x00000080 +#define SX_DEBUG_PROBE 0x00000100 +#define SX_DEBUG_INIT 0x00000200 +#define SX_DEBUG_CLEANUP 0x00000400 +#define SX_DEBUG_CLOSE 0x00000800 +#define SX_DEBUG_FIRMWARE 0x00001000 +#define SX_DEBUG_MEMTEST 0x00002000 + +#define SX_DEBUG_ALL 0xffffffff + + +#define O_OTHER(tty) \ + ((O_OLCUC(tty)) ||\ + (O_ONLCR(tty)) ||\ + (O_OCRNL(tty)) ||\ + (O_ONOCR(tty)) ||\ + (O_ONLRET(tty)) ||\ + (O_OFILL(tty)) ||\ + (O_OFDEL(tty)) ||\ + (O_NLDLY(tty)) ||\ + (O_CRDLY(tty)) ||\ + (O_TABDLY(tty)) ||\ + (O_BSDLY(tty)) ||\ + (O_VTDLY(tty)) ||\ + (O_FFDLY(tty))) + +/* Same for input. */ +#define I_OTHER(tty) \ + ((I_INLCR(tty)) ||\ + (I_IGNCR(tty)) ||\ + (I_ICRNL(tty)) ||\ + (I_IUCLC(tty)) ||\ + (L_ISIG(tty))) + +#define MOD_TA ( TA>>4) +#define MOD_MTA (MTA_CD1400>>4) +#define MOD_SXDC ( SXDC>>4) + + +/* We copy the download code over to the card in chunks of ... bytes */ +#define SX_CHUNK_SIZE 128 + +#endif /* __KERNEL__ */ + + + +/* Specialix document 6210046-11 page 3 */ +#define SPX(X) (('S'<<24) | ('P' << 16) | (X)) + +/* Specialix-Linux specific IOCTLS. */ +#define SPXL(X) (SPX(('L' << 8) | (X))) + + +#define SXIO_SET_BOARD SPXL(0x01) +#define SXIO_GET_TYPE SPXL(0x02) +#define SXIO_DOWNLOAD SPXL(0x03) +#define SXIO_INIT SPXL(0x04) +#define SXIO_SETDEBUG SPXL(0x05) +#define SXIO_GETDEBUG SPXL(0x06) +#define SXIO_DO_RAMTEST SPXL(0x07) +#define SXIO_SETGSDEBUG SPXL(0x08) +#define SXIO_GETGSDEBUG SPXL(0x09) +#define SXIO_GETNPORTS SPXL(0x0a) + + +#ifndef SXCTL_MISC_MINOR +/* Allow others to gather this into "major.h" or something like that */ +#define SXCTL_MISC_MINOR 167 +#endif + +#ifndef SX_NORMAL_MAJOR +/* This allows overriding on the compiler commandline, or in a "major.h" + include or something like that */ +#define SX_NORMAL_MAJOR 32 +#define SX_CALLOUT_MAJOR 33 +#endif + + +#define SX_TYPE_SX 0x01 +#define SX_TYPE_SI 0x02 +#define SX_TYPE_CF 0x03 + + +#define WINDOW_LEN(board) (IS_CF_BOARD(board)?0x20000:SX_WINDOW_LEN) +/* Need a #define for ^^^^^^^ !!! */ + diff --git a/drivers/staging/generic_serial/sxboards.h b/drivers/staging/generic_serial/sxboards.h new file mode 100644 index 0000000..427927d --- /dev/null +++ b/drivers/staging/generic_serial/sxboards.h @@ -0,0 +1,206 @@ +/************************************************************************/ +/* */ +/* Title : SX/SI/XIO Board Hardware Definitions */ +/* */ +/* Author : N.P.Vassallo */ +/* */ +/* Creation : 16th March 1998 */ +/* */ +/* Version : 3.0.0 */ +/* */ +/* Copyright : (c) Specialix International Ltd. 1998 */ +/* */ +/* Description : Prototypes, structures and definitions */ +/* describing the SX/SI/XIO board hardware */ +/* */ +/************************************************************************/ + +/* History... + +3.0.0 16/03/98 NPV Creation. + +*/ + +#ifndef _sxboards_h /* If SXBOARDS.H not already defined */ +#define _sxboards_h 1 + +/***************************************************************************** +******************************* ****************************** +******************************* Board Types ****************************** +******************************* ****************************** +*****************************************************************************/ + +/* BUS types... */ +#define BUS_ISA 0 +#define BUS_MCA 1 +#define BUS_EISA 2 +#define BUS_PCI 3 + +/* Board phases... */ +#define SI1_Z280 1 +#define SI2_Z280 2 +#define SI3_T225 3 + +/* Board types... */ +#define CARD_TYPE(bus,phase) (bus<<4|phase) +#define CARD_BUS(type) ((type>>4)&0xF) +#define CARD_PHASE(type) (type&0xF) + +#define TYPE_SI1_ISA CARD_TYPE(BUS_ISA,SI1_Z280) +#define TYPE_SI2_ISA CARD_TYPE(BUS_ISA,SI2_Z280) +#define TYPE_SI2_EISA CARD_TYPE(BUS_EISA,SI2_Z280) +#define TYPE_SI2_PCI CARD_TYPE(BUS_PCI,SI2_Z280) + +#define TYPE_SX_ISA CARD_TYPE(BUS_ISA,SI3_T225) +#define TYPE_SX_PCI CARD_TYPE(BUS_PCI,SI3_T225) +/***************************************************************************** +****************************** ****************************** +****************************** Phase 1 Z280 ****************************** +****************************** ****************************** +*****************************************************************************/ + +/* ISA board details... */ +#define SI1_ISA_WINDOW_LEN 0x10000 /* 64 Kbyte shared memory window */ +//#define SI1_ISA_MEMORY_LEN 0x8000 /* Usable memory - unused define*/ +//#define SI1_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */ +//#define SI1_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */ +//#define SI2_ISA_ADDR_STEP SI2_ISA_WINDOW_LEN/* ISA board address step */ +//#define SI2_ISA_IRQ_MASK 0x9800 /* IRQs 15,12,11 */ + +/* ISA board, register definitions... */ +//#define SI2_ISA_ID_BASE 0x7FF8 /* READ: Board ID string */ +#define SI1_ISA_RESET 0x8000 /* WRITE: Host Reset */ +#define SI1_ISA_RESET_CLEAR 0xc000 /* WRITE: Host Reset clear*/ +#define SI1_ISA_WAIT 0x9000 /* WRITE: Host wait */ +#define SI1_ISA_WAIT_CLEAR 0xd000 /* WRITE: Host wait clear */ +#define SI1_ISA_INTCL 0xa000 /* WRITE: Host Reset */ +#define SI1_ISA_INTCL_CLEAR 0xe000 /* WRITE: Host Reset */ + + +/***************************************************************************** +****************************** ****************************** +****************************** Phase 2 Z280 ****************************** +****************************** ****************************** +*****************************************************************************/ + +/* ISA board details... */ +#define SI2_ISA_WINDOW_LEN 0x8000 /* 32 Kbyte shared memory window */ +#define SI2_ISA_MEMORY_LEN 0x7FF8 /* Usable memory */ +#define SI2_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */ +#define SI2_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */ +#define SI2_ISA_ADDR_STEP SI2_ISA_WINDOW_LEN/* ISA board address step */ +#define SI2_ISA_IRQ_MASK 0x9800 /* IRQs 15,12,11 */ + +/* ISA board, register definitions... */ +#define SI2_ISA_ID_BASE 0x7FF8 /* READ: Board ID string */ +#define SI2_ISA_RESET SI2_ISA_ID_BASE /* WRITE: Host Reset */ +#define SI2_ISA_IRQ11 (SI2_ISA_ID_BASE+1) /* WRITE: Set IRQ11 */ +#define SI2_ISA_IRQ12 (SI2_ISA_ID_BASE+2) /* WRITE: Set IRQ12 */ +#define SI2_ISA_IRQ15 (SI2_ISA_ID_BASE+3) /* WRITE: Set IRQ15 */ +#define SI2_ISA_IRQSET (SI2_ISA_ID_BASE+4) /* WRITE: Set Host Interrupt */ +#define SI2_ISA_INTCLEAR (SI2_ISA_ID_BASE+5) /* WRITE: Enable Host Interrupt */ + +#define SI2_ISA_IRQ11_SET 0x10 +#define SI2_ISA_IRQ11_CLEAR 0x00 +#define SI2_ISA_IRQ12_SET 0x10 +#define SI2_ISA_IRQ12_CLEAR 0x00 +#define SI2_ISA_IRQ15_SET 0x10 +#define SI2_ISA_IRQ15_CLEAR 0x00 +#define SI2_ISA_INTCLEAR_SET 0x10 +#define SI2_ISA_INTCLEAR_CLEAR 0x00 +#define SI2_ISA_IRQSET_CLEAR 0x10 +#define SI2_ISA_IRQSET_SET 0x00 +#define SI2_ISA_RESET_SET 0x00 +#define SI2_ISA_RESET_CLEAR 0x10 + +/* PCI board details... */ +#define SI2_PCI_WINDOW_LEN 0x100000 /* 1 Mbyte memory window */ + +/* PCI board register definitions... */ +#define SI2_PCI_SET_IRQ 0x40001 /* Set Host Interrupt */ +#define SI2_PCI_RESET 0xC0001 /* Host Reset */ + +/***************************************************************************** +****************************** ****************************** +****************************** Phase 3 T225 ****************************** +****************************** ****************************** +*****************************************************************************/ + +/* General board details... */ +#define SX_WINDOW_LEN 64*1024 /* 64 Kbyte memory window */ + +/* ISA board details... */ +#define SX_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */ +#define SX_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */ +#define SX_ISA_ADDR_STEP SX_WINDOW_LEN /* ISA board address step */ +#define SX_ISA_IRQ_MASK 0x9E00 /* IRQs 15,12,11,10,9 */ + +/* Hardware register definitions... */ +#define SX_EVENT_STATUS 0x7800 /* READ: T225 Event Status */ +#define SX_EVENT_STROBE 0x7800 /* WRITE: T225 Event Strobe */ +#define SX_EVENT_ENABLE 0x7880 /* WRITE: T225 Event Enable */ +#define SX_VPD_ROM 0x7C00 /* READ: Vital Product Data ROM */ +#define SX_CONFIG 0x7C00 /* WRITE: Host Configuration Register */ +#define SX_IRQ_STATUS 0x7C80 /* READ: Host Interrupt Status */ +#define SX_SET_IRQ 0x7C80 /* WRITE: Set Host Interrupt */ +#define SX_RESET_STATUS 0x7D00 /* READ: Host Reset Status */ +#define SX_RESET 0x7D00 /* WRITE: Host Reset */ +#define SX_RESET_IRQ 0x7D80 /* WRITE: Reset Host Interrupt */ + +/* SX_VPD_ROM definitions... */ +#define SX_VPD_SLX_ID1 0x00 +#define SX_VPD_SLX_ID2 0x01 +#define SX_VPD_HW_REV 0x02 +#define SX_VPD_HW_ASSEM 0x03 +#define SX_VPD_UNIQUEID4 0x04 +#define SX_VPD_UNIQUEID3 0x05 +#define SX_VPD_UNIQUEID2 0x06 +#define SX_VPD_UNIQUEID1 0x07 +#define SX_VPD_MANU_YEAR 0x08 +#define SX_VPD_MANU_WEEK 0x09 +#define SX_VPD_IDENT 0x10 +#define SX_VPD_IDENT_STRING "JET HOST BY KEV#" + +/* SX unique identifiers... */ +#define SX_UNIQUEID_MASK 0xF0 +#define SX_ISA_UNIQUEID1 0x20 +#define SX_PCI_UNIQUEID1 0x50 + +/* SX_CONFIG definitions... */ +#define SX_CONF_BUSEN 0x02 /* Enable T225 memory and I/O */ +#define SX_CONF_HOSTIRQ 0x04 /* Enable board to host interrupt */ + +/* SX bootstrap... */ +#define SX_BOOTSTRAP "\x28\x20\x21\x02\x60\x0a" +#define SX_BOOTSTRAP_SIZE 6 +#define SX_BOOTSTRAP_ADDR (0x8000-SX_BOOTSTRAP_SIZE) + +/***************************************************************************** +********************************** ********************************** +********************************** EISA ********************************** +********************************** ********************************** +*****************************************************************************/ + +#define SI2_EISA_OFF 0x42 +#define SI2_EISA_VAL 0x01 +#define SI2_EISA_WINDOW_LEN 0x10000 + +/***************************************************************************** +*********************************** ********************************** +*********************************** PCI ********************************** +*********************************** ********************************** +*****************************************************************************/ + +/* General definitions... */ + +#define SPX_VENDOR_ID 0x11CB /* Assigned by the PCI SIG */ +#define SPX_DEVICE_ID 0x4000 /* SI/XIO boards */ +#define SPX_PLXDEVICE_ID 0x2000 /* SX boards */ + +#define SPX_SUB_VENDOR_ID SPX_VENDOR_ID /* Same as vendor id */ +#define SI2_SUB_SYS_ID 0x400 /* Phase 2 (Z280) board */ +#define SX_SUB_SYS_ID 0x200 /* Phase 3 (t225) board */ + +#endif /*_sxboards_h */ + +/* End of SXBOARDS.H */ diff --git a/drivers/staging/generic_serial/sxwindow.h b/drivers/staging/generic_serial/sxwindow.h new file mode 100644 index 0000000..cf01b66 --- /dev/null +++ b/drivers/staging/generic_serial/sxwindow.h @@ -0,0 +1,393 @@ +/************************************************************************/ +/* */ +/* Title : SX Shared Memory Window Structure */ +/* */ +/* Author : N.P.Vassallo */ +/* */ +/* Creation : 16th March 1998 */ +/* */ +/* Version : 3.0.0 */ +/* */ +/* Copyright : (c) Specialix International Ltd. 1998 */ +/* */ +/* Description : Prototypes, structures and definitions */ +/* describing the SX/SI/XIO cards shared */ +/* memory window structure: */ +/* SXCARD */ +/* SXMODULE */ +/* SXCHANNEL */ +/* */ +/************************************************************************/ + +/* History... + +3.0.0 16/03/98 NPV Creation. (based on STRUCT.H) + +*/ + +#ifndef _sxwindow_h /* If SXWINDOW.H not already defined */ +#define _sxwindow_h 1 + +/***************************************************************************** +*************************** *************************** +*************************** Common Definitions *************************** +*************************** *************************** +*****************************************************************************/ + +typedef struct _SXCARD *PSXCARD; /* SXCARD structure pointer */ +typedef struct _SXMODULE *PMOD; /* SXMODULE structure pointer */ +typedef struct _SXCHANNEL *PCHAN; /* SXCHANNEL structure pointer */ + +/***************************************************************************** +********************************* ********************************* +********************************* SXCARD ********************************* +********************************* ********************************* +*****************************************************************************/ + +typedef struct _SXCARD +{ + BYTE cc_init_status; /* 0x00 Initialisation status */ + BYTE cc_mem_size; /* 0x01 Size of memory on card */ + WORD cc_int_count; /* 0x02 Interrupt count */ + WORD cc_revision; /* 0x04 Download code revision */ + BYTE cc_isr_count; /* 0x06 Count when ISR is run */ + BYTE cc_main_count; /* 0x07 Count when main loop is run */ + WORD cc_int_pending; /* 0x08 Interrupt pending */ + WORD cc_poll_count; /* 0x0A Count when poll is run */ + BYTE cc_int_set_count; /* 0x0C Count when host interrupt is set */ + BYTE cc_rfu[0x80 - 0x0D]; /* 0x0D Pad structure to 128 bytes (0x80) */ + +} SXCARD; + +/* SXCARD.cc_init_status definitions... */ +#define ADAPTERS_FOUND (BYTE)0x01 +#define NO_ADAPTERS_FOUND (BYTE)0xFF + +/* SXCARD.cc_mem_size definitions... */ +#define SX_MEMORY_SIZE (BYTE)0x40 + +/* SXCARD.cc_int_count definitions... */ +#define INT_COUNT_DEFAULT 100 /* Hz */ + +/***************************************************************************** +******************************** ******************************** +******************************** SXMODULE ******************************** +******************************** ******************************** +*****************************************************************************/ + +#define TOP_POINTER(a) ((a)|0x8000) /* Sets top bit of word */ +#define UNTOP_POINTER(a) ((a)&~0x8000) /* Clears top bit of word */ + +typedef struct _SXMODULE +{ + WORD mc_next; /* 0x00 Next module "pointer" (ORed with 0x8000) */ + BYTE mc_type; /* 0x02 Type of TA in terms of number of channels */ + BYTE mc_mod_no; /* 0x03 Module number on SI bus cable (0 closest to card) */ + BYTE mc_dtr; /* 0x04 Private DTR copy (TA only) */ + BYTE mc_rfu1; /* 0x05 Reserved */ + WORD mc_uart; /* 0x06 UART base address for this module */ + BYTE mc_chip; /* 0x08 Chip type / number of ports */ + BYTE mc_current_uart; /* 0x09 Current uart selected for this module */ +#ifdef DOWNLOAD + PCHAN mc_chan_pointer[8]; /* 0x0A Pointer to each channel structure */ +#else + WORD mc_chan_pointer[8]; /* 0x0A Define as WORD if not compiling into download */ +#endif + WORD mc_rfu2; /* 0x1A Reserved */ + BYTE mc_opens1; /* 0x1C Number of open ports on first four ports on MTA/SXDC */ + BYTE mc_opens2; /* 0x1D Number of open ports on second four ports on MTA/SXDC */ + BYTE mc_mods; /* 0x1E Types of connector module attached to MTA/SXDC */ + BYTE mc_rev1; /* 0x1F Revision of first CD1400 on MTA/SXDC */ + BYTE mc_rev2; /* 0x20 Revision of second CD1400 on MTA/SXDC */ + BYTE mc_mtaasic_rev; /* 0x21 Revision of MTA ASIC 1..4 -> A, B, C, D */ + BYTE mc_rfu3[0x100 - 0x22]; /* 0x22 Pad structure to 256 bytes (0x100) */ + +} SXMODULE; + +/* SXMODULE.mc_type definitions... */ +#define FOUR_PORTS (BYTE)4 +#define EIGHT_PORTS (BYTE)8 + +/* SXMODULE.mc_chip definitions... */ +#define CHIP_MASK 0xF0 +#define TA (BYTE)0 +#define TA4 (TA | FOUR_PORTS) +#define TA8 (TA | EIGHT_PORTS) +#define TA4_ASIC (BYTE)0x0A +#define TA8_ASIC (BYTE)0x0B +#define MTA_CD1400 (BYTE)0x28 +#define SXDC (BYTE)0x48 + +/* SXMODULE.mc_mods definitions... */ +#define MOD_RS232DB25 0x00 /* RS232 DB25 (socket/plug) */ +#define MOD_RS232RJ45 0x01 /* RS232 RJ45 (shielded/opto-isolated) */ +#define MOD_RESERVED_2 0x02 /* Reserved (RS485) */ +#define MOD_RS422DB25 0x03 /* RS422 DB25 Socket */ +#define MOD_RESERVED_4 0x04 /* Reserved */ +#define MOD_PARALLEL 0x05 /* Parallel */ +#define MOD_RESERVED_6 0x06 /* Reserved (RS423) */ +#define MOD_RESERVED_7 0x07 /* Reserved */ +#define MOD_2_RS232DB25 0x08 /* Rev 2.0 RS232 DB25 (socket/plug) */ +#define MOD_2_RS232RJ45 0x09 /* Rev 2.0 RS232 RJ45 */ +#define MOD_RESERVED_A 0x0A /* Rev 2.0 Reserved */ +#define MOD_2_RS422DB25 0x0B /* Rev 2.0 RS422 DB25 */ +#define MOD_RESERVED_C 0x0C /* Rev 2.0 Reserved */ +#define MOD_2_PARALLEL 0x0D /* Rev 2.0 Parallel */ +#define MOD_RESERVED_E 0x0E /* Rev 2.0 Reserved */ +#define MOD_BLANK 0x0F /* Blank Panel */ + +/***************************************************************************** +******************************** ******************************* +******************************** SXCHANNEL ******************************* +******************************** ******************************* +*****************************************************************************/ + +#define TX_BUFF_OFFSET 0x60 /* Transmit buffer offset in channel structure */ +#define BUFF_POINTER(a) (((a)+TX_BUFF_OFFSET)|0x8000) +#define UNBUFF_POINTER(a) (jet_channel*)(((a)&~0x8000)-TX_BUFF_OFFSET) +#define BUFFER_SIZE 256 +#define HIGH_WATER ((BUFFER_SIZE / 4) * 3) +#define LOW_WATER (BUFFER_SIZE / 4) + +typedef struct _SXCHANNEL +{ + WORD next_item; /* 0x00 Offset from window base of next channels hi_txbuf (ORred with 0x8000) */ + WORD addr_uart; /* 0x02 INTERNAL pointer to uart address. Includes FASTPATH bit */ + WORD module; /* 0x04 Offset from window base of parent SXMODULE structure */ + BYTE type; /* 0x06 Chip type / number of ports (copy of mc_chip) */ + BYTE chan_number; /* 0x07 Channel number on the TA/MTA/SXDC */ + WORD xc_status; /* 0x08 Flow control and I/O status */ + BYTE hi_rxipos; /* 0x0A Receive buffer input index */ + BYTE hi_rxopos; /* 0x0B Receive buffer output index */ + BYTE hi_txopos; /* 0x0C Transmit buffer output index */ + BYTE hi_txipos; /* 0x0D Transmit buffer input index */ + BYTE hi_hstat; /* 0x0E Command register */ + BYTE dtr_bit; /* 0x0F INTERNAL DTR control byte (TA only) */ + BYTE txon; /* 0x10 INTERNAL copy of hi_txon */ + BYTE txoff; /* 0x11 INTERNAL copy of hi_txoff */ + BYTE rxon; /* 0x12 INTERNAL copy of hi_rxon */ + BYTE rxoff; /* 0x13 INTERNAL copy of hi_rxoff */ + BYTE hi_mr1; /* 0x14 Mode Register 1 (databits,parity,RTS rx flow)*/ + BYTE hi_mr2; /* 0x15 Mode Register 2 (stopbits,local,CTS tx flow)*/ + BYTE hi_csr; /* 0x16 Clock Select Register (baud rate) */ + BYTE hi_op; /* 0x17 Modem Output Signal */ + BYTE hi_ip; /* 0x18 Modem Input Signal */ + BYTE hi_state; /* 0x19 Channel status */ + BYTE hi_prtcl; /* 0x1A Channel protocol (flow control) */ + BYTE hi_txon; /* 0x1B Transmit XON character */ + BYTE hi_txoff; /* 0x1C Transmit XOFF character */ + BYTE hi_rxon; /* 0x1D Receive XON character */ + BYTE hi_rxoff; /* 0x1E Receive XOFF character */ + BYTE close_prev; /* 0x1F INTERNAL channel previously closed flag */ + BYTE hi_break; /* 0x20 Break and error control */ + BYTE break_state; /* 0x21 INTERNAL copy of hi_break */ + BYTE hi_mask; /* 0x22 Mask for received data */ + BYTE mask; /* 0x23 INTERNAL copy of hi_mask */ + BYTE mod_type; /* 0x24 MTA/SXDC hardware module type */ + BYTE ccr_state; /* 0x25 INTERNAL MTA/SXDC state of CCR register */ + BYTE ip_mask; /* 0x26 Input handshake mask */ + BYTE hi_parallel; /* 0x27 Parallel port flag */ + BYTE par_error; /* 0x28 Error code for parallel loopback test */ + BYTE any_sent; /* 0x29 INTERNAL data sent flag */ + BYTE asic_txfifo_size; /* 0x2A INTERNAL SXDC transmit FIFO size */ + BYTE rfu1[2]; /* 0x2B Reserved */ + BYTE csr; /* 0x2D INTERNAL copy of hi_csr */ +#ifdef DOWNLOAD + PCHAN nextp; /* 0x2E Offset from window base of next channel structure */ +#else + WORD nextp; /* 0x2E Define as WORD if not compiling into download */ +#endif + BYTE prtcl; /* 0x30 INTERNAL copy of hi_prtcl */ + BYTE mr1; /* 0x31 INTERNAL copy of hi_mr1 */ + BYTE mr2; /* 0x32 INTERNAL copy of hi_mr2 */ + BYTE hi_txbaud; /* 0x33 Extended transmit baud rate (SXDC only if((hi_csr&0x0F)==0x0F) */ + BYTE hi_rxbaud; /* 0x34 Extended receive baud rate (SXDC only if((hi_csr&0xF0)==0xF0) */ + BYTE txbreak_state; /* 0x35 INTERNAL MTA/SXDC transmit break state */ + BYTE txbaud; /* 0x36 INTERNAL copy of hi_txbaud */ + BYTE rxbaud; /* 0x37 INTERNAL copy of hi_rxbaud */ + WORD err_framing; /* 0x38 Count of receive framing errors */ + WORD err_parity; /* 0x3A Count of receive parity errors */ + WORD err_overrun; /* 0x3C Count of receive overrun errors */ + WORD err_overflow; /* 0x3E Count of receive buffer overflow errors */ + BYTE rfu2[TX_BUFF_OFFSET - 0x40]; /* 0x40 Reserved until hi_txbuf */ + BYTE hi_txbuf[BUFFER_SIZE]; /* 0x060 Transmit buffer */ + BYTE hi_rxbuf[BUFFER_SIZE]; /* 0x160 Receive buffer */ + BYTE rfu3[0x300 - 0x260]; /* 0x260 Reserved until 768 bytes (0x300) */ + +} SXCHANNEL; + +/* SXCHANNEL.addr_uart definitions... */ +#define FASTPATH 0x1000 /* Set to indicate fast rx/tx processing (TA only) */ + +/* SXCHANNEL.xc_status definitions... */ +#define X_TANY 0x0001 /* XON is any character (TA only) */ +#define X_TION 0x0001 /* Tx interrupts on (MTA only) */ +#define X_TXEN 0x0002 /* Tx XON/XOFF enabled (TA only) */ +#define X_RTSEN 0x0002 /* RTS FLOW enabled (MTA only) */ +#define X_TXRC 0x0004 /* XOFF received (TA only) */ +#define X_RTSLOW 0x0004 /* RTS dropped (MTA only) */ +#define X_RXEN 0x0008 /* Rx XON/XOFF enabled */ +#define X_ANYXO 0x0010 /* XOFF pending/sent or RTS dropped */ +#define X_RXSE 0x0020 /* Rx XOFF sent */ +#define X_NPEND 0x0040 /* Rx XON pending or XOFF pending */ +#define X_FPEND 0x0080 /* Rx XOFF pending */ +#define C_CRSE 0x0100 /* Carriage return sent (TA only) */ +#define C_TEMR 0x0100 /* Tx empty requested (MTA only) */ +#define C_TEMA 0x0200 /* Tx empty acked (MTA only) */ +#define C_ANYP 0x0200 /* Any protocol bar tx XON/XOFF (TA only) */ +#define C_EN 0x0400 /* Cooking enabled (on MTA means port is also || */ +#define C_HIGH 0x0800 /* Buffer previously hit high water */ +#define C_CTSEN 0x1000 /* CTS automatic flow-control enabled */ +#define C_DCDEN 0x2000 /* DCD/DTR checking enabled */ +#define C_BREAK 0x4000 /* Break detected */ +#define C_RTSEN 0x8000 /* RTS automatic flow control enabled (MTA only) */ +#define C_PARITY 0x8000 /* Parity checking enabled (TA only) */ + +/* SXCHANNEL.hi_hstat definitions... */ +#define HS_IDLE_OPEN 0x00 /* Channel open state */ +#define HS_LOPEN 0x02 /* Local open command (no modem monitoring) */ +#define HS_MOPEN 0x04 /* Modem open command (wait for DCD signal) */ +#define HS_IDLE_MPEND 0x06 /* Waiting for DCD signal state */ +#define HS_CONFIG 0x08 /* Configuration command */ +#define HS_CLOSE 0x0A /* Close command */ +#define HS_START 0x0C /* Start transmit break command */ +#define HS_STOP 0x0E /* Stop transmit break command */ +#define HS_IDLE_CLOSED 0x10 /* Closed channel state */ +#define HS_IDLE_BREAK 0x12 /* Transmit break state */ +#define HS_FORCE_CLOSED 0x14 /* Force close command */ +#define HS_RESUME 0x16 /* Clear pending XOFF command */ +#define HS_WFLUSH 0x18 /* Flush transmit buffer command */ +#define HS_RFLUSH 0x1A /* Flush receive buffer command */ +#define HS_SUSPEND 0x1C /* Suspend output command (like XOFF received) */ +#define PARALLEL 0x1E /* Parallel port loopback test command (Diagnostics Only) */ +#define ENABLE_RX_INTS 0x20 /* Enable receive interrupts command (Diagnostics Only) */ +#define ENABLE_TX_INTS 0x22 /* Enable transmit interrupts command (Diagnostics Only) */ +#define ENABLE_MDM_INTS 0x24 /* Enable modem interrupts command (Diagnostics Only) */ +#define DISABLE_INTS 0x26 /* Disable interrupts command (Diagnostics Only) */ + +/* SXCHANNEL.hi_mr1 definitions... */ +#define MR1_BITS 0x03 /* Data bits mask */ +#define MR1_5_BITS 0x00 /* 5 data bits */ +#define MR1_6_BITS 0x01 /* 6 data bits */ +#define MR1_7_BITS 0x02 /* 7 data bits */ +#define MR1_8_BITS 0x03 /* 8 data bits */ +#define MR1_PARITY 0x1C /* Parity mask */ +#define MR1_ODD 0x04 /* Odd parity */ +#define MR1_EVEN 0x00 /* Even parity */ +#define MR1_WITH 0x00 /* Parity enabled */ +#define MR1_FORCE 0x08 /* Force parity */ +#define MR1_NONE 0x10 /* No parity */ +#define MR1_NOPARITY MR1_NONE /* No parity */ +#define MR1_ODDPARITY (MR1_WITH|MR1_ODD) /* Odd parity */ +#define MR1_EVENPARITY (MR1_WITH|MR1_EVEN) /* Even parity */ +#define MR1_MARKPARITY (MR1_FORCE|MR1_ODD) /* Mark parity */ +#define MR1_SPACEPARITY (MR1_FORCE|MR1_EVEN) /* Space parity */ +#define MR1_RTS_RXFLOW 0x80 /* RTS receive flow control */ + +/* SXCHANNEL.hi_mr2 definitions... */ +#define MR2_STOP 0x0F /* Stop bits mask */ +#define MR2_1_STOP 0x07 /* 1 stop bit */ +#define MR2_2_STOP 0x0F /* 2 stop bits */ +#define MR2_CTS_TXFLOW 0x10 /* CTS transmit flow control */ +#define MR2_RTS_TOGGLE 0x20 /* RTS toggle on transmit */ +#define MR2_NORMAL 0x00 /* Normal mode */ +#define MR2_AUTO 0x40 /* Auto-echo mode (TA only) */ +#define MR2_LOCAL 0x80 /* Local echo mode */ +#define MR2_REMOTE 0xC0 /* Remote echo mode (TA only) */ + +/* SXCHANNEL.hi_csr definitions... */ +#define CSR_75 0x0 /* 75 baud */ +#define CSR_110 0x1 /* 110 baud (TA), 115200 (MTA/SXDC) */ +#define CSR_38400 0x2 /* 38400 baud */ +#define CSR_150 0x3 /* 150 baud */ +#define CSR_300 0x4 /* 300 baud */ +#define CSR_600 0x5 /* 600 baud */ +#define CSR_1200 0x6 /* 1200 baud */ +#define CSR_2000 0x7 /* 2000 baud */ +#define CSR_2400 0x8 /* 2400 baud */ +#define CSR_4800 0x9 /* 4800 baud */ +#define CSR_1800 0xA /* 1800 baud */ +#define CSR_9600 0xB /* 9600 baud */ +#define CSR_19200 0xC /* 19200 baud */ +#define CSR_57600 0xD /* 57600 baud */ +#define CSR_EXTBAUD 0xF /* Extended baud rate (hi_txbaud/hi_rxbaud) */ + +/* SXCHANNEL.hi_op definitions... */ +#define OP_RTS 0x01 /* RTS modem output signal */ +#define OP_DTR 0x02 /* DTR modem output signal */ + +/* SXCHANNEL.hi_ip definitions... */ +#define IP_CTS 0x02 /* CTS modem input signal */ +#define IP_DCD 0x04 /* DCD modem input signal */ +#define IP_DSR 0x20 /* DTR modem input signal */ +#define IP_RI 0x40 /* RI modem input signal */ + +/* SXCHANNEL.hi_state definitions... */ +#define ST_BREAK 0x01 /* Break received (clear with config) */ +#define ST_DCD 0x02 /* DCD signal changed state */ + +/* SXCHANNEL.hi_prtcl definitions... */ +#define SP_TANY 0x01 /* Transmit XON/XANY (if SP_TXEN enabled) */ +#define SP_TXEN 0x02 /* Transmit XON/XOFF flow control */ +#define SP_CEN 0x04 /* Cooking enabled */ +#define SP_RXEN 0x08 /* Rx XON/XOFF enabled */ +#define SP_DCEN 0x20 /* DCD / DTR check */ +#define SP_DTR_RXFLOW 0x40 /* DTR receive flow control */ +#define SP_PAEN 0x80 /* Parity checking enabled */ + +/* SXCHANNEL.hi_break definitions... */ +#define BR_IGN 0x01 /* Ignore any received breaks */ +#define BR_INT 0x02 /* Interrupt on received break */ +#define BR_PARMRK 0x04 /* Enable parmrk parity error processing */ +#define BR_PARIGN 0x08 /* Ignore chars with parity errors */ +#define BR_ERRINT 0x80 /* Treat parity/framing/overrun errors as exceptions */ + +/* SXCHANNEL.par_error definitions.. */ +#define DIAG_IRQ_RX 0x01 /* Indicate serial receive interrupt (diags only) */ +#define DIAG_IRQ_TX 0x02 /* Indicate serial transmit interrupt (diags only) */ +#define DIAG_IRQ_MD 0x04 /* Indicate serial modem interrupt (diags only) */ + +/* SXCHANNEL.hi_txbaud/hi_rxbaud definitions... (SXDC only) */ +#define BAUD_75 0x00 /* 75 baud */ +#define BAUD_115200 0x01 /* 115200 baud */ +#define BAUD_38400 0x02 /* 38400 baud */ +#define BAUD_150 0x03 /* 150 baud */ +#define BAUD_300 0x04 /* 300 baud */ +#define BAUD_600 0x05 /* 600 baud */ +#define BAUD_1200 0x06 /* 1200 baud */ +#define BAUD_2000 0x07 /* 2000 baud */ +#define BAUD_2400 0x08 /* 2400 baud */ +#define BAUD_4800 0x09 /* 4800 baud */ +#define BAUD_1800 0x0A /* 1800 baud */ +#define BAUD_9600 0x0B /* 9600 baud */ +#define BAUD_19200 0x0C /* 19200 baud */ +#define BAUD_57600 0x0D /* 57600 baud */ +#define BAUD_230400 0x0E /* 230400 baud */ +#define BAUD_460800 0x0F /* 460800 baud */ +#define BAUD_921600 0x10 /* 921600 baud */ +#define BAUD_50 0x11 /* 50 baud */ +#define BAUD_110 0x12 /* 110 baud */ +#define BAUD_134_5 0x13 /* 134.5 baud */ +#define BAUD_200 0x14 /* 200 baud */ +#define BAUD_7200 0x15 /* 7200 baud */ +#define BAUD_56000 0x16 /* 56000 baud */ +#define BAUD_64000 0x17 /* 64000 baud */ +#define BAUD_76800 0x18 /* 76800 baud */ +#define BAUD_128000 0x19 /* 128000 baud */ +#define BAUD_150000 0x1A /* 150000 baud */ +#define BAUD_14400 0x1B /* 14400 baud */ +#define BAUD_256000 0x1C /* 256000 baud */ +#define BAUD_28800 0x1D /* 28800 baud */ + +/* SXCHANNEL.txbreak_state definiions... */ +#define TXBREAK_OFF 0 /* Not sending break */ +#define TXBREAK_START 1 /* Begin sending break */ +#define TXBREAK_START1 2 /* Begin sending break, part 1 */ +#define TXBREAK_ON 3 /* Sending break */ +#define TXBREAK_STOP 4 /* Stop sending break */ +#define TXBREAK_STOP1 5 /* Stop sending break, part 1 */ + +#endif /* _sxwindow_h */ + +/* End of SXWINDOW.H */ + diff --git a/drivers/staging/generic_serial/vme_scc.c b/drivers/staging/generic_serial/vme_scc.c new file mode 100644 index 0000000..9683864 --- /dev/null +++ b/drivers/staging/generic_serial/vme_scc.c @@ -0,0 +1,1145 @@ +/* + * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports + * implementation. + * Copyright 1999 Richard Hirst <richard@sleepie.demon.co.uk> + * + * Based on atari_SCC.c which was + * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include <linux/module.h> +#include <linux/kdev_t.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/miscdevice.h> +#include <linux/console.h> +#include <linux/init.h> +#include <asm/setup.h> +#include <asm/bootinfo.h> + +#ifdef CONFIG_MVME147_SCC +#include <asm/mvme147hw.h> +#endif +#ifdef CONFIG_MVME162_SCC +#include <asm/mvme16xhw.h> +#endif +#ifdef CONFIG_BVME6000_SCC +#include <asm/bvme6000hw.h> +#endif + +#include <linux/generic_serial.h> +#include "scc.h" + + +#define CHANNEL_A 0 +#define CHANNEL_B 1 + +#define SCC_MINOR_BASE 64 + +/* Shadows for all SCC write registers */ +static unsigned char scc_shadow[2][16]; + +/* Location to access for SCC register access delay */ +static volatile unsigned char *scc_del = NULL; + +/* To keep track of STATUS_REG state for detection of Ext/Status int source */ +static unsigned char scc_last_status_reg[2]; + +/***************************** Prototypes *****************************/ + +/* Function prototypes */ +static void scc_disable_tx_interrupts(void * ptr); +static void scc_enable_tx_interrupts(void * ptr); +static void scc_disable_rx_interrupts(void * ptr); +static void scc_enable_rx_interrupts(void * ptr); +static int scc_carrier_raised(struct tty_port *port); +static void scc_shutdown_port(void * ptr); +static int scc_set_real_termios(void *ptr); +static void scc_hungup(void *ptr); +static void scc_close(void *ptr); +static int scc_chars_in_buffer(void * ptr); +static int scc_open(struct tty_struct * tty, struct file * filp); +static int scc_ioctl(struct tty_struct * tty, + unsigned int cmd, unsigned long arg); +static void scc_throttle(struct tty_struct *tty); +static void scc_unthrottle(struct tty_struct *tty); +static irqreturn_t scc_tx_int(int irq, void *data); +static irqreturn_t scc_rx_int(int irq, void *data); +static irqreturn_t scc_stat_int(int irq, void *data); +static irqreturn_t scc_spcond_int(int irq, void *data); +static void scc_setsignals(struct scc_port *port, int dtr, int rts); +static int scc_break_ctl(struct tty_struct *tty, int break_state); + +static struct tty_driver *scc_driver; + +static struct scc_port scc_ports[2]; + +/*--------------------------------------------------------------------------- + * Interface from generic_serial.c back here + *--------------------------------------------------------------------------*/ + +static struct real_driver scc_real_driver = { + scc_disable_tx_interrupts, + scc_enable_tx_interrupts, + scc_disable_rx_interrupts, + scc_enable_rx_interrupts, + scc_shutdown_port, + scc_set_real_termios, + scc_chars_in_buffer, + scc_close, + scc_hungup, + NULL +}; + + +static const struct tty_operations scc_ops = { + .open = scc_open, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .flush_buffer = gs_flush_buffer, + .ioctl = scc_ioctl, + .throttle = scc_throttle, + .unthrottle = scc_unthrottle, + .set_termios = gs_set_termios, + .stop = gs_stop, + .start = gs_start, + .hangup = gs_hangup, + .break_ctl = scc_break_ctl, +}; + +static const struct tty_port_operations scc_port_ops = { + .carrier_raised = scc_carrier_raised, +}; + +/*---------------------------------------------------------------------------- + * vme_scc_init() and support functions + *---------------------------------------------------------------------------*/ + +static int __init scc_init_drivers(void) +{ + int error; + + scc_driver = alloc_tty_driver(2); + if (!scc_driver) + return -ENOMEM; + scc_driver->owner = THIS_MODULE; + scc_driver->driver_name = "scc"; + scc_driver->name = "ttyS"; + scc_driver->major = TTY_MAJOR; + scc_driver->minor_start = SCC_MINOR_BASE; + scc_driver->type = TTY_DRIVER_TYPE_SERIAL; + scc_driver->subtype = SERIAL_TYPE_NORMAL; + scc_driver->init_termios = tty_std_termios; + scc_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + scc_driver->init_termios.c_ispeed = 9600; + scc_driver->init_termios.c_ospeed = 9600; + scc_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(scc_driver, &scc_ops); + + if ((error = tty_register_driver(scc_driver))) { + printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n", + error); + put_tty_driver(scc_driver); + return 1; + } + + return 0; +} + + +/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1). + */ + +static void __init scc_init_portstructs(void) +{ + struct scc_port *port; + int i; + + for (i = 0; i < 2; i++) { + port = scc_ports + i; + tty_port_init(&port->gs.port); + port->gs.port.ops = &scc_port_ops; + port->gs.magic = SCC_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &scc_real_driver; +#ifdef NEW_WRITE_LOCKING + port->gs.port_write_mutex = MUTEX; +#endif + init_waitqueue_head(&port->gs.port.open_wait); + init_waitqueue_head(&port->gs.port.close_wait); + } +} + + +#ifdef CONFIG_MVME147_SCC +static int __init mvme147_scc_init(void) +{ + struct scc_port *port; + int error; + + printk(KERN_INFO "SCC: MVME147 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)M147_SCC_A_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(MVME147_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED, + "SCC-A TX", port); + if (error) + goto fail; + error = request_irq(MVME147_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-A status", port); + if (error) + goto fail_free_a_tx; + error = request_irq(MVME147_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED, + "SCC-A RX", port); + if (error) + goto fail_free_a_stat; + error = request_irq(MVME147_IRQ_SCCA_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-A special cond", port); + if (error) + goto fail_free_a_rx; + + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME147_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)M147_SCC_B_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(MVME147_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED, + "SCC-B TX", port); + if (error) + goto fail_free_a_spcond; + error = request_irq(MVME147_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-B status", port); + if (error) + goto fail_free_b_tx; + error = request_irq(MVME147_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED, + "SCC-B RX", port); + if (error) + goto fail_free_b_stat; + error = request_irq(MVME147_IRQ_SCCB_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-B special cond", port); + if (error) + goto fail_free_b_rx; + + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the PCC chip */ + m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; + +fail_free_b_rx: + free_irq(MVME147_IRQ_SCCB_RX, port); +fail_free_b_stat: + free_irq(MVME147_IRQ_SCCB_STAT, port); +fail_free_b_tx: + free_irq(MVME147_IRQ_SCCB_TX, port); +fail_free_a_spcond: + free_irq(MVME147_IRQ_SCCA_SPCOND, port); +fail_free_a_rx: + free_irq(MVME147_IRQ_SCCA_RX, port); +fail_free_a_stat: + free_irq(MVME147_IRQ_SCCA_STAT, port); +fail_free_a_tx: + free_irq(MVME147_IRQ_SCCA_TX, port); +fail: + return error; +} +#endif + + +#ifdef CONFIG_MVME162_SCC +static int __init mvme162_scc_init(void) +{ + struct scc_port *port; + int error; + + if (!(mvme16x_config & MVME16x_CONFIG_GOT_SCCA)) + return (-ENODEV); + + printk(KERN_INFO "SCC: MVME162 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)MVME_SCC_A_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(MVME162_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED, + "SCC-A TX", port); + if (error) + goto fail; + error = request_irq(MVME162_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-A status", port); + if (error) + goto fail_free_a_tx; + error = request_irq(MVME162_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED, + "SCC-A RX", port); + if (error) + goto fail_free_a_stat; + error = request_irq(MVME162_IRQ_SCCA_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-A special cond", port); + if (error) + goto fail_free_a_rx; + + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME162_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)MVME_SCC_B_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(MVME162_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED, + "SCC-B TX", port); + if (error) + goto fail_free_a_spcond; + error = request_irq(MVME162_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-B status", port); + if (error) + goto fail_free_b_tx; + error = request_irq(MVME162_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED, + "SCC-B RX", port); + if (error) + goto fail_free_b_stat; + error = request_irq(MVME162_IRQ_SCCB_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-B special cond", port); + if (error) + goto fail_free_b_rx; + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the MC2 chip */ + *(volatile char *)0xfff4201d = 0x14; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; + +fail_free_b_rx: + free_irq(MVME162_IRQ_SCCB_RX, port); +fail_free_b_stat: + free_irq(MVME162_IRQ_SCCB_STAT, port); +fail_free_b_tx: + free_irq(MVME162_IRQ_SCCB_TX, port); +fail_free_a_spcond: + free_irq(MVME162_IRQ_SCCA_SPCOND, port); +fail_free_a_rx: + free_irq(MVME162_IRQ_SCCA_RX, port); +fail_free_a_stat: + free_irq(MVME162_IRQ_SCCA_STAT, port); +fail_free_a_tx: + free_irq(MVME162_IRQ_SCCA_TX, port); +fail: + return error; +} +#endif + + +#ifdef CONFIG_BVME6000_SCC +static int __init bvme6000_scc_init(void) +{ + struct scc_port *port; + int error; + + printk(KERN_INFO "SCC: BVME6000 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)BVME_SCC_A_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(BVME_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED, + "SCC-A TX", port); + if (error) + goto fail; + error = request_irq(BVME_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-A status", port); + if (error) + goto fail_free_a_tx; + error = request_irq(BVME_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED, + "SCC-A RX", port); + if (error) + goto fail_free_a_stat; + error = request_irq(BVME_IRQ_SCCA_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-A special cond", port); + if (error) + goto fail_free_a_rx; + + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)BVME_SCC_B_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + error = request_irq(BVME_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED, + "SCC-B TX", port); + if (error) + goto fail_free_a_spcond; + error = request_irq(BVME_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED, + "SCC-B status", port); + if (error) + goto fail_free_b_tx; + error = request_irq(BVME_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED, + "SCC-B RX", port); + if (error) + goto fail_free_b_stat; + error = request_irq(BVME_IRQ_SCCB_SPCOND, scc_spcond_int, + IRQF_DISABLED, "SCC-B special cond", port); + if (error) + goto fail_free_b_rx; + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; + +fail: + free_irq(BVME_IRQ_SCCA_STAT, port); +fail_free_a_tx: + free_irq(BVME_IRQ_SCCA_RX, port); +fail_free_a_stat: + free_irq(BVME_IRQ_SCCA_SPCOND, port); +fail_free_a_rx: + free_irq(BVME_IRQ_SCCB_TX, port); +fail_free_a_spcond: + free_irq(BVME_IRQ_SCCB_STAT, port); +fail_free_b_tx: + free_irq(BVME_IRQ_SCCB_RX, port); +fail_free_b_stat: + free_irq(BVME_IRQ_SCCB_SPCOND, port); +fail_free_b_rx: + return error; +} +#endif + + +static int __init vme_scc_init(void) +{ + int res = -ENODEV; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + res = mvme147_scc_init(); +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + res = mvme162_scc_init(); +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + res = bvme6000_scc_init(); +#endif + return res; +} + +module_init(vme_scc_init); + + +/*--------------------------------------------------------------------------- + * Interrupt handlers + *--------------------------------------------------------------------------*/ + +static irqreturn_t scc_rx_int(int irq, void *data) +{ + unsigned char ch; + struct scc_port *port = data; + struct tty_struct *tty = port->gs.port.tty; + SCC_ACCESS_INIT(port); + + ch = SCCread_NB(RX_DATA_REG); + if (!tty) { + printk(KERN_WARNING "scc_rx_int with NULL tty!\n"); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return IRQ_HANDLED; + } + tty_insert_flip_char(tty, ch, 0); + + /* Check if another character is already ready; in that case, the + * spcond_int() function must be used, because this character may have an + * error condition that isn't signalled by the interrupt vector used! + */ + if (SCCread(INT_PENDING_REG) & + (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) { + scc_spcond_int (irq, data); + return IRQ_HANDLED; + } + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + + +static irqreturn_t scc_spcond_int(int irq, void *data) +{ + struct scc_port *port = data; + struct tty_struct *tty = port->gs.port.tty; + unsigned char stat, ch, err; + int int_pending_mask = port->channel == CHANNEL_A ? + IPR_A_RX : IPR_B_RX; + SCC_ACCESS_INIT(port); + + if (!tty) { + printk(KERN_WARNING "scc_spcond_int with NULL tty!\n"); + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return IRQ_HANDLED; + } + do { + stat = SCCread(SPCOND_STATUS_REG); + ch = SCCread_NB(RX_DATA_REG); + + if (stat & SCSR_RX_OVERRUN) + err = TTY_OVERRUN; + else if (stat & SCSR_PARITY_ERR) + err = TTY_PARITY; + else if (stat & SCSR_CRC_FRAME_ERR) + err = TTY_FRAME; + else + err = 0; + + tty_insert_flip_char(tty, ch, err); + + /* ++TeSche: *All* errors have to be cleared manually, + * else the condition persists for the next chars + */ + if (err) + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + + } while(SCCread(INT_PENDING_REG) & int_pending_mask); + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + + +static irqreturn_t scc_tx_int(int irq, void *data) +{ + struct scc_port *port = data; + SCC_ACCESS_INIT(port); + + if (!port->gs.port.tty) { + printk(KERN_WARNING "scc_tx_int with NULL tty!\n"); + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return IRQ_HANDLED; + } + while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) { + if (port->x_char) { + SCCwrite(TX_DATA_REG, port->x_char); + port->x_char = 0; + } + else if ((port->gs.xmit_cnt <= 0) || + port->gs.port.tty->stopped || + port->gs.port.tty->hw_stopped) + break; + else { + SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]); + port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->gs.xmit_cnt <= 0) + break; + } + } + if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped || + port->gs.port.tty->hw_stopped) { + /* disable tx interrupts */ + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ + port->gs.port.flags &= ~GS_TX_INTEN; + } + if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) + tty_wakeup(port->gs.port.tty); + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return IRQ_HANDLED; +} + + +static irqreturn_t scc_stat_int(int irq, void *data) +{ + struct scc_port *port = data; + unsigned channel = port->channel; + unsigned char last_sr, sr, changed; + SCC_ACCESS_INIT(port); + + last_sr = scc_last_status_reg[channel]; + sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG); + changed = last_sr ^ sr; + + if (changed & SR_DCD) { + port->c_dcd = !!(sr & SR_DCD); + if (!(port->gs.port.flags & ASYNC_CHECK_CD)) + ; /* Don't report DCD changes */ + else if (port->c_dcd) { + wake_up_interruptible(&port->gs.port.open_wait); + } + else { + if (port->gs.port.tty) + tty_hangup (port->gs.port.tty); + } + } + SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return IRQ_HANDLED; +} + + +/*--------------------------------------------------------------------------- + * generic_serial.c callback funtions + *--------------------------------------------------------------------------*/ + +static void scc_disable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + port->gs.port.flags &= ~GS_TX_INTEN; + local_irq_restore(flags); +} + + +static void scc_enable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB); + /* restart the transmitter */ + scc_tx_int (0, port); + local_irq_restore(flags); +} + + +static void scc_disable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + SCCmod(INT_AND_DMA_REG, + ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0); + local_irq_restore(flags); +} + + +static void scc_enable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + SCCmod(INT_AND_DMA_REG, 0xff, + IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL); + local_irq_restore(flags); +} + + +static int scc_carrier_raised(struct tty_port *port) +{ + struct scc_port *sc = container_of(port, struct scc_port, gs.port); + unsigned channel = sc->channel; + + return !!(scc_last_status_reg[channel] & SR_DCD); +} + + +static void scc_shutdown_port(void *ptr) +{ + struct scc_port *port = ptr; + + port->gs.port.flags &= ~ GS_ACTIVE; + if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { + scc_setsignals (port, 0, 0); + } +} + + +static int scc_set_real_termios (void *ptr) +{ + /* the SCC has char sizes 5,7,6,8 in that order! */ + static int chsize_map[4] = { 0, 2, 1, 3 }; + unsigned cflag, baud, chsize, channel, brgval = 0; + unsigned long flags; + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0; + + channel = port->channel; + + if (channel == CHANNEL_A) + return 0; /* Settings controlled by boot PROM */ + + cflag = port->gs.port.tty->termios->c_cflag; + baud = port->gs.baud; + chsize = (cflag & CSIZE) >> 4; + + if (baud == 0) { + /* speed == 0 -> drop DTR */ + local_irq_save(flags); + SCCmod(TX_CTRL_REG, ~TCR_DTR, 0); + local_irq_restore(flags); + return 0; + } + else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) || + (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) || + (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) { + printk(KERN_NOTICE "SCC: Bad speed requested, %d\n", baud); + return 0; + } + + if (cflag & CLOCAL) + port->gs.port.flags &= ~ASYNC_CHECK_CD; + else + port->gs.port.flags |= ASYNC_CHECK_CD; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2; +#endif + /* Now we have all parameters and can go to set them: */ + local_irq_save(flags); + + /* receiver's character size and auto-enables */ + SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE), + (chsize_map[chsize] << 6) | + ((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0)); + /* parity and stop bits (both, Tx and Rx), clock mode never changes */ + SCCmod (AUX1_CTRL_REG, + ~(A1CR_PARITY_MASK | A1CR_MODE_MASK), + ((cflag & PARENB + ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN) + : A1CR_PARITY_NONE) + | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1))); + /* sender's character size, set DTR for valid baud rate */ + SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR); + /* clock sources never change */ + /* disable BRG before changing the value */ + SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0); + /* BRG value */ + SCCwrite(TIMER_LOW_REG, brgval & 0xff); + SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff); + /* BRG enable, and clock source never changes */ + SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB); + + local_irq_restore(flags); + + return 0; +} + + +static int scc_chars_in_buffer (void *ptr) +{ + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1; +} + + +/* Comment taken from sx.c (2.4.0): + 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 scc_hungup(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); +} + + +static void scc_close(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); +} + + +/*--------------------------------------------------------------------------- + * Internal support functions + *--------------------------------------------------------------------------*/ + +static void scc_setsignals(struct scc_port *port, int dtr, int rts) +{ + unsigned long flags; + unsigned char t; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + t = SCCread(TX_CTRL_REG); + if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR); + if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS); + SCCwrite(TX_CTRL_REG, t); + local_irq_restore(flags); +} + + +static void scc_send_xchar(struct tty_struct *tty, char ch) +{ + struct scc_port *port = tty->driver_data; + + port->x_char = ch; + if (ch) + scc_enable_tx_interrupts(port); +} + + +/*--------------------------------------------------------------------------- + * Driver entrypoints referenced from above + *--------------------------------------------------------------------------*/ + +static int scc_open (struct tty_struct * tty, struct file * filp) +{ + int line = tty->index; + int retval; + struct scc_port *port = &scc_ports[line]; + int i, channel = port->channel; + unsigned long flags; + SCC_ACCESS_INIT(port); +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC) + static const struct { + unsigned reg, val; + } mvme_init_tab[] = { + /* Values for MVME162 and MVME147 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif +#if defined(CONFIG_BVME6000_SCC) + static const struct { + unsigned reg, val; + } bvme_init_tab[] = { + /* Values for BVME6000 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif + if (!(port->gs.port.flags & ASYNC_INITIALIZED)) { + local_irq_save(flags); +#if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC) + if (MACH_IS_MVME147 || MACH_IS_MVME16x) { + for (i = 0; i < ARRAY_SIZE(mvme_init_tab); ++i) + SCCwrite(mvme_init_tab[i].reg, mvme_init_tab[i].val); + } +#endif +#if defined(CONFIG_BVME6000_SCC) + if (MACH_IS_BVME6000) { + for (i = 0; i < ARRAY_SIZE(bvme_init_tab); ++i) + SCCwrite(bvme_init_tab[i].reg, bvme_init_tab[i].val); + } +#endif + + /* remember status register for detection of DCD and CTS changes */ + scc_last_status_reg[channel] = SCCread(STATUS_REG); + + port->c_dcd = 0; /* Prevent initial 1->0 interrupt */ + scc_setsignals (port, 1,1); + local_irq_restore(flags); + } + + tty->driver_data = port; + port->gs.port.tty = tty; + port->gs.port.count++; + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.port.count--; + return retval; + } + port->gs.port.flags |= GS_ACTIVE; + retval = gs_block_til_ready(port, filp); + + if (retval) { + port->gs.port.count--; + return retval; + } + + port->c_dcd = tty_port_carrier_raised(&port->gs.port); + + scc_enable_rx_interrupts(port); + + return 0; +} + + +static void scc_throttle (struct tty_struct * tty) +{ + struct scc_port *port = tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + local_irq_save(flags); + SCCmod(TX_CTRL_REG, ~TCR_RTS, 0); + local_irq_restore(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, STOP_CHAR(tty)); +} + + +static void scc_unthrottle (struct tty_struct * tty) +{ + struct scc_port *port = tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + local_irq_save(flags); + SCCmod(TX_CTRL_REG, 0xff, TCR_RTS); + local_irq_restore(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, START_CHAR(tty)); +} + + +static int scc_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + + +static int scc_break_ctl(struct tty_struct *tty, int break_state) +{ + struct scc_port *port = tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + local_irq_save(flags); + SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, + break_state ? TCR_SEND_BREAK : 0); + local_irq_restore(flags); + return 0; +} + + +/*--------------------------------------------------------------------------- + * Serial console stuff... + *--------------------------------------------------------------------------*/ + +#define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0) + +static void scc_ch_write (char ch) +{ + volatile char *p = NULL; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + p = (volatile char *)M147_SCC_A_ADDR; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + p = (volatile char *)MVME_SCC_A_ADDR; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + p = (volatile char *)BVME_SCC_A_ADDR; +#endif + + do { + scc_delay(); + } + while (!(*p & 4)); + scc_delay(); + *p = 8; + scc_delay(); + *p = ch; +} + +/* The console must be locked when we get here. */ + +static void scc_console_write (struct console *co, const char *str, unsigned count) +{ + unsigned long flags; + + local_irq_save(flags); + + while (count--) + { + if (*str == '\n') + scc_ch_write ('\r'); + scc_ch_write (*str++); + } + local_irq_restore(flags); +} + +static struct tty_driver *scc_console_device(struct console *c, int *index) +{ + *index = c->index; + return scc_driver; +} + +static struct console sercons = { + .name = "ttyS", + .write = scc_console_write, + .device = scc_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + + +static int __init vme_scc_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME147 || + vme_brdtype == VME_TYPE_MVME162 || + vme_brdtype == VME_TYPE_MVME172 || + vme_brdtype == VME_TYPE_BVME4000 || + vme_brdtype == VME_TYPE_BVME6000) + register_console(&sercons); + return 0; +} +console_initcall(vme_scc_console_init); |