diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | arch/mips/Makefile | 23 | ||||
-rw-r--r-- | drivers/char/dz.c | 1595 | ||||
-rw-r--r-- | drivers/char/dz.h | 242 | ||||
-rw-r--r-- | drivers/net/bagetlance.c | 1363 | ||||
-rw-r--r-- | drivers/net/declance.c | 1265 | ||||
-rw-r--r-- | drivers/scsi/dec_esp.c | 299 | ||||
-rw-r--r-- | drivers/scsi/dec_esp.h | 42 | ||||
-rw-r--r-- | drivers/tc/Makefile | 21 | ||||
-rw-r--r-- | drivers/tc/tc.c | 230 | ||||
-rw-r--r-- | drivers/tc/zs.c | 2108 | ||||
-rw-r--r-- | drivers/tc/zs.h | 405 | ||||
-rw-r--r-- | include/asm-mips/baget/baget.h | 69 | ||||
-rw-r--r-- | include/asm-mips/baget/vac.h | 208 | ||||
-rw-r--r-- | include/asm-mips/baget/vic.h | 193 | ||||
-rw-r--r-- | include/asm-mips/dec/interrupts.h | 79 | ||||
-rw-r--r-- | include/asm-mips/dec/ioasic_addrs.h | 67 | ||||
-rw-r--r-- | include/asm-mips/dec/ioasic_ints.h | 108 | ||||
-rw-r--r-- | include/asm-mips/dec/kn01.h | 28 | ||||
-rw-r--r-- | include/asm-mips/dec/kn02.h | 41 | ||||
-rw-r--r-- | include/asm-mips/dec/kn02xa.h | 34 | ||||
-rw-r--r-- | include/asm-mips/dec/kn03.h | 33 | ||||
-rw-r--r-- | include/asm-mips/dec/machtype.h | 20 | ||||
-rw-r--r-- | include/asm-mips/dec/tc.h | 43 | ||||
-rw-r--r-- | include/asm-mips/dec/tcinfo.h | 47 | ||||
-rw-r--r-- | include/asm-mips/dec/tcmodule.h | 35 |
26 files changed, 8601 insertions, 1 deletions
@@ -187,6 +187,10 @@ ifdef CONFIG_HAMRADIO DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.a endif +ifeq ($(CONFIG_TC),y) +DRIVERS := $(DRIVERS) drivers/tc/tc.a +endif + include arch/$(ARCH)/Makefile .S.s: diff --git a/arch/mips/Makefile b/arch/mips/Makefile index b5a448d3f..5935a513e 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.12 1998/08/25 09:14:34 ralf Exp $ +# $Id: Makefile,v 1.13 1998/10/18 13:37:31 tsbogend Exp $ # # 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 @@ -97,6 +97,17 @@ CORE_FILES += arch/mips/algor/algor.o SUBDIRS += arch/mips/algor #LOADADDR += 0x80000000 endif + +# +# DECstation family +# +ifdef CONFIG_DECSTATION +CORE_FILES += arch/mips/dec/dec.o +SUBDIRS += arch/mips/dec arch/mips/dec/prom +LIBS += arch/mips/dec/prom/rexlib.a +LOADADDR += 0x80040000 +endif + # # Acer PICA 61, Mips Magnum 4000 and Olivetti M700. # @@ -106,12 +117,14 @@ SUBDIRS += arch/mips/jazz arch/mips/arc LIBS += arch/mips/arc/arclib.a LOADADDR += 0x80080000 endif + ifdef CONFIG_SNI_RM200_PCI CORE_FILES += arch/mips/sni/sni.o SUBDIRS += arch/mips/sni arch/mips/arc LIBS += arch/mips/arc/arclib.a LOADADDR += 0x80080000 endif + ifdef CONFIG_SGI LIBS += arch/mips/sgi/kernel/sgikern.a arch/mips/arc/arclib.a SUBDIRS += arch/mips/sgi/kernel arch/mips/arc @@ -125,6 +138,14 @@ HOSTCC = cc endif # +# Baget/MIPS +# +ifdef CONFIG_BAGET_MIPS +SUBDIRS += arch/mips/baget arch/mips/baget/prom +LIBS += arch/mips/baget/baget.a arch/mips/baget/prom/bagetlib.a +endif + +# # Choosing incompatible machines durings configuration will result in # error messages during linking. Select a default linkscript if # none has been choosen above. diff --git a/drivers/char/dz.c b/drivers/char/dz.c new file mode 100644 index 000000000..347638591 --- /dev/null +++ b/drivers/char/dz.c @@ -0,0 +1,1595 @@ +/* + * dz.c: Serial port driver for DECStations equiped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + * [31-AUG-98] triemer + * Changed IRQ to use Harald's dec internals interrupts.h + * removed base_addr code - moving address assignment to setup.c + * Changed name of dz_init to rs_init to be consistent with tc code + * [13-NOV-98] triemer fixed code to receive characters + * after patches by harald to irq code. + * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout + * field from "current" - somewhere between 2.1.121 and 2.1.131 + */ + +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/mm.h> +#include <linux/major.h> +#include <linux/param.h> +#include <linux/tqueue.h> +#include <linux/interrupt.h> +#include <asm-mips/wbflush.h> +/* for definition of SERIAL */ +#include <asm/dec/interrupts.h> + +/* for definition of struct console */ +#ifdef CONFIG_SERIAL_CONSOLE +#define CONSOLE_LINE (3) +#include <linux/console.h> +#endif /* ifdef CONFIG_SERIAL_CONSOLE */ + +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> + +#include <asm/uaccess.h> +#include <asm/irq.h> +#include <asm/dec/machtype.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> + +#define DEBUG_DZ 1 +#ifdef DEBUG_DZ +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/ptrace.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/fs.h> +#include <asm/bootinfo.h> + +extern int (*prom_printf) (char *,...); +#endif + + + +#include "dz.h" + +#define DZ_INTR_DEBUG 1 + +DECLARE_TASK_QUEUE(tq_serial); + +extern struct wait_queue *keypress_wait; +static struct dz_serial *lines[4]; +static unsigned char tmp_buffer[256]; + + + +#ifdef DEBUG_DZ +/* + * debugging code to send out chars via prom + */ +static void debug_console( const char *s,int count) +{ + unsigned i; + + for (i = 0; i < count; i++) { + if (*s == 10) + prom_printf("%c", 13); + prom_printf("%c", *s++); + } +} +#endif + +/* + * ------------------------------------------------------------ + * dz_in () and dz_out () + * + * These routines are used to access the registers of the DZ + * chip, hiding relocation differences between implementation. + * ------------------------------------------------------------ + */ + +static inline unsigned short dz_in (struct dz_serial *info, unsigned offset) +{ + volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset); + return *addr; +} + +static inline void dz_out (struct dz_serial *info, unsigned offset, unsigned short value) +{ + + volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset); + *addr = value; + +} + +/* + * ------------------------------------------------------------ + * rs_stop () and rs_start () + * + * These routines are called before setting or resetting + * tty->stopped. They enable or disable transmitter interrupts, + * as necessary. + * ------------------------------------------------------------ + */ + +static void dz_stop (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned short mask, tmp; + + + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); /* read the TX flag */ + + tmp &= ~mask; /* clear the TX flag */ + dz_out (info, DZ_TCR, tmp); +} + +static void dz_start (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned short mask, tmp; + + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); /* read the TX flag */ + + tmp |= mask; /* set the TX flag */ + dz_out (info, DZ_TCR, tmp); + +} + +/* + * ------------------------------------------------------------ + * Here starts the interrupt handling routines. All of the + * following subroutines are declared as inline and are folded + * into dz_interrupt. They were separated out for readability's + * sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer dz.c + * + * and look at the resulting assemble code in serial.s. + * + * ------------------------------------------------------------ + */ + +/* + * ------------------------------------------------------------ + * dz_sched_event () + * + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + * ------------------------------------------------------------ + */ +static inline void dz_sched_event (struct dz_serial *info, int event) +{ + info->event |= 1 << event; + queue_task (&info->tqueue, &tq_serial); + mark_bh (SERIAL_BH); +} + +/* + * ------------------------------------------------------------ + * receive_char () + * + * This routine deals with inputs from any lines. + * ------------------------------------------------------------ + */ +static inline void receive_chars (struct dz_serial *info_in) +{ + + struct dz_serial *info; + struct tty_struct *tty = 0; + struct async_icount *icount; + int ignore = 0; + unsigned short status, tmp; + unsigned char ch; + + /* this code is going to be a problem... + the call to tty_flip_buffer is going to need + to be rethought... + */ + do + { + status = dz_in (info_in, DZ_RBUF); + info = lines[LINE(status)]; + + /* punt so we don't get duplicate characters */ + if (!(status & DZ_DVAL)) + goto ignore_char; + + + ch = UCHAR(status); /* grab the char */ + +#ifdef 0 + if (info->is_console) { + if (ch == 0) return; /* it's a break ... */ + + wake_up (&keypress_wait); /* It is a 'keyboard interrupt' ;-) */ + } +#endif + + tty = info->tty; /* now tty points to the proper dev */ + icount = &info->icount; + + if (!tty) break; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = 0; + icount->rx++; + + /* keep track of the statistics */ + if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { + if (status & DZ_PERR) /* parity error */ + icount->parity++; + else if (status & DZ_FERR) /* frame error */ + icount->frame++; + if (status & DZ_OERR) /* overrun error */ + icount->overrun++; + + /* check to see if we should ignore the character + and mask off conditions that should be ignored + */ + + if (status & info->ignore_status_mask) { + if (++ignore > 100 ) break; + goto ignore_char; + } + + /* mask off the error conditions we want to ignore */ + tmp = status & info->read_status_mask; + + if (tmp & DZ_PERR) + { + *tty->flip.flag_buf_ptr = TTY_PARITY; + debug_console("PERR\n",5); + } + else if (tmp & DZ_FERR) + { + *tty->flip.flag_buf_ptr = TTY_FRAME; + debug_console("FERR\n",5); + } + if (tmp & DZ_OERR) + { + debug_console("OERR\n",5); + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + } while (status & DZ_DVAL); + + if (tty) + tty_flip_buffer_push(tty); +} + +/* + * ------------------------------------------------------------ + * transmit_char () + * + * This routine deals with outputs to any lines. + * ------------------------------------------------------------ + */ +static inline void transmit_chars (struct dz_serial *info) +{ + unsigned char tmp; + + + + if (info->x_char) { /* XON/XOFF chars */ + dz_out (info, DZ_TDR, info->x_char); + info->icount.tx++; + info->x_char = 0; + return; + } + + /* if nothing to do or stopped or hardware stopped */ + if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) { + dz_stop (info->tty); + return; + } + + /* if something to do ... (rember the dz has no output fifo so we go one char at a time :-< */ + tmp = (unsigned short)info->xmit_buf[info->xmit_tail++]; + dz_out (info, DZ_TDR, tmp); + info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1); + info->icount.tx++; + + if (--info->xmit_cnt < WAKEUP_CHARS) + dz_sched_event (info, DZ_EVENT_WRITE_WAKEUP); + + + /* Are we done */ + if (info->xmit_cnt <= 0) dz_stop (info->tty); +} + +/* + * ------------------------------------------------------------ + * check_modem_status () + * + * Only valid for the MODEM line duh ! + * ------------------------------------------------------------ + */ +static inline void check_modem_status (struct dz_serial *info) +{ + unsigned short status; + + /* if not ne modem line just return */ + if (info->line != DZ_MODEM) return; + + status = dz_in (info, DZ_MSR); + + /* it's easy, since DSR2 is the only bit in the register */ + if (status) info->icount.dsr++; +} + +/* + * ------------------------------------------------------------ + * dz_interrupt () + * + * this is the main interrupt routine for the DZ chip. + * It deals with the multiple ports. + * ------------------------------------------------------------ + */ +static void dz_interrupt (int irq, void *dev, struct pt_regs *regs) +{ + struct dz_serial *info; + unsigned short status; + + status = dz_in ((struct dz_serial *)dev, DZ_CSR); /* get the reason why we just got an irq */ + info = lines[LINE(status)]; /* re-arrange info the proper port */ + + if (status & DZ_RDONE) + receive_chars (info); /* the receive function */ + + if (status & DZ_TRDY) + transmit_chars (info); +} + +/* + * ------------------------------------------------------------------- + * Here ends the DZ interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh (void) +{ + run_task_queue (&tq_serial); +} + +static void do_softint (void *private_data) +{ + struct dz_serial *info = (struct dz_serial *)private_data; + struct tty_struct *tty = info->tty; + + if (!tty) return; + + if (test_and_clear_bit (DZ_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible (&tty->write_wait); + } +} + +/* + * ------------------------------------------------------------------- + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * ------------------------------------------------------------------- + */ +static void do_serial_hangup (void *private_data) +{ + struct dz_serial *info = (struct dz_serial *)private_data; + struct tty_struct *tty = info->tty;; + + if (!tty) return; + + tty_hangup (tty); +} + +/* + * ------------------------------------------------------------------- + * startup () + * + * various initialization tasks + * ------------------------------------------------------------------- + */ +static int startup (struct dz_serial *info) +{ + unsigned long page, flags; + unsigned short tmp; + + if (info->is_initialized) return 0; + + save_flags (flags); + cli (); + + if (!info->port) { + if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags); + restore_flags (flags); + return -ENODEV; + } + + if (!info->xmit_buf) { + page = get_free_page (GFP_KERNEL); + if (!page) { + restore_flags (flags); + return -ENOMEM; + } + info->xmit_buf = (unsigned char *)page; + } + + if (info->tty) clear_bit (TTY_IO_ERROR, &info->tty->flags); + + /* enable the interrupt and the scanning */ + tmp = dz_in (info, DZ_CSR); + tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); + dz_out (info, DZ_CSR, tmp); + + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* set up the speed */ + change_speed (info); + + /* clear the line transmitter buffer + I can't figure out why I need to do this - but + its necessary - in order for the console portion + and the interrupt portion to live happily side by side. + */ + + /* clear the line transmitter buffer + I can't figure out why I need to do this - but + its necessary - in order for the console portion + and the interrupt portion to live happily side by side. + */ + + info->is_initialized = 1; + + restore_flags (flags); + return 0; +} + +/* + * ------------------------------------------------------------------- + * shutdown () + * + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + * ------------------------------------------------------------------- + */ +static void shutdown (struct dz_serial *info) +{ + unsigned long flags; + unsigned short tmp; + + if (!info->is_initialized) return; + + save_flags (flags); + cli (); + + dz_stop (info->tty); + + + + info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ + dz_out (info, DZ_LPR, info->cflags); + + if (info->xmit_buf) { /* free Tx buffer */ + free_page ((unsigned long)info->xmit_buf); + info->xmit_buf = 0; + } + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + tmp = dz_in (info, DZ_TCR); + if (tmp & DZ_MODEM_DTR) { + tmp &= ~DZ_MODEM_DTR; + dz_out (info, DZ_TCR, tmp); + } + } + + if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags); + + info->is_initialized = 0; + restore_flags (flags); +} + +/* + * ------------------------------------------------------------------- + * change_speed () + * + * set the baud rate. + * ------------------------------------------------------------------- + */ +static void change_speed (struct dz_serial *info) +{ + unsigned long flags; + unsigned cflag; + int baud; + + if (!info->tty || !info->tty->termios) return; + + save_flags (flags); + cli (); + + info->cflags = info->line; + + cflag = info->tty->termios->c_cflag; + + switch (cflag & CSIZE) { + case CS5: info->cflags |= DZ_CS5; break; + case CS6: info->cflags |= DZ_CS6; break; + case CS7: info->cflags |= DZ_CS7; break; + case CS8: + default: info->cflags |= DZ_CS8; + } + + if (cflag & CSTOPB) info->cflags |= DZ_CSTOPB; + if (cflag & PARENB) info->cflags |= DZ_PARENB; + if (cflag & PARODD) info->cflags |= DZ_PARODD; + + baud = tty_get_baud_rate (info->tty); + switch (baud) { + case 50 : info->cflags |= DZ_B50; break; + case 75 : info->cflags |= DZ_B75; break; + case 110 : info->cflags |= DZ_B110; break; + case 134 : info->cflags |= DZ_B134; break; + case 150 : info->cflags |= DZ_B150; break; + case 300 : info->cflags |= DZ_B300; break; + case 600 : info->cflags |= DZ_B600; break; + case 1200: info->cflags |= DZ_B1200; break; + case 1800: info->cflags |= DZ_B1800; break; + case 2000: info->cflags |= DZ_B2000; break; + case 2400: info->cflags |= DZ_B2400; break; + case 3600: info->cflags |= DZ_B3600; break; + case 4800: info->cflags |= DZ_B4800; break; + case 7200: info->cflags |= DZ_B7200; break; + case 9600: + default : info->cflags |= DZ_B9600; + } + + info->cflags |= DZ_RXENAB; + dz_out (info, DZ_LPR, info->cflags); + + /* setup accept flag */ + info->read_status_mask = DZ_OERR; + if (I_INPCK(info->tty)) + info->read_status_mask |= (DZ_FERR | DZ_PERR); + + /* characters to ignore */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= (DZ_FERR | DZ_PERR); + + restore_flags (flags); +} + +/* + * ------------------------------------------------------------------- + * dz_flush_char () + * + * Flush the buffer. + * ------------------------------------------------------------------- + */ +static void dz_flush_chars (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned long flags; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) + return; + + save_flags (flags); + cli (); + + dz_start (info->tty); + + restore_flags (flags); +} + + +/* + * ------------------------------------------------------------------- + * dz_write () + * + * main output routine. + * ------------------------------------------------------------------- + */ +static int dz_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned long flags; + int c, ret = 0; + + if (!tty ) return ret; + if (!info->xmit_buf) return ret; + if (!tmp_buf) tmp_buf = tmp_buffer; + + + + if (from_user) { + + down (&tmp_buf_sem); + while (1) { + c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); + if (c <= 0) break; + + c -= copy_from_user (tmp_buf, buf, c); + if (!c) { + if (!ret) ret = -EFAULT; + break; + } + + save_flags (flags); + cli (); + + c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE-1)); + info->xmit_cnt += c; + + restore_flags(flags); + + buf += c; + count -= c; + ret += c; + } + + up (&tmp_buf_sem); + } else { + + + while (1) { + save_flags (flags); + cli (); + + c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + restore_flags (flags); + break; + } + memcpy (info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE-1)); + info->xmit_cnt += c; + + restore_flags (flags); + + buf += c; + count -= c; + ret += c; + } + } + + + if (info->xmit_cnt) + { + if (!tty->stopped) + { + if (!tty->hw_stopped) + { + dz_start (info->tty); + } + } + } + return ret; +} + +/* + * ------------------------------------------------------------------- + * dz_write_room () + * + * compute the amount of space available for writing. + * ------------------------------------------------------------------- + */ +static int dz_write_room (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + int ret; + + ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) ret = 0; + return ret; +} + +/* + * ------------------------------------------------------------------- + * dz_chars_in_buffer () + * + * compute the amount of char left to be transmitted + * ------------------------------------------------------------------- + */ +static int dz_chars_in_buffer (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + return info->xmit_cnt; +} + +/* + * ------------------------------------------------------------------- + * dz_flush_buffer () + * + * Empty the output buffer + * ------------------------------------------------------------------- + */ +static void dz_flush_buffer (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + cli (); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + sti (); + + wake_up_interruptible (&tty->write_wait); + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * ------------------------------------------------------------ + * dz_throttle () and dz_unthrottle () + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled (or not). + * ------------------------------------------------------------ + */ +static void dz_throttle (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + if (I_IXOFF(tty)) + info->x_char = STOP_CHAR(tty); +} + +static void dz_unthrottle (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + } +} + +static void dz_send_xchar (struct tty_struct *tty, char ch) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + info->x_char = ch; + + if (ch) dz_start (info->tty); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl () and friends + * ------------------------------------------------------------ + */ +static int get_serial_info (struct dz_serial *info, struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + + memset (&tmp, 0, sizeof(tmp)); + + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->port; + tmp.irq = SERIAL; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + + return copy_to_user (retinfo, &tmp, sizeof(*retinfo)); +} + +static int set_serial_info (struct dz_serial *info, struct serial_struct *new_info) +{ + struct serial_struct new_serial; + struct dz_serial old_info; + int retval = 0; + + if (!new_info) + return -EFAULT; + + copy_from_user (&new_serial, new_info, sizeof(new_serial)); + old_info = *info; + + if (!suser()) + return -EPERM; + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + + retval = startup (info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info (struct dz_serial *info, unsigned int *value) +{ + unsigned short status = dz_in (info, DZ_LPR); + + return put_user (status, value); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break (struct dz_serial *info, int duration) +{ + unsigned long flags; + unsigned short tmp, mask; + + if (!info->port) + return; + + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); + tmp |= mask; + + current->state = TASK_INTERRUPTIBLE; + + save_flags (flags); + cli(); + + dz_out (info, DZ_TCR, tmp); + + schedule_timeout(jiffies + duration); + + tmp &= ~mask; + dz_out (info, DZ_TCR, tmp); + + restore_flags (flags); +} + +static int dz_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + int error; + struct dz_serial * info = (struct dz_serial *)tty->driver_data; + int retval; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change (tty); + if (retval) + return retval; + tty_wait_until_sent (tty, 0); + if (!arg) + send_break (info, HZ/4); /* 1/4 second */ + return 0; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change (tty); + if (retval) + return retval; + tty_wait_until_sent (tty, 0); + send_break (info, arg ? arg*(HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(long)); + if (error) + return error; + put_user (C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); + return 0; + + case TIOCSSOFTCAR: + error = get_user (arg, (unsigned long *)arg); + if (error) + return error; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + return 0; + + case TIOCGSERIAL: + error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(struct serial_struct)); + if (error) + return error; + return get_serial_info (info, (struct serial_struct *)arg); + + case TIOCSSERIAL: + return set_serial_info (info, (struct serial_struct *) arg); + + case TIOCSERGETLSR: /* Get line status register */ + error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); + if (error) + return error; + else + return get_lsr_info (info, (unsigned int *)arg); + + case TIOCSERGSTRUCT: + error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(struct dz_serial)); + if (error) + return error; + copy_to_user((struct dz_serial *)arg, info, sizeof(struct dz_serial)); + return 0; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static void dz_set_termios (struct tty_struct *tty, + struct termios *old_termios) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + + change_speed (info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + dz_start (tty); + } +} + +/* + * ------------------------------------------------------------ + * dz_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we turn off + * the transmit enable and receive enable flags. + * ------------------------------------------------------------ + */ +static void dz_close (struct tty_struct *tty, struct file *filp) +{ + struct dz_serial * info = (struct dz_serial *)tty->driver_data; + unsigned long flags; + + if (!info) return; + + save_flags (flags); + cli(); + + if (tty_hung_up_p (filp)) { + restore_flags (flags); + return; + } + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("dz_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); + info->count = 0; + } + + if (info->count) { + restore_flags (flags); + return; + } + info->flags |= DZ_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & DZ_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & DZ_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + + if (info->closing_wait != DZ_CLOSING_WAIT_NONE) + tty_wait_until_sent (tty, info->closing_wait); + + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + + shutdown (info); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer (tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + (tty->ldisc.open)(tty); + } + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(jiffies + info->close_delay); + } + wake_up_interruptible (&info->open_wait); + } + + info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE | DZ_CLOSING); + wake_up_interruptible (&info->close_wait); + + restore_flags (flags); +} + +/* + * dz_hangup () --- called by tty_hangup() when a hangup is signaled. + */ +static void dz_hangup (struct tty_struct *tty) +{ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + dz_flush_buffer (tty); + shutdown (info); + info->event = 0; + info->count = 0; + info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible (&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready (struct tty_struct *tty, struct file *filp, struct dz_serial *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & DZ_CLOSING) { + interruptible_sleep_on (&info->close_wait); + return -EAGAIN; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & DZ_NORMAL_ACTIVE) + return -EBUSY; + + if ((info->flags & DZ_CALLOUT_ACTIVE) && + (info->flags & DZ_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + + if ((info->flags & DZ_CALLOUT_ACTIVE) && + (info->flags & DZ_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= DZ_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & DZ_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= DZ_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & DZ_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + 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, info->count is dropped by one, so that + * dz_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue (&info->open_wait, &wait); + + info->count--; + info->blocked_open++; + while (1) { + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p (filp) || !(info->is_initialized)) { + retval = -EAGAIN; + break; + } + if (!(info->flags & DZ_CALLOUT_ACTIVE) && + !(info->flags & DZ_CLOSING) && do_clocal) + break; + if (signal_pending (current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue (&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + + if (retval) + return retval; + info->flags |= DZ_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port. It also performs the + * serial-specific initialization for the tty structure. + */ +static int dz_open (struct tty_struct *tty, struct file *filp) +{ + struct dz_serial *info; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + + /* The dz lines for the mouse/keyboard must be + * opened using their respective drivers. + */ + if ((line < 0) || (line >= DZ_NB_PORT)) + return -ENODEV; + + if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE)) + return -ENODEV; + + info = lines[line]; + info->count++; + + tty->driver_data = info; + info->tty = tty; + + /* + * Start up serial port + */ + retval = startup (info); + if (retval) + return retval; + + + + retval = block_til_ready (tty, filp, info); + if (retval) + return retval; + + if ((info->count == 1) && (info->flags & DZ_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed (info); + + } + + info->session = current->session; + info->pgrp = current->pgrp; + return 0; +} + +static void show_serial_version (void) +{ + printk("%s%s\n", dz_name, dz_version); +} + + +__initfunc(int dz_init(void)) +{ + int i, flags; + struct dz_serial *info; + + /* Setup base handler, and timer table. */ + init_bh (SERIAL_BH, do_serial_bh); + + show_serial_version (); + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = DZ_NB_PORT; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + + serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = dz_open; + serial_driver.close = dz_close; + serial_driver.write = dz_write; + serial_driver.flush_chars = dz_flush_chars; + serial_driver.write_room = dz_write_room; + serial_driver.chars_in_buffer = dz_chars_in_buffer; + serial_driver.flush_buffer = dz_flush_buffer; + serial_driver.ioctl = dz_ioctl; + serial_driver.throttle = dz_throttle; + serial_driver.unthrottle = dz_unthrottle; + serial_driver.send_xchar = dz_send_xchar; + serial_driver.set_termios = dz_set_termios; + serial_driver.stop = dz_stop; + serial_driver.start = dz_start; + serial_driver.hangup = dz_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver (&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver (&callout_driver)) + panic("Couldn't register callout driver\n"); + save_flags(flags); cli(); + + i = 0; + for (info = &multi[i]; i < DZ_NB_PORT; i++) + { + lines[i] = info; + info->magic = SERIAL_MAGIC; + + if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) + info->port = (unsigned long) KN01_DZ11_BASE; + else + info->port = (unsigned long) KN02_DZ11_BASE; + + info->line = i; + info->tty = 0; + info->close_delay = 50; + info->closing_wait = 3000; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + + /* If we are pointing to address zero then punt - not correctly + set up in setup.c to handle this. */ + if (! info->port) + return 0; + + printk("ttyS%02d at 0x%04x (irq = %d)\n", info->line, info->port, SERIAL); + } + + /* reset the chip */ +#ifndef CONFIG_SERIAL_CONSOLE + dz_out(info, DZ_CSR, DZ_CLR); + while ((tmp = dz_in(info,DZ_CSR)) & DZ_CLR) ; + wbflush(); + + /* enable scanning */ + dz_out(info, DZ_CSR, DZ_MSE); +#endif + + /* order matters here... the trick is that flags + is updated... in request_irq - to immediatedly obliterate + it is unwise. */ + restore_flags(flags); + + + if (request_irq (SERIAL, dz_interrupt, SA_INTERRUPT, "DZ", lines[0])) + panic ("Unable to register DZ interrupt\n"); + + return 0; +} + +#ifdef CONFIG_SERIAL_CONSOLE +static void dz_console_put_char (unsigned char ch) +{ + long flags; + int loops = 1000; + unsigned short tmp = ch; + /* this code sends stuff out to serial device - spinning its + wheels and waiting. */ + + /* force the issue - point it at lines[3]*/ + dz_console=&multi[CONSOLE_LINE]; + + save_flags(flags); + cli(); + + + /* spin our wheels */ + while (((dz_in(dz_console,DZ_TCR) & DZ_TRDY) != DZ_TRDY) && loops--) + ; + + /* Actually transmit the character. */ + dz_out (dz_console, DZ_TDR, tmp); + + restore_flags(flags); +} +/* + * ------------------------------------------------------------------- + * dz_console_print () + * + * dz_console_print is registered for printk. + * ------------------------------------------------------------------- + */ +static void dz_console_print (struct console *cons, + const char *str, + unsigned int count) +{ +#ifdef DEBUG_DZ + prom_printf((char *)str); +#endif + while (count--) + { + if (*str == '\n') + dz_console_put_char ('\r'); + dz_console_put_char (*str++); + } +} + +static int dz_console_wait_key(struct console *co) +{ + return 0; +} + +static kdev_t dz_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +__initfunc(static int dz_console_setup(struct console *co, char *options)) +{ + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + char *s; + unsigned short mask,tmp; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) + parity = *s++; + if (*s) + bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= DZ_B1200; + break; + case 2400: + cflag |= DZ_B2400; + break; + case 4800: + cflag |= DZ_B4800; + break; + case 9600: + default: + cflag |= DZ_B9600; + break; + } + switch(bits) { + case 7: + cflag |= DZ_CS7; + break; + default: + case 8: + cflag |= DZ_CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= DZ_PARODD; + break; + case 'e': case 'E': + cflag |= DZ_PARENB; + break; + } + co->cflag = cflag; + + /* TOFIX: force to console line */ + dz_console = &multi[CONSOLE_LINE]; + dz_console->port = KN01_DZ11_BASE; + dz_console->line = CONSOLE_LINE; + + dz_out(dz_console, DZ_CSR, DZ_CLR); + while ((tmp = dz_in(dz_console,DZ_CSR)) & DZ_CLR) + ; + + /* enable scanning */ + dz_out(dz_console, DZ_CSR, DZ_MSE); + + /* Set up flags... */ + dz_console->cflags = 0; + dz_console->cflags |= DZ_B9600; + dz_console->cflags |= DZ_CS8; + dz_console->cflags |= DZ_PARENB; + dz_out (dz_console, DZ_LPR, dz_console->cflags); + + + mask = 1 << dz_console->line; + tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ + if (!(tmp & mask)) { + tmp |= mask; /* set the TX flag */ + dz_out (dz_console, DZ_TCR, tmp); + } + + + /* TOFIX: force to console line */ + dz_console = &multi[CONSOLE_LINE]; + dz_console->port = KN01_DZ11_BASE; + dz_console->line = CONSOLE_LINE; + + dz_out(dz_console, DZ_CSR, DZ_CLR); + while ((tmp = dz_in(dz_console,DZ_CSR)) & DZ_CLR) + ; + + /* enable scanning */ + dz_out(dz_console, DZ_CSR, DZ_MSE); + + /* Set up flags... */ + dz_console->cflags = 0; + dz_console->cflags |= DZ_B9600; + dz_console->cflags |= DZ_CS8; + dz_console->cflags |= DZ_PARENB; + dz_out (dz_console, DZ_LPR, dz_console->cflags); + + + mask = 1 << dz_console->line; + tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ + if (!(tmp & mask)) { + tmp |= mask; /* set the TX flag */ + dz_out (dz_console, DZ_TCR, tmp); + } + + + return 0; +} + +static struct console dz_sercons = { + "ttyS", + dz_console_print, + NULL, + dz_console_device, + dz_console_wait_key, + NULL, + dz_console_setup, + CON_CONSDEV | CON_PRINTBUFFER, + CONSOLE_LINE, + 0, + NULL +}; + +__initfunc (long dz_serial_console_init(long kmem_start, long kmem_end)) +{ + register_console(&dz_sercons); + + return kmem_start; +} + +#endif /* ifdef CONFIG_SERIAL_CONSOLE */ + diff --git a/drivers/char/dz.h b/drivers/char/dz.h new file mode 100644 index 000000000..1b986d908 --- /dev/null +++ b/drivers/char/dz.h @@ -0,0 +1,242 @@ +/* + * dz.h: Serial port driver for DECStations equiped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + */ +#ifndef DZ_SERIAL_H +#define DZ_SERIAL_H + +/* + * Definitions for the Control and Status Received. + */ +#define DZ_TRDY 0x8000 /* Transmitter empty */ +#define DZ_TIE 0x4000 /* Transmitter Interrupt Enable */ +#define DZ_RDONE 0x0080 /* Receiver data ready */ +#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ +#define DZ_MSE 0x0020 /* Master Scan Enable */ +#define DZ_CLR 0x0010 /* Master reset */ +#define DZ_MAINT 0x0008 /* Loop Back Mode */ + +/* + * Definitions for the Received buffer. + */ +#define DZ_RBUF_MASK 0x00FF /* Data Mask in the Receive Buffer */ +#define DZ_LINE_MASK 0x0300 /* Line Mask in the Receive Buffer */ +#define DZ_DVAL 0x8000 /* Valid Data indicator */ +#define DZ_OERR 0x4000 /* Overrun error indicator */ +#define DZ_FERR 0x2000 /* Frame error indicator */ +#define DZ_PERR 0x1000 /* Parity error indicator */ + +#define LINE(x) (x & DZ_LINE_MASK) >> 8 /* Get the line number from the input buffer */ +#define UCHAR(x) (unsigned char)(x & DZ_RBUF_MASK) + +/* + * Definitions for the Transmit Register. + */ +#define DZ_LINE_KEYBOARD 0x0001 +#define DZ_LINE_MOUSE 0x0002 +#define DZ_LINE_MODEM 0x0004 +#define DZ_LINE_PRINTER 0x0008 + +#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ + +/* + * Definitions for the Modem Status Register. + */ +#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ + +/* + * Definitions for the Transmit Data Register. + */ +#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ +#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ +#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ +#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ + +/* + * Definitions for the Line Parameter Register. + */ +#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ +#define DZ_MOUSE 0x0001 /* line 1 = mouse */ +#define DZ_MODEM 0x0002 /* line 2 = modem */ +#define DZ_PRINTER 0x0003 /* line 3 = printer */ + +#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ +#define DZ_CS5 0x0000 /* 5 bits per byte */ +#define DZ_CS6 0x0008 /* 6 bits per byte */ +#define DZ_CS7 0x0010 /* 7 bits per byte */ +#define DZ_CS8 0x0018 /* 8 bits per byte */ + +#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ + +#define DZ_PARENB 0x0040 /* Parity enable */ +#define DZ_PARODD 0x0080 /* Odd parity instead of even */ + +#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ +#define DZ_B50 0x0000 +#define DZ_B75 0x0100 +#define DZ_B110 0x0200 +#define DZ_B134 0x0300 +#define DZ_B150 0x0400 +#define DZ_B300 0x0500 +#define DZ_B600 0x0600 +#define DZ_B1200 0x0700 +#define DZ_B1800 0x0800 +#define DZ_B2000 0x0900 +#define DZ_B2400 0x0A00 +#define DZ_B3600 0x0B00 +#define DZ_B4800 0x0C00 +#define DZ_B7200 0x0D00 +#define DZ_B9600 0x0E00 + +#define DZ_CREAD 0x1000 /* Enable receiver */ +#define DZ_RXENAB 0x1000 /* enable receive char */ +/* + * Addresses for the DZ registers + */ +#define DZ_CSR 0x00 /* Control and Status Register */ +#define DZ_RBUF 0x08 /* Receive Buffer */ +#define DZ_LPR 0x08 /* Line Parameters Register */ +#define DZ_TCR 0x10 /* Transmitter Control Register */ +#define DZ_MSR 0x18 /* Modem Status Register */ +#define DZ_TDR 0x18 /* Transmit Data Register */ + + +#define DZ_NB_PORT 4 + +#define DZ_XMIT_SIZE 4096 /* buffer size */ +#define WAKEUP_CHARS DZ_XMIT_SIZE/4 + +#define DZ_EVENT_WRITE_WAKEUP 0 + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define DZ_INITIALIZED 0x80000000 /* Serial port was initialized */ +#define DZ_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ +#define DZ_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ +#define DZ_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ +#define DZ_CLOSING 0x08000000 /* Serial port is closing */ +#define DZ_CTS_FLOW 0x04000000 /* Do CTS flow control */ +#define DZ_CHECK_CD 0x02000000 /* i.e., CLOCAL */ + +#define DZ_CLOSING_WAIT_INF 0 +#define DZ_CLOSING_WAIT_NONE 65535 + +#define DZ_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ +#define DZ_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define DZ_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ + +struct dz_serial { + unsigned port; /* base address for the port */ + int type; + int flags; + int baud_base; + int blocked_open; + unsigned short close_delay; + unsigned short closing_wait; + unsigned short line; /* port/line number */ + unsigned short cflags; /* line configuration flag */ + unsigned short x_char; /* xon/xoff character */ + unsigned short read_status_mask; /* mask for read condition */ + unsigned short ignore_status_mask; /* mask for ignore condition */ + unsigned long event; /* mask used in BH */ + unsigned char *xmit_buf; /* Transmit buffer */ + int xmit_head; /* Position of the head */ + int xmit_tail; /* Position of the tail */ + int xmit_cnt; /* Count of the chars in the buffer */ + int count; /* indicates how many times it has been opened */ + int magic; + + struct async_icount icount; /* keep track of things ... */ + struct tty_struct *tty; /* tty associated */ + struct tq_struct tqueue; /* Queue for BH */ + struct tq_struct tqueue_hangup; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + + unsigned char is_console; /* flag indicating a serial console */ + unsigned char is_initialized; +}; + +static struct dz_serial multi[DZ_NB_PORT]; /* Four serial lines in the DZ chip */ +static struct dz_serial *dz_console; +static struct tty_driver serial_driver, callout_driver; + +static struct tty_struct *serial_table[DZ_NB_PORT]; +static struct termios *serial_termios[DZ_NB_PORT]; +static struct termios *serial_termios_locked[DZ_NB_PORT]; + +static int serial_refcount; + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +static struct semaphore tmp_buf_sem = MUTEX; + +static char *dz_name = "DECstation DZ serial driver version "; +static char *dz_version = "1.02"; + +static inline unsigned short dz_in (struct dz_serial *, unsigned); +static inline void dz_out (struct dz_serial *, unsigned, unsigned short); + +static inline void dz_sched_event (struct dz_serial *, int); +static inline void receive_chars (struct dz_serial *); +static inline void transmit_chars (struct dz_serial *); +static inline void check_modem_status (struct dz_serial *); + +static void dz_stop (struct tty_struct *); +static void dz_start (struct tty_struct *); +static void dz_interrupt (int, void *, struct pt_regs *); +static void do_serial_bh (void); +static void do_softint (void *); +static void do_serial_hangup (void *); +static void change_speed (struct dz_serial *); +static void dz_flush_chars (struct tty_struct *); +static void dz_console_print (struct console *, const char *, unsigned int); +static void dz_flush_buffer (struct tty_struct *); +static void dz_throttle (struct tty_struct *); +static void dz_unthrottle (struct tty_struct *); +static void dz_send_xchar (struct tty_struct *, char); +static void shutdown (struct dz_serial *); +static void send_break (struct dz_serial *, int); +static void dz_set_termios (struct tty_struct *, struct termios *); +static void dz_close (struct tty_struct *, struct file *); +static void dz_hangup (struct tty_struct *); +static void show_serial_version (void); + +static int dz_write (struct tty_struct *, int, const unsigned char *, int); +static int dz_write_room (struct tty_struct *); +static int dz_chars_in_buffer (struct tty_struct *); +static int startup (struct dz_serial *); +static int get_serial_info (struct dz_serial *, struct serial_struct *); +static int set_serial_info (struct dz_serial *, struct serial_struct *); +static int get_lsr_info (struct dz_serial *, unsigned int *); +static int dz_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long); +static int block_til_ready (struct tty_struct *, struct file *, struct dz_serial *); +static int dz_open (struct tty_struct *, struct file *); + +#ifdef MODULE +int init_module (void) +void cleanup_module (void) +#endif + +#endif + +#endif /* DZ_SERIAL_H */ diff --git a/drivers/net/bagetlance.c b/drivers/net/bagetlance.c new file mode 100644 index 000000000..f08007e6d --- /dev/null +++ b/drivers/net/bagetlance.c @@ -0,0 +1,1363 @@ +/* $Id$ + * vmelance.c: Ethernet driver for VME Lance cards on Baget/MIPS + * This code stealed and adopted from linux/drivers/net/atarilance.c + * See that for author info + * + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + */ + +/* + * Driver code for Baget/Lance taken from atarilance.c, which also + * works well in case of Besta. Most significant changes made here + * related with 16BIT-only access to A24 space. + */ + +static char *version = "bagetlance.c: v1.1 11/10/98\n"; + +#include <linux/module.h> + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/baget/baget.h> + +#define BAGET_LANCE_IRQ BAGET_IRQ_MASK(0xdf) + +/* + * Define following if you don't need 16BIT-only access to Lance memory + * (Normally BAGET needs it) + */ +#undef NORMAL_MEM_ACCESS + +/* Debug level: + * 0 = silent, print only serious errors + * 1 = normal, print error messages + * 2 = debug, print debug infos + * 3 = debug, print even more debug infos (packet data) + */ + +#define LANCE_DEBUG 1 + +#ifdef LANCE_DEBUG +static int lance_debug = LANCE_DEBUG; +#else +static int lance_debug = 1; +#endif +MODULE_PARM(lance_debug, "i"); + +/* Print debug messages on probing? */ +#undef LANCE_DEBUG_PROBE + +#define DPRINTK(n,a) \ + do { \ + if (lance_debug >= n) \ + printk a; \ + } while( 0 ) + +#ifdef LANCE_DEBUG_PROBE +# define PROBE_PRINT(a) printk a +#else +# define PROBE_PRINT(a) +#endif + +/* These define the number of Rx and Tx buffers as log2. (Only powers + * of two are valid) + * Much more rx buffers (32) are reserved than tx buffers (8), since receiving + * is more time critical then sending and packets may have to remain in the + * board's memory when main memory is low. + */ + +/* Baget Lance has 64K on-board memory, so it looks we can't increase + buffer quantity (40*1.5K is about 64K) */ + +#define TX_LOG_RING_SIZE 3 +#define RX_LOG_RING_SIZE 5 + +/* These are the derived values */ + +#define TX_RING_SIZE (1 << TX_LOG_RING_SIZE) +#define TX_RING_LEN_BITS (TX_LOG_RING_SIZE << 5) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) + +#define RX_RING_SIZE (1 << RX_LOG_RING_SIZE) +#define RX_RING_LEN_BITS (RX_LOG_RING_SIZE << 5) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) + +/* The LANCE Rx and Tx ring descriptors. */ +struct lance_rx_head { + volatile unsigned short base; /* Low word of base addr */ +#ifdef NORMAL_MEM_ACCESS + /* Following two fields are joined into one short to guarantee + 16BIT access to Baget lance registers */ + volatile unsigned char flag; + unsigned char base_hi; /* High word of base addr (unused) */ +#else +/* Following macros are used as replecements to 8BIT fields */ +#define GET_FLAG(h) (((h)->flag_base_hi >> 8) & 0xff) +#define SET_FLAG(h,f) (h)->flag_base_hi = ((h)->flag_base_hi & 0xff) | \ + (((unsigned)(f)) << 8) + volatile unsigned short flag_base_hi; +#endif + volatile short buf_length; /* This length is 2s complement! */ + volatile short msg_length; /* This length is "normal". */ +}; + + +struct lance_tx_head { + volatile unsigned short base; /* Low word of base addr */ +#ifdef NORMAL_MEM_ACCESS +/* See comments above about 8BIT-access Baget A24-space problems */ + volatile unsigned char flag; + unsigned char base_hi; /* High word of base addr (unused) */ +#else + volatile unsigned short flag_base_hi; +#endif + volatile short length; /* Length is 2s complement! */ + volatile short misc; +}; + +struct ringdesc { + volatile unsigned short adr_lo; /* Low 16 bits of address */ +#ifdef NORMAL_MEM_ACCESS +/* See comments above about 8BIT-access Bage A24-space problems */ + unsigned char len; /* Length bits */ + unsigned char adr_hi; /* High 8 bits of address (unused) */ +#else + volatile unsigned short len_adr_hi; +#endif +}; + +/* The LANCE initialization block, described in databook. */ +struct lance_init_block { + unsigned short mode; /* Pre-set mode */ + unsigned char hwaddr[6]; /* Physical ethernet address */ + unsigned filter[2]; /* Multicast filter (unused). */ + /* Receive and transmit ring base, along with length bits. */ + struct ringdesc rx_ring; + struct ringdesc tx_ring; +}; + +/* The whole layout of the Lance shared memory */ +struct lance_memory { + struct lance_init_block init; + struct lance_tx_head tx_head[TX_RING_SIZE]; + struct lance_rx_head rx_head[RX_RING_SIZE]; + char packet_area[0]; /* packet data follow after the + * init block and the ring + * descriptors and are located + * at runtime */ +}; + +/* RieblCard specifics: + * The original TOS driver for these cards reserves the area from offset + * 0xee70 to 0xeebb for storing configuration data. Of interest to us is the + * Ethernet address there, and the magic for verifying the data's validity. + * The reserved area isn't touch by packet buffers. Furthermore, offset 0xfffe + * is reserved for the interrupt vector number. + */ +#define RIEBL_RSVD_START 0xee70 +#define RIEBL_RSVD_END 0xeec0 +#define RIEBL_MAGIC 0x09051990 +#define RIEBL_MAGIC_ADDR ((unsigned long *)(((char *)MEM) + 0xee8a)) +#define RIEBL_HWADDR_ADDR ((unsigned char *)(((char *)MEM) + 0xee8e)) +#define RIEBL_IVEC_ADDR ((unsigned short *)(((char *)MEM) + 0xfffe)) + +/* This is a default address for the old RieblCards without a battery + * that have no ethernet address at boot time. 00:00:36:04 is the + * prefix for Riebl cards, the 00:00 at the end is arbitrary. + */ + +static unsigned char OldRieblDefHwaddr[6] = { + 0x00, 0x00, 0x36, 0x04, 0x00, 0x00 +}; + +/* I/O registers of the Lance chip */ + +struct lance_ioreg { +/* base+0x0 */ volatile unsigned short data; +/* base+0x2 */ volatile unsigned short addr; + unsigned char _dummy1[3]; +/* base+0x7 */ volatile unsigned char ivec; + unsigned char _dummy2[5]; +/* base+0xd */ volatile unsigned char eeprom; + unsigned char _dummy3; +/* base+0xf */ volatile unsigned char mem; +}; + +/* Types of boards this driver supports */ + +enum lance_type { + OLD_RIEBL, /* old Riebl card without battery */ + NEW_RIEBL, /* new Riebl card with battery */ + PAM_CARD /* PAM card with EEPROM */ +}; + +static char *lance_names[] = { + "Riebl-Card (without battery)", + "Riebl-Card (with battery)", + "PAM intern card" +}; + +/* The driver's private device structure */ + +struct lance_private { + enum lance_type cardtype; + struct lance_ioreg *iobase; + struct lance_memory *mem; + int cur_rx, cur_tx; /* The next free ring entry */ + int dirty_tx; /* Ring entries to be freed. */ + /* copy function */ + void *(*memcpy_f)( void *, const void *, size_t ); + struct net_device_stats stats; +/* These two must be ints for set_bit() */ + int tx_full; + int lock; +}; + +/* I/O register access macros */ + +#define MEM lp->mem +#define DREG IO->data +#define AREG IO->addr +#define REGA(a) ( AREG = (a), DREG ) + +/* Definitions for packet buffer access: */ +#define PKT_BUF_SZ 1544 +/* Get the address of a packet buffer corresponding to a given buffer head */ +#define PKTBUF_ADDR(head) (((unsigned char *)(MEM)) + (head)->base) + +/* Possible memory/IO addresses for probing */ + +struct lance_addr { + unsigned long memaddr; + unsigned long ioaddr; + int slow_flag; +} lance_addr_list[] = { + { BAGET_LANCE_MEM_BASE, BAGET_LANCE_IO_BASE, 1 } /* Baget Lance */ +}; + +#define N_LANCE_ADDR (sizeof(lance_addr_list)/sizeof(*lance_addr_list)) + + +#define LANCE_HI_BASE (0xff & (BAGET_LANCE_MEM_BASE >> 16)) + +/* Definitions for the Lance */ + +/* tx_head flags */ +#define TMD1_ENP 0x01 /* end of packet */ +#define TMD1_STP 0x02 /* start of packet */ +#define TMD1_DEF 0x04 /* deferred */ +#define TMD1_ONE 0x08 /* one retry needed */ +#define TMD1_MORE 0x10 /* more than one retry needed */ +#define TMD1_ERR 0x40 /* error summary */ +#define TMD1_OWN 0x80 /* ownership (set: chip owns) */ + +#define TMD1_OWN_CHIP TMD1_OWN +#define TMD1_OWN_HOST 0 + +/* tx_head misc field */ +#define TMD3_TDR 0x03FF /* Time Domain Reflectometry counter */ +#define TMD3_RTRY 0x0400 /* failed after 16 retries */ +#define TMD3_LCAR 0x0800 /* carrier lost */ +#define TMD3_LCOL 0x1000 /* late collision */ +#define TMD3_UFLO 0x4000 /* underflow (late memory) */ +#define TMD3_BUFF 0x8000 /* buffering error (no ENP) */ + +/* rx_head flags */ +#define RMD1_ENP 0x01 /* end of packet */ +#define RMD1_STP 0x02 /* start of packet */ +#define RMD1_BUFF 0x04 /* buffer error */ +#define RMD1_CRC 0x08 /* CRC error */ +#define RMD1_OFLO 0x10 /* overflow */ +#define RMD1_FRAM 0x20 /* framing error */ +#define RMD1_ERR 0x40 /* error summary */ +#define RMD1_OWN 0x80 /* ownership (set: ship owns) */ + +#define RMD1_OWN_CHIP RMD1_OWN +#define RMD1_OWN_HOST 0 + +/* register names */ +#define CSR0 0 /* mode/status */ +#define CSR1 1 /* init block addr (low) */ +#define CSR2 2 /* init block addr (high) */ +#define CSR3 3 /* misc */ +#define CSR8 8 /* address filter */ +#define CSR15 15 /* promiscuous mode */ + +/* CSR0 */ +/* (R=readable, W=writeable, S=set on write, C=clear on write) */ +#define CSR0_INIT 0x0001 /* initialize (RS) */ +#define CSR0_STRT 0x0002 /* start (RS) */ +#define CSR0_STOP 0x0004 /* stop (RS) */ +#define CSR0_TDMD 0x0008 /* transmit demand (RS) */ +#define CSR0_TXON 0x0010 /* transmitter on (R) */ +#define CSR0_RXON 0x0020 /* receiver on (R) */ +#define CSR0_INEA 0x0040 /* interrupt enable (RW) */ +#define CSR0_INTR 0x0080 /* interrupt active (R) */ +#define CSR0_IDON 0x0100 /* initialization done (RC) */ +#define CSR0_TINT 0x0200 /* transmitter interrupt (RC) */ +#define CSR0_RINT 0x0400 /* receiver interrupt (RC) */ +#define CSR0_MERR 0x0800 /* memory error (RC) */ +#define CSR0_MISS 0x1000 /* missed frame (RC) */ +#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) (RC) */ +#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits (RC) */ +#define CSR0_ERR 0x8000 /* error (RC) */ + +/* CSR3 */ +#define CSR3_BCON 0x0001 /* byte control */ +#define CSR3_ACON 0 // fixme: 0x0002 /* ALE control */ +#define CSR3_BSWP 0x0004 /* byte swap (1=big endian) */ + + + +/***************************** Prototypes *****************************/ + +static int addr_accessible( volatile void *regp, int wordflag, int + writeflag ); +static unsigned long lance_probe1( struct device *dev, struct lance_addr + *init_rec ); +static int lance_open( struct device *dev ); +static void lance_init_ring( struct device *dev ); +static int lance_start_xmit( struct sk_buff *skb, struct device *dev ); +static void lance_interrupt( int irq, void *dev_id, struct pt_regs *fp ); +static int lance_rx( struct device *dev ); +static int lance_close( struct device *dev ); +static struct net_device_stats *lance_get_stats( struct device *dev ); +static void set_multicast_list( struct device *dev ); +static int lance_set_mac_address( struct device *dev, void *addr ); + +/************************* End of Prototypes **************************/ + +/* Network traffic statistic (bytes) */ + +int lance_stat = 0; + +static void update_lance_stat (int len) { + lance_stat += len; +} + +/* + This function is used to access Baget/Lance memory to avoid + 8/32BIT access to VAC A24 space + ALL memcpy calls was chenged to this function to avoid dbe problems + Don't confuse with function name -- it stays from original code +*/ + +void *slow_memcpy( void *dst, const void *src, size_t len ) + +{ + unsigned long to = (unsigned long)dst; + unsigned long from = (unsigned long)src; + unsigned long to_end = to +len; + + /* Unaligned flags */ + + int odd_from = from & 1; + int odd_to = to & 1; + int odd_to_end = to_end & 1; + + /* Align for 16BIT-access first */ + + register unsigned short *from_a = (unsigned short*) (from & ~1); + register unsigned short *to_a = (unsigned short*) (to & ~1); + register unsigned short *to_end_a = (unsigned short*) (to_end & ~1); + + /* Caching values -- not in loop invariant */ + + register unsigned short from_v; + register unsigned short to_v; + + /* Invariant is: from_a and to_a are pointers before or exactly to + currently copying byte */ + + if (odd_to) { + /* First byte unaligned case */ + from_v = *from_a; + to_v = *to_a; + + to_v &= ~0xff; + to_v |= 0xff & (from_v >> (odd_from ? 0 : 8)); + *to_a++ = to_v; + + if (odd_from) from_a++; + } + if (odd_from == odd_to) { + /* Same parity */ + while (to_a + 7 < to_end_a) { + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "lh\t%2,0(%1)\n\t" + "nop\n\t" + "lh\t%3,2(%1)\n\t" + "sh\t%2,0(%0)\n\t" + "lh\t%4,4(%1)\n\t" + "sh\t%3,2(%0)\n\t" + "lh\t%5,6(%1)\n\t" + "sh\t%4,4(%0)\n\t" + "lh\t%2,8(%1)\n\t" + "sh\t%5,6(%0)\n\t" + "lh\t%3,10(%1)\n\t" + "sh\t%2,8(%0)\n\t" + "lh\t%4,12(%1)\n\t" + "sh\t%3,10(%0)\n\t" + "lh\t%5,14(%1)\n\t" + "sh\t%4,12(%0)\n\t" + "nop\n\t" + "sh\t%5,14(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to_a), "1" (from_a) + :"memory"); + + to_a += 8; + from_a += 8; + + } + while (to_a < to_end_a) { + *to_a++ = *from_a++; + } + } else { + /* Different parity */ + from_v = *from_a; + while (to_a < to_end_a) { + unsigned short from_v_next; + from_v_next = *++from_a; + *to_a++ = ((from_v & 0xff)<<8) | ((from_v_next>>8) & 0xff); + from_v = from_v_next; + } + + } + if (odd_to_end) { + /* Last byte unaligned case */ + to_v = *to_a; + from_v = *from_a; + + to_v &= ~0xff00; + if (odd_from == odd_to) { + to_v |= from_v & 0xff00; + } else { + to_v |= (from_v<<8) & 0xff00; + } + + *to_a = to_v; + } + + update_lance_stat( len ); + + return( dst ); +} + + +__initfunc(int bagetlance_probe( struct device *dev )) + +{ int i; + static int found = 0; + + if (found) + /* Assume there's only one board possible... That seems true, since + * the Riebl/PAM board's address cannot be changed. */ + return( ENODEV ); + + for( i = 0; i < N_LANCE_ADDR; ++i ) { + if (lance_probe1( dev, &lance_addr_list[i] )) { + found = 1; + return( 0 ); + } + } + + return( ENODEV ); +} + + + +/* Derived from hwreg_present() in vme/config.c: */ + +__initfunc(static int addr_accessible( volatile void *regp, + int wordflag, + int writeflag )) +{ + /* We have a fine function to do it */ + extern int try_read(unsigned long, int); + return try_read((unsigned long)regp, sizeof(short)) != -1; +} + + + +/* Original atari driver uses it */ +#define IRQ_TYPE_PRIO SA_INTERRUPT +#define IRQ_SOURCE_TO_VECTOR(x) (x) + +__initfunc(static unsigned long lance_probe1( struct device *dev, + struct lance_addr *init_rec )) + +{ volatile unsigned short *memaddr = + (volatile unsigned short *)init_rec->memaddr; + volatile unsigned short *ioaddr = + (volatile unsigned short *)init_rec->ioaddr; + struct lance_private *lp; + struct lance_ioreg *IO; + int i; + static int did_version = 0; + unsigned short save1, save2; + + PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n", + (long)memaddr, (long)ioaddr )); + + /* Test whether memory readable and writable */ + PROBE_PRINT(( "lance_probe1: testing memory to be accessible\n" )); + if (!addr_accessible( memaddr, 1, 1 )) goto probe_fail; + + if ((unsigned long)memaddr >= KSEG2) { + extern int kseg2_alloc_io (unsigned long addr, unsigned long size); + if (kseg2_alloc_io((unsigned long)memaddr, BAGET_LANCE_MEM_SIZE)) { + printk("bagetlance: unable map lance memory\n"); + goto probe_fail; + } + } + + /* Written values should come back... */ + PROBE_PRINT(( "lance_probe1: testing memory to be writable (1)\n" )); + save1 = *memaddr; + *memaddr = 0x0001; + if (*memaddr != 0x0001) goto probe_fail; + PROBE_PRINT(( "lance_probe1: testing memory to be writable (2)\n" )); + *memaddr = 0x0000; + if (*memaddr != 0x0000) goto probe_fail; + *memaddr = save1; + + /* First port should be readable and writable */ + PROBE_PRINT(( "lance_probe1: testing ioport to be accessible\n" )); + if (!addr_accessible( ioaddr, 1, 1 )) goto probe_fail; + + /* and written values should be readable */ + PROBE_PRINT(( "lance_probe1: testing ioport to be writeable\n" )); + save2 = ioaddr[1]; + ioaddr[1] = 0x0001; + if (ioaddr[1] != 0x0001) goto probe_fail; + + /* The CSR0_INIT bit should not be readable */ + PROBE_PRINT(( "lance_probe1: testing CSR0 register function (1)\n" )); + save1 = ioaddr[0]; + ioaddr[1] = CSR0; + ioaddr[0] = CSR0_INIT | CSR0_STOP; + if (ioaddr[0] != CSR0_STOP) { + ioaddr[0] = save1; + ioaddr[1] = save2; + goto probe_fail; + } + PROBE_PRINT(( "lance_probe1: testing CSR0 register function (2)\n" )); + ioaddr[0] = CSR0_STOP; + if (ioaddr[0] != CSR0_STOP) { + ioaddr[0] = save1; + ioaddr[1] = save2; + goto probe_fail; + } + + /* Now ok... */ + PROBE_PRINT(( "lance_probe1: Lance card detected\n" )); + goto probe_ok; + + probe_fail: + return( 0 ); + + probe_ok: + init_etherdev( dev, sizeof(struct lance_private) ); + if (!dev->priv) + dev->priv = kmalloc( sizeof(struct lance_private), GFP_KERNEL ); + lp = (struct lance_private *)dev->priv; + MEM = (struct lance_memory *)memaddr; + IO = lp->iobase = (struct lance_ioreg *)ioaddr; + dev->base_addr = (unsigned long)ioaddr; /* informational only */ + lp->memcpy_f = init_rec->slow_flag ? slow_memcpy : memcpy; + + REGA( CSR0 ) = CSR0_STOP; + + /* Now test for type: If the eeprom I/O port is readable, it is a + * PAM card */ + if (addr_accessible( &(IO->eeprom), 0, 0 )) { + /* Switch back to Ram */ + i = IO->mem; + lp->cardtype = PAM_CARD; + } +#ifdef NORMAL_MEM_ACCESS + else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) { +#else + else if (({ + unsigned short *a = (unsigned short*)RIEBL_MAGIC_ADDR; + (((int)a[0]) << 16) + ((int)a[1]) == RIEBL_MAGIC; + })) { +#endif + lp->cardtype = NEW_RIEBL; + } + else + lp->cardtype = OLD_RIEBL; + + if (lp->cardtype == PAM_CARD || + memaddr == (unsigned short *)0xffe00000) { + /* PAMs card and Riebl on ST use level 5 autovector */ + request_irq(BAGET_LANCE_IRQ, lance_interrupt, IRQ_TYPE_PRIO, + "PAM/Riebl-ST Ethernet", dev); + dev->irq = (unsigned short)BAGET_LANCE_IRQ; + } + else { + /* For VME-RieblCards, request a free VME int; + * (This must be unsigned long, since dev->irq is short and the + * IRQ_MACHSPEC bit would be cut off...) + */ + unsigned long irq = BAGET_LANCE_IRQ; + if (!irq) { + printk( "Lance: request for VME interrupt failed\n" ); + return( 0 ); + } + request_irq(irq, lance_interrupt, IRQ_TYPE_PRIO, + "Riebl-VME Ethernet", dev); + dev->irq = irq; + } + + printk("%s: %s at io %#lx, mem %#lx, irq %d%s, hwaddr ", + dev->name, lance_names[lp->cardtype], + (unsigned long)ioaddr, + (unsigned long)memaddr, + dev->irq, + init_rec->slow_flag ? " (slow memcpy)" : "" ); + + /* Get the ethernet address */ + switch( lp->cardtype ) { + case OLD_RIEBL: + /* No ethernet address! (Set some default address) */ + slow_memcpy( dev->dev_addr, OldRieblDefHwaddr, 6 ); + break; + case NEW_RIEBL: + lp->memcpy_f( dev->dev_addr, RIEBL_HWADDR_ADDR, 6 ); + break; + case PAM_CARD: + i = IO->eeprom; + for( i = 0; i < 6; ++i ) + dev->dev_addr[i] = + ((((unsigned short *)MEM)[i*2] & 0x0f) << 4) | + ((((unsigned short *)MEM)[i*2+1] & 0x0f)); + i = IO->mem; + break; + } + for( i = 0; i < 6; ++i ) + printk( "%02x%s", dev->dev_addr[i], (i < 5) ? ":" : "\n" ); + if (lp->cardtype == OLD_RIEBL) { + printk( "%s: Warning: This is a default ethernet address!\n", + dev->name ); + printk( " Use \"ifconfig hw ether ...\" to set the address.\n" ); + } + + MEM->init.mode = 0x0000; /* Disable Rx and Tx. */ + + { + unsigned char hwaddr[6]; + for( i = 0; i < 6; i++ ) + hwaddr[i] = dev->dev_addr[i^1]; /* <- 16 bit swap! */ + slow_memcpy(MEM->init.hwaddr, hwaddr, sizeof(hwaddr)); + } + + MEM->init.filter[0] = 0x00000000; + MEM->init.filter[1] = 0x00000000; + MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head ); + +#ifdef NORMAL_MEM_ACCESS + MEM->init.rx_ring.adr_hi = LANCE_HI_BASE; + MEM->init.rx_ring.len = RX_RING_LEN_BITS; +#else + MEM->init.rx_ring.len_adr_hi = + ((unsigned)RX_RING_LEN_BITS << 8) | LANCE_HI_BASE; +#endif + + + MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head ); + +#ifdef NORMAL_MEM_ACCESS + MEM->init.tx_ring.adr_hi = LANCE_HI_BASE; + MEM->init.tx_ring.len = TX_RING_LEN_BITS; +#else + MEM->init.tx_ring.len_adr_hi = + ((unsigned)TX_RING_LEN_BITS<<8) | LANCE_HI_BASE; +#endif + + if (lp->cardtype == PAM_CARD) + IO->ivec = IRQ_SOURCE_TO_VECTOR(dev->irq); + else + *RIEBL_IVEC_ADDR = IRQ_SOURCE_TO_VECTOR(dev->irq); + + if (did_version++ == 0) + DPRINTK( 1, ( version )); + + /* The LANCE-specific entries in the device structure. */ + dev->open = &lance_open; + dev->hard_start_xmit = &lance_start_xmit; + dev->stop = &lance_close; + dev->get_stats = &lance_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->set_mac_address = &lance_set_mac_address; + dev->start = 0; + + memset( &lp->stats, 0, sizeof(lp->stats) ); + + return( 1 ); +} + + +static int lance_open( struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + struct lance_ioreg *IO = lp->iobase; + int i; + + DPRINTK( 2, ( "%s: lance_open()\n", dev->name )); + + lance_init_ring(dev); + /* Re-initialize the LANCE, and start it when done. */ + + REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0); + REGA( CSR2 ) = 0; + REGA( CSR1 ) = 0; + REGA( CSR0 ) = CSR0_INIT; + /* From now on, AREG is kept to point to CSR0 */ + + i = 1000000; + while (--i > 0) + if (DREG & CSR0_IDON) + break; + if (i < 0 || (DREG & CSR0_ERR)) { + DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n", + dev->name, i, DREG )); + DREG = CSR0_STOP; + return( -EIO ); + } + DREG = CSR0_IDON; + DREG = CSR0_STRT; + DREG = CSR0_INEA; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG )); + MOD_INC_USE_COUNT; + + return( 0 ); +} + + +/* Initialize the LANCE Rx and Tx rings. */ + +static void lance_init_ring( struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + int i; + unsigned offset; + + lp->lock = 0; + lp->tx_full = 0; + lp->cur_rx = lp->cur_tx = 0; + lp->dirty_tx = 0; + + offset = offsetof( struct lance_memory, packet_area ); + +/* If the packet buffer at offset 'o' would conflict with the reserved area + * of RieblCards, advance it */ +#define CHECK_OFFSET(o) \ + do { \ + if (lp->cardtype == OLD_RIEBL || lp->cardtype == NEW_RIEBL) { \ + if (((o) < RIEBL_RSVD_START) ? (o)+PKT_BUF_SZ > RIEBL_RSVD_START \ + : (o) < RIEBL_RSVD_END) \ + (o) = RIEBL_RSVD_END; \ + } \ + } while(0) + + for( i = 0; i < TX_RING_SIZE; i++ ) { + CHECK_OFFSET(offset); + MEM->tx_head[i].base = offset; +#ifdef NORMAL_MEM_ACCESS + MEM->tx_head[i].flag = TMD1_OWN_HOST; + MEM->tx_head[i].base_hi = LANCE_HI_BASE; +#else + MEM->tx_head[i].flag_base_hi = + (TMD1_OWN_HOST<<8) | LANCE_HI_BASE; +#endif + MEM->tx_head[i].length = 0; + MEM->tx_head[i].misc = 0; + offset += PKT_BUF_SZ; + } + + for( i = 0; i < RX_RING_SIZE; i++ ) { + CHECK_OFFSET(offset); + MEM->rx_head[i].base = offset; +#ifdef NORMAL_MEM_ACCESS + MEM->rx_head[i].flag = TMD1_OWN_CHIP; + MEM->rx_head[i].base_hi = LANCE_HI_BASE; +#else + MEM->rx_head[i].flag_base_hi = + (TMD1_OWN_CHIP<<8) | LANCE_HI_BASE; +#endif + MEM->rx_head[i].buf_length = -PKT_BUF_SZ; + MEM->rx_head[i].msg_length = 0; + offset += PKT_BUF_SZ; + } +} + + +static int lance_start_xmit( struct sk_buff *skb, struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + struct lance_ioreg *IO = lp->iobase; + int entry, len; + struct lance_tx_head *head; + unsigned long flags; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 20) + return( 1 ); + AREG = CSR0; + DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n", + dev->name, DREG )); + DREG = CSR0_STOP; + /* + * Always set BSWP after a STOP as STOP puts it back into + * little endian mode. + */ + REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0); + lp->stats.tx_errors++; +#ifndef final_version + { int i; + DPRINTK( 2, ( "Ring data: dirty_tx %d cur_tx %d%s cur_rx %d\n", + lp->dirty_tx, lp->cur_tx, + lp->tx_full ? " (full)" : "", + lp->cur_rx )); + for( i = 0 ; i < RX_RING_SIZE; i++ ) + DPRINTK( 2, ( "rx #%d: base=%04x blen=%04x mlen=%04x\n", + i, MEM->rx_head[i].base, + -MEM->rx_head[i].buf_length, + MEM->rx_head[i].msg_length )); + for( i = 0 ; i < TX_RING_SIZE; i++ ) + DPRINTK( 2, ( "tx #%d: base=%04x len=%04x misc=%04x\n", + i, MEM->tx_head[i].base, + -MEM->tx_head[i].length, + MEM->tx_head[i].misc )); + } +#endif + lance_init_ring(dev); + REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT; + + dev->tbusy = 0; + dev->trans_start = jiffies; + + return( 0 ); + } + + DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n", + dev->name, DREG )); + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit( 0, (void*)&dev->tbusy ) != 0) { + DPRINTK( 0, ( "%s: Transmitter access conflict.\n", dev->name )); + return 1; + } + + if (test_and_set_bit( 0, (void*)&lp->lock ) != 0) { + DPRINTK( 0, ( "%s: tx queue lock!.\n", dev->name )); + /* don't clear dev->tbusy flag. */ + return 1; + } + + /* Fill in a Tx ring entry */ + if (lance_debug >= 3) { + u_char *p; + int i; + printk( "%s: TX pkt type 0x%04x from ", dev->name, + ((u_short *)skb->data)[6]); + for( p = &((u_char *)skb->data)[6], i = 0; i < 6; i++ ) + printk("%02x%s", *p++, i != 5 ? ":" : "" ); + printk(" to "); + for( p = (u_char *)skb->data, i = 0; i < 6; i++ ) + printk("%02x%s", *p++, i != 5 ? ":" : "" ); + printk(" data at 0x%08x len %d\n", (int)skb->data, + (int)skb->len ); + } + + /* We're not prepared for the int until the last flags are set/reset. And + * the int may happen already after setting the OWN_CHIP... */ + save_flags(flags); + cli(); + + /* Mask to ring buffer boundary. */ + entry = lp->cur_tx & TX_RING_MOD_MASK; + head = &(MEM->tx_head[entry]); + + /* Caution: the write order is important here, set the "ownership" bits + * last. + */ + + /* The old LANCE chips doesn't automatically pad buffers to min. size. */ + len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + /* PAM-Card has a bug: Can only send packets with even number of bytes! */ + if (lp->cardtype == PAM_CARD && (len & 1)) + ++len; + + head->length = -len; + head->misc = 0; + lp->memcpy_f( PKTBUF_ADDR(head), (void *)skb->data, skb->len ); +#ifdef NORMAL_MEM_ACCESS + head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP; +#else + SET_FLAG(head,(TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP)); +#endif + dev_kfree_skb( skb ); + lp->cur_tx++; + lp->stats.tx_bytes += skb->len; + while( lp->cur_tx >= TX_RING_SIZE && lp->dirty_tx >= TX_RING_SIZE ) { + lp->cur_tx -= TX_RING_SIZE; + lp->dirty_tx -= TX_RING_SIZE; + } + + /* Trigger an immediate send poll. */ + DREG = CSR0_INEA | CSR0_TDMD; + dev->trans_start = jiffies; + + lp->lock = 0; +#ifdef NORMAL_MEM_ACCESS + if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) == +#else + if ((GET_FLAG(&MEM->tx_head[(entry+1) & TX_RING_MOD_MASK]) & TMD1_OWN) == +#endif + TMD1_OWN_HOST) + dev->tbusy = 0; + else + lp->tx_full = 1; + restore_flags(flags); + + return 0; +} + +/* The LANCE interrupt handler. */ + +static void lance_interrupt( int irq, void *dev_id, struct pt_regs *fp) +{ + struct device *dev = dev_id; + struct lance_private *lp; + struct lance_ioreg *IO; + int csr0, boguscnt = 10; + + if (dev == NULL) { + DPRINTK( 1, ( "lance_interrupt(): interrupt for unknown device.\n" )); + return; + } + + lp = (struct lance_private *)dev->priv; + IO = lp->iobase; + AREG = CSR0; + + if (dev->interrupt) { + DPRINTK( 1, ( "Re-entering CAUSE=%08x STATUS=%08x\n", + read_32bit_cp0_register(CP0_CAUSE), + read_32bit_cp0_register(CP0_STATUS) )); + panic("lance: interrupt handler reentered !"); + } + + dev->interrupt = 1; + + while( ((csr0 = DREG) & (CSR0_ERR | CSR0_TINT | CSR0_RINT)) && + --boguscnt >= 0) { + /* Acknowledge all of the current interrupt sources ASAP. */ + DREG = csr0 & ~(CSR0_INIT | CSR0_STRT | CSR0_STOP | + CSR0_TDMD | CSR0_INEA); + + DPRINTK( 2, ( "%s: interrupt csr0=%04x new csr=%04x.\n", + dev->name, csr0, DREG )); + + if (csr0 & CSR0_RINT) /* Rx interrupt */ + lance_rx( dev ); + + if (csr0 & CSR0_TINT) { /* Tx-done interrupt */ + int dirty_tx = lp->dirty_tx; + + while( dirty_tx < lp->cur_tx) { + int entry = dirty_tx & TX_RING_MOD_MASK; +#ifdef NORMAL_MEM_ACCESS + int status = MEM->tx_head[entry].flag; +#else + int status = GET_FLAG(&MEM->tx_head[entry]); +#endif + if (status & TMD1_OWN_CHIP) + break; /* It still hasn't been Txed */ + +#ifdef NORMAL_MEM_ACCESS + MEM->tx_head[entry].flag = 0; +#else + SET_FLAG(&MEM->tx_head[entry],0); +#endif + + if (status & TMD1_ERR) { + /* There was an major error, log it. */ + int err_status = MEM->tx_head[entry].misc; + lp->stats.tx_errors++; + if (err_status & TMD3_RTRY) lp->stats.tx_aborted_errors++; + if (err_status & TMD3_LCAR) lp->stats.tx_carrier_errors++; + if (err_status & TMD3_LCOL) lp->stats.tx_window_errors++; + if (err_status & TMD3_UFLO) { + /* Ackk! On FIFO errors the Tx unit is turned off! */ + lp->stats.tx_fifo_errors++; + /* Remove this verbosity later! */ + DPRINTK( 1, ( "%s: Tx FIFO error! Status %04x\n", + dev->name, csr0 )); + /* Restart the chip. */ + DREG = CSR0_STRT; + } + } else { + if (status & (TMD1_MORE | TMD1_ONE | TMD1_DEF)) + lp->stats.collisions++; + lp->stats.tx_packets++; + } + dirty_tx++; + } + +#ifndef final_version + if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { + DPRINTK( 0, ( "out-of-sync dirty pointer," + " %d vs. %d, full=%d.\n", + dirty_tx, lp->cur_tx, lp->tx_full )); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (lp->tx_full && dev->tbusy + && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + /* The ring is no longer full, clear tbusy. */ + lp->tx_full = 0; + dev->tbusy = 0; + mark_bh( NET_BH ); + } + + lp->dirty_tx = dirty_tx; + } + + /* Log misc errors. */ + if (csr0 & CSR0_BABL) lp->stats.tx_errors++; /* Tx babble. */ + if (csr0 & CSR0_MISS) lp->stats.rx_errors++; /* Missed a Rx frame. */ + if (csr0 & CSR0_MERR) { + DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), " + "status %04x.\n", dev->name, csr0 )); + /* Restart the chip. */ + DREG = CSR0_STRT; + } + } + + /* Clear any other interrupt, and set interrupt enable. */ + DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR | + CSR0_IDON | CSR0_INEA; + + DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n", + dev->name, DREG )); + dev->interrupt = 0; + return; +} + + +static int lance_rx( struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + int entry = lp->cur_rx & RX_RING_MOD_MASK; + int i; + +#ifdef NORMAL_MEM_ACCESS + DPRINTK( 2, ( "%s: rx int, flag=%04x\n", dev->name, + MEM->rx_head[entry].flag )); +#else + DPRINTK( 2, ( "%s: rx int, flag=%04x\n", dev->name, + GET_FLAG(&MEM->rx_head[entry]) )); +#endif + + /* If we own the next entry, it's a new packet. Send it up. */ +#ifdef NORMAL_MEM_ACCESS + while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) { +#else + while( (GET_FLAG(&MEM->rx_head[entry]) & RMD1_OWN) == RMD1_OWN_HOST ) { +#endif + struct lance_rx_head *head = &(MEM->rx_head[entry]); +#ifdef NORMAL_MEM_ACCESS + int status = head->flag; +#else + int status = GET_FLAG(head); +#endif + + if (status != (RMD1_ENP|RMD1_STP)) { /* There was an error. */ + /* There is a tricky error noted by John Murphy, + <murf@perftech.com> to Russ Nelson: Even with full-sized + buffers it's possible for a jabber packet to use two + buffers, with only the last correctly noting the error. */ + if (status & RMD1_ENP) /* Only count a general error at the */ + lp->stats.rx_errors++; /* end of a packet.*/ + if (status & RMD1_FRAM) lp->stats.rx_frame_errors++; + if (status & RMD1_OFLO) lp->stats.rx_over_errors++; + if (status & RMD1_CRC) lp->stats.rx_crc_errors++; + if (status & RMD1_BUFF) lp->stats.rx_fifo_errors++; +#ifdef NORMAL_MEM_ACCESS + head->flag &= (RMD1_ENP|RMD1_STP); +#else + SET_FLAG(head,GET_FLAG(head) & (RMD1_ENP|RMD1_STP)); +#endif + } else { + /* Malloc up new buffer, compatible with net-3. */ + short pkt_len = head->msg_length & 0xfff; + struct sk_buff *skb; + + if (pkt_len < 60) { + printk( "%s: Runt packet!\n", dev->name ); + lp->stats.rx_errors++; + } + else { + skb = dev_alloc_skb( pkt_len+2 ); + if (skb == NULL) { + DPRINTK( 1, ( "%s: Memory squeeze, deferring packet.\n", + dev->name )); + for( i = 0; i < RX_RING_SIZE; i++ ) +#ifdef NORMAL_MEM_ACCESS + if (MEM->rx_head[(entry+i) & RX_RING_MOD_MASK].flag & +#else + if (GET_FLAG(&MEM->rx_head[(entry+i) & \ + RX_RING_MOD_MASK]) & +#endif + RMD1_OWN_CHIP) + break; + + if (i > RX_RING_SIZE - 2) { + lp->stats.rx_dropped++; +#ifdef NORMAL_MEM_ACCESS + head->flag |= RMD1_OWN_CHIP; +#else + SET_FLAG(head,GET_FLAG(head) | RMD1_OWN_CHIP); +#endif + lp->cur_rx++; + } + break; + } + + if (lance_debug >= 3) { + u_char *data = PKTBUF_ADDR(head), *p; + printk( "%s: RX pkt type 0x%04x from ", dev->name, + ((u_short *)data)[6]); + for( p = &data[6], i = 0; i < 6; i++ ) + printk("%02x%s", *p++, i != 5 ? ":" : "" ); + printk(" to "); + for( p = data, i = 0; i < 6; i++ ) + printk("%02x%s", *p++, i != 5 ? ":" : "" ); + printk(" data %02x %02x %02x %02x %02x %02x %02x %02x " + "len %d\n", + data[15], data[16], data[17], data[18], + data[19], data[20], data[21], data[22], + pkt_len ); + } + + skb->dev = dev; + skb_reserve( skb, 2 ); /* 16 byte align */ + skb_put( skb, pkt_len ); /* Make room */ + lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + } + } + +#ifdef NORMAL_MEM_ACCESS + head->flag |= RMD1_OWN_CHIP; +#else + SET_FLAG(head,GET_FLAG(head) | RMD1_OWN_CHIP); +#endif + entry = (++lp->cur_rx) & RX_RING_MOD_MASK; + } + lp->cur_rx &= RX_RING_MOD_MASK; + + /* From lance.c (Donald Becker): */ + /* We should check that at least two ring entries are free. If not, + we should free one and mark stats->rx_dropped++. */ + + return 0; +} + + +static int lance_close( struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + struct lance_ioreg *IO = lp->iobase; + + dev->start = 0; + dev->tbusy = 1; + + AREG = CSR0; + + DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, DREG )); + + /* We stop the LANCE here -- it occasionally polls + memory if we don't. */ + DREG = CSR0_STOP; + + MOD_DEC_USE_COUNT; + return 0; +} + + +static struct net_device_stats *lance_get_stats( struct device *dev ) + +{ + struct lance_private *lp = (struct lance_private *)dev->priv; + return &lp->stats; +} + + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ + +static void set_multicast_list( struct device *dev ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + struct lance_ioreg *IO = lp->iobase; + + if (!dev->start) + /* Only possible if board is already started */ + return; + + /* We take the simple way out and always enable promiscuous mode. */ + DREG = CSR0_STOP; /* Temporarily stop the lance. */ + + if (dev->flags & IFF_PROMISC) { + /* Log any net taps. */ + DPRINTK( 1, ( "%s: Promiscuous mode enabled.\n", dev->name )); + REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */ + } else { + short multicast_table[4]; + int num_addrs = dev->mc_count; + int i; + /* We don't use the multicast table, but rely on upper-layer + * filtering. */ + memset( multicast_table, (num_addrs == 0) ? 0 : -1, + sizeof(multicast_table) ); + for( i = 0; i < 4; i++ ) + REGA( CSR8+i ) = multicast_table[i]; + REGA( CSR15 ) = 0; /* Unset promiscuous mode */ + } + + /* + * Always set BSWP after a STOP as STOP puts it back into + * little endian mode. + */ + REGA( CSR3 ) = CSR3_BSWP | (lp->cardtype == PAM_CARD ? CSR3_ACON : 0); + + /* Resume normal operation and reset AREG to CSR0 */ + REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT; +} + + +/* This is needed for old RieblCards and possible for new RieblCards */ + +static int lance_set_mac_address( struct device *dev, void *addr ) + +{ struct lance_private *lp = (struct lance_private *)dev->priv; + struct sockaddr *saddr = addr; + int i; + + if (lp->cardtype != OLD_RIEBL && lp->cardtype != NEW_RIEBL) + return( -EOPNOTSUPP ); + + if (dev->start) { + /* Only possible while card isn't started */ + DPRINTK( 1, ( "%s: hwaddr can be set only while card isn't open.\n", + dev->name )); + return( -EIO ); + } + + slow_memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len ); + + { + unsigned char hwaddr[6]; + for( i = 0; i < 6; i++ ) + hwaddr[i] = dev->dev_addr[i^1]; /* <- 16 bit swap! */ + slow_memcpy(MEM->init.hwaddr, hwaddr, sizeof(hwaddr)); + } + + lp->memcpy_f( RIEBL_HWADDR_ADDR, dev->dev_addr, 6 ); + /* set also the magic for future sessions */ +#ifdef NORMAL_MEM_ACCESS + *RIEBL_MAGIC_ADDR = RIEBL_MAGIC; +#else + { + unsigned long magic = RIEBL_MAGIC; + slow_memcpy(RIEBL_MAGIC_ADDR, &magic, sizeof(*RIEBL_MAGIC_ADDR)); + } +#endif + return( 0 ); +} + + +#ifdef MODULE +static char devicename[9] = { 0, }; + +static struct device bagetlance_dev = +{ + devicename, /* filled in by register_netdev() */ + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, bagetlance_probe, +}; + +int init_module(void) + +{ int err; + + if ((err = register_netdev( &bagetlance_dev ))) { + if (err == -EIO) { + printk( "No Vme Lance board found. Module not loaded.\n"); + } + return( err ); + } + return( 0 ); +} + +void cleanup_module(void) + +{ + unregister_netdev( &bagetlance_dev ); +} + +#endif /* MODULE */ + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/net/declance.c b/drivers/net/declance.c new file mode 100644 index 000000000..b5c5b4f42 --- /dev/null +++ b/drivers/net/declance.c @@ -0,0 +1,1265 @@ +/* + * Lance ethernet driver for the MIPS processor based + * DECstation family + * + * + * adopted from sunlance.c by Richard van den Berg + * + * additional sources: + * - PMAD-AA TURBOchannel Ethernet Module Functional Specification, + * Revision 1.2 + * + * History: + * + * v0.001: The kernel accepts the code and it shows the hardware address. + * + * v0.002: Removed most sparc stuff, left only some module and dma stuff. + * + * v0.003: Enhanced base address calculation from proposals by + * Harald Koerfgen and Thomas Riemer. + * + * v0.004: lance-regs is pointing at the right addresses, added prom + * check. First start of address mapping and DMA. + * + * v0.005: started to play around with LANCE-DMA. This driver will not work + * for non IOASIC lances. HK + * + * v0.006: added pointer arrays to lance_private and setup routine for them + * in dec_lance_init. HK + * + * v0.007: Big shit. The LANCE seems to use a different DMA mechanism to access + * the init block. This looks like one (short) word at a time, but the smallest + * amount the IOASIC can transfer is a (long) word. So we have a 2-2 padding here. + * Changed lance_init_block accordingly. The 16-16 padding for the buffers + * seems to be correct. HK + * + * v0.008 - mods to make PMAX_LANCE work. 01/09/1999 triemer + */ + +#undef DEBUG_DRIVER + +static char *version = +"declance.c: v0.008 by Linux Mips DECstation task force\n"; + +static char *lancestr = "LANCE"; + +/* + * card types + */ +#define ASIC_LANCE 1 +#define PMAD_LANCE 2 +#define PMAX_LANCE 3 + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic_ints.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/machtype.h> +#include <asm/dec/tc.h> +#include <asm/dec/kn01.h> +#include <asm/wbflush.h> +#include <asm/addrspace.h> + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/hdreg.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/string.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/utsname.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <linux/etherdevice.h> + +#ifndef CONFIG_TC +unsigned long system_base = 0; +unsigned long dmaptr; +#endif +static int type; + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +#define LE_CSR0 0 +#define LE_CSR1 1 +#define LE_CSR2 2 +#define LE_CSR3 3 + +#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */ + +#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */ +#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */ +#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */ +#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */ +#define LE_C0_MERR 0x0800 /* ME: Memory error */ +#define LE_C0_RINT 0x0400 /* Received interrupt */ +#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */ +#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */ +#define LE_C0_INTR 0x0080 /* Interrupt or error */ +#define LE_C0_INEA 0x0040 /* Interrupt enable */ +#define LE_C0_RXON 0x0020 /* Receiver on */ +#define LE_C0_TXON 0x0010 /* Transmitter on */ +#define LE_C0_TDMD 0x0008 /* Transmitter demand */ +#define LE_C0_STOP 0x0004 /* Stop the card */ +#define LE_C0_STRT 0x0002 /* Start the card */ +#define LE_C0_INIT 0x0001 /* Init the card */ + +#define LE_C3_BSWP 0x4 /* SWAP */ +#define LE_C3_ACON 0x2 /* ALE Control */ +#define LE_C3_BCON 0x1 /* Byte control */ + +/* Receive message descriptor 1 */ +#define LE_R1_OWN 0x80 /* Who owns the entry */ +#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */ +#define LE_R1_FRA 0x20 /* FRA: Frame error */ +#define LE_R1_OFL 0x10 /* OFL: Frame overflow */ +#define LE_R1_CRC 0x08 /* CRC error */ +#define LE_R1_BUF 0x04 /* BUF: Buffer error */ +#define LE_R1_SOP 0x02 /* Start of packet */ +#define LE_R1_EOP 0x01 /* End of packet */ +#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T1_OWN 0x80 /* Lance owns the packet */ +#define LE_T1_ERR 0x40 /* Error summary */ +#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */ +#define LE_T1_EONE 0x08 /* Error: one retry needed */ +#define LE_T1_EDEF 0x04 /* Error: deferred */ +#define LE_T1_SOP 0x02 /* Start of packet */ +#define LE_T1_EOP 0x01 /* End of packet */ +#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T3_BUF 0x8000 /* Buffer error */ +#define LE_T3_UFL 0x4000 /* Error underflow */ +#define LE_T3_LCOL 0x1000 /* Error late collision */ +#define LE_T3_CLOS 0x0800 /* Error carrier loss */ +#define LE_T3_RTY 0x0400 /* Error retry */ +#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */ + +/* Define: 2^4 Tx buffers and 2^4 Rx buffers */ + +#ifndef LANCE_LOG_TX_BUFFERS +#define LANCE_LOG_TX_BUFFERS 4 +#define LANCE_LOG_RX_BUFFERS 4 +#endif + +#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) + +#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) + +#define PKT_BUF_SZ 1536 +#define RX_BUFF_SIZE PKT_BUF_SZ +#define TX_BUFF_SIZE PKT_BUF_SZ + +#undef TEST_HITS +#define DEBUG_DRIVER 1 + +#define ZERO 0 + +/* The DS2000/3000 have a linear 64 KB buffer. + + * The PMAD-AA has 128 kb buffer on-board. + * + * The IOASIC LANCE devices use a shared memory region. This region as seen + * from the CPU is (max) 128 KB long and has to be on an 128 KB boundary. + * The LANCE sees this as a 64 KB long continuous memory region. + * + * The LANCE's DMA address is used as an index in this buffer and DMA takes + * place in bursts of eight 16-Bit words which are packed into four 32-Bit words + * by the IOASIC. This leads to a strange padding: 16 bytes of valid data followed + * by a 16 byte gap :-(. + */ + +struct lance_rx_desc { + unsigned short rmd0; /* low address of packet */ + short gap0; + unsigned char rmd1_hadr; /* high address of packet */ + unsigned char rmd1_bits; /* descriptor bits */ + short gap1; + short length; /* This length is 2s complement (negative)! + * Buffer length + */ + short gap2; + unsigned short mblength; /* This is the actual number of bytes received */ + short gap3; +}; + +struct lance_tx_desc { + unsigned short tmd0; /* low address of packet */ + short gap0; + unsigned char tmd1_hadr; /* high address of packet */ + unsigned char tmd1_bits; /* descriptor bits */ + short gap1; + short length; /* Length is 2s complement (negative)! */ + short gap2; + unsigned short misc; + short gap3; +}; + + +/* First part of the LANCE initialization block, described in databook. */ +struct lance_init_block { + unsigned short mode; /* Pre-set mode (reg. 15) */ + short gap0; + + unsigned char phys_addr[12]; /* Physical ethernet address + * only 0, 1, 4, 5, 8, 9 are valid + * 2, 3, 6, 7, 10, 11 are gaps + */ + unsigned short filter[8]; /* Multicast filter. + * only 0, 2, 4, 6 are valid + * 1, 3, 5, 7 are gaps + */ + + /* Receive and transmit ring base, along with extra bits. */ + unsigned short rx_ptr; /* receive descriptor addr */ + short gap1; + unsigned short rx_len; /* receive len and high addr */ + short gap2; + unsigned short tx_ptr; /* transmit descriptor addr */ + short gap3; + unsigned short tx_len; /* transmit len and high addr */ + short gap4; + char gap5[16]; + + /* The buffer descriptors */ + struct lance_rx_desc brx_ring[RX_RING_SIZE]; + struct lance_tx_desc btx_ring[TX_RING_SIZE]; +}; + +#define BUF_OFFSET_CPU sizeof(struct lance_init_block) +#define BUF_OFFSET_LNC (sizeof(struct lance_init_block)>>1) + +#define libdesc_offset(rt, elem) \ +((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem]))))) + +/* + * This works *only* for the ring descriptors + */ +#define LANCE_ADDR(x) (PHYSADDR(x) >> 1) + +struct lance_private { + char *name; + volatile struct lance_regs *ll; + volatile struct lance_init_block *init_block; + volatile unsigned long *dma_ptr_reg; + + int rx_new, tx_new; + int rx_old, tx_old; + + struct net_device_stats stats; + + unsigned short busmaster_regval; + + struct device *dev; /* Backpointer */ + struct lance_private *next_module; + + /* Pointers to the ring buffers as seen from the CPU */ + char *rx_buf_ptr_cpu[RX_RING_SIZE]; + char *tx_buf_ptr_cpu[TX_RING_SIZE]; + + /* Pointers to the ring buffers as seen from the LANCE */ + char *rx_buf_ptr_lnc[RX_RING_SIZE]; + char *tx_buf_ptr_lnc[TX_RING_SIZE]; +}; + +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ + lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\ + lp->tx_old - lp->tx_new-1) + +/* The lance control ports are at an absolute address, machine and tc-slot + * dependant. + * DECstations do only 32-bit access and the LANCE uses 16 bit addresses, + * so we have to give the structure an extra member making rap pointing + * at the right address + */ +struct lance_regs { + volatile unsigned short rdp; /* register data port */ + unsigned short pad; + volatile unsigned short rap; /* register address port */ +}; + +int dec_lance_debug = 2; + +/* + #ifdef MODULE + static struct lance_private *root_lance_dev = NULL; + #endif + */ + +static inline void writereg(volatile unsigned short *regptr, short value) +{ + *regptr = value; + wbflush(); +} + +/* Load the CSR registers */ +static void load_csrs(struct lance_private *lp) +{ + volatile struct lance_regs *ll = lp->ll; + int leptr; + + /* The address space as seen from the LANCE + * begins at address 0. HK + */ + leptr = 0; + + writereg(&ll->rap, LE_CSR1); + writereg(&ll->rdp, (leptr & 0xFFFF)); + writereg(&ll->rap, LE_CSR2); + writereg(&ll->rdp, leptr >> 16); + writereg(&ll->rap, LE_CSR3); + writereg(&ll->rdp, lp->busmaster_regval); + + /* Point back to csr0 */ + writereg(&ll->rap, LE_CSR0); +} + +/* + * Our specialized copy routines + * + */ +void cp_to_buf(void *to, const void *from, __kernel_size_t len) +{ + unsigned short *tp, *fp, clen; + unsigned char *rtp, *rfp; + + if (type == PMAX_LANCE) { + clen = len >> 1; + tp = (unsigned short *) to; + fp = (unsigned short *) from; + + while (clen--) { + *tp++ = *fp++; + tp++; + } + + clen = len & 1; + rtp = (unsigned char *) tp; + rfp = (unsigned char *) fp; + while (clen--) { + *rtp++ = *rfp++; + } + } else { + /* + * copy 16 Byte chunks + */ + clen = len >> 4; + tp = (unsigned short *) to; + fp = (unsigned short *) from; + while (clen--) { + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + tp += 8; + } + + /* + * do the rest, if any. + */ + clen = len & 15; + rtp = (unsigned char *) tp; + rfp = (unsigned char *) fp; + while (clen--) { + *rtp++ = *rfp++; + } + } + + wbflush(); +} + +void cp_from_buf(void *to, unsigned char *from, int len) +{ + unsigned short *tp, *fp, clen; + unsigned char *rtp, *rfp; + + if (type == PMAX_LANCE) { + clen = len >> 1; + tp = (unsigned short *) to; + fp = (unsigned short *) from; + while (clen--) { + *tp++ = *fp++; + fp++; + } + + clen = len & 1; + + rtp = (unsigned char *) tp; + rfp = (unsigned char *) fp; + + while (clen--) { + *rtp++ = *rfp++; + } + } else { + + /* + * copy 16 Byte chunks + */ + clen = len >> 4; + tp = (unsigned short *) to; + fp = (unsigned short *) from; + while (clen--) { + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + *tp++ = *fp++; + fp += 8; + } + + /* + * do the rest, if any. + */ + clen = len & 15; + rtp = (unsigned char *) tp; + rfp = (unsigned char *) fp; + while (clen--) { + *rtp++ = *rfp++; + } + + + } + +} + +/* Setup the Lance Rx and Tx rings */ +/* Sets dev->tbusy */ +static void lance_init_ring(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_init_block *ib; + int leptr; + int i; + + ib = (struct lance_init_block *) (dev->mem_start); + + /* Lock out other processes while setting up hardware */ + dev->tbusy = 1; + lp->rx_new = lp->tx_new = 0; + lp->rx_old = lp->tx_old = 0; + + ib->mode = 0; + + /* Copy the ethernet address to the lance init block. + * XXX bit 0 of the physical address registers has to be zero + */ + ib->phys_addr[0] = dev->dev_addr[0]; + ib->phys_addr[1] = dev->dev_addr[1]; + ib->phys_addr[4] = dev->dev_addr[2]; + ib->phys_addr[5] = dev->dev_addr[3]; + ib->phys_addr[8] = dev->dev_addr[4]; + ib->phys_addr[9] = dev->dev_addr[5]; + /* Setup the initialization block */ + + /* Setup rx descriptor pointer */ + leptr = LANCE_ADDR(libdesc_offset(brx_ring, 0)); + ib->rx_len = (LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16); + ib->rx_ptr = leptr; + if (ZERO) + printk("RX ptr: %8.8x(%8.8x)\n", leptr, libdesc_offset(brx_ring, 0)); + + /* Setup tx descriptor pointer */ + leptr = LANCE_ADDR(libdesc_offset(btx_ring, 0)); + ib->tx_len = (LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16); + ib->tx_ptr = leptr; + if (ZERO) + printk("TX ptr: %8.8x(%8.8x)\n", leptr, libdesc_offset(btx_ring, 0)); + + /* Clear the multicast filter */ + ib->filter[0] = 0; + ib->filter[2] = 0; + ib->filter[4] = 0; + ib->filter[6] = 0; + if (ZERO) + printk("TX rings:\n"); + + /* Setup the Tx ring entries */ + for (i = 0; i < TX_RING_SIZE; i++) { + leptr = (int) lp->tx_buf_ptr_lnc[i]; + ib->btx_ring[i].tmd0 = leptr; + ib->btx_ring[i].tmd1_hadr = leptr >> 16; + ib->btx_ring[i].tmd1_bits = 0; + ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */ + ib->btx_ring[i].misc = 0; + if (i < 3 && ZERO) + printk("%d: 0x%8.8x(0x%8.8x)\n", i, leptr, (int) lp->tx_buf_ptr_cpu[i]); + } + + /* Setup the Rx ring entries */ + if (ZERO) + printk("RX rings:\n"); + for (i = 0; i < RX_RING_SIZE; i++) { + leptr = (int) lp->rx_buf_ptr_lnc[i]; + ib->brx_ring[i].rmd0 = leptr; + ib->brx_ring[i].rmd1_hadr = leptr >> 16; + ib->brx_ring[i].rmd1_bits = LE_R1_OWN; + ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000; + ib->brx_ring[i].mblength = 0; + if (i < 3 && ZERO) + printk("%d: 0x%8.8x(0x%8.8x)\n", i, leptr, (int) lp->rx_buf_ptr_cpu[i]); + } + wbflush(); +} + +static int init_restart_lance(struct lance_private *lp) +{ + volatile struct lance_regs *ll = lp->ll; + int i; + + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_INIT); + + /* Wait for the lance to complete initialization */ + for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) { + udelay(10); + } + if ((i == 100) || (ll->rdp & LE_C0_ERR)) { + printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp); + return -1; + } + if ((ll->rdp & LE_C0_ERR)) { + printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp); + return -1; + } + writereg(&ll->rdp, LE_C0_IDON); + writereg(&ll->rdp, LE_C0_STRT); + writereg(&ll->rdp, LE_C0_INEA); + + return 0; +} + +static int lance_rx(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_init_block *ib; + volatile struct lance_rx_desc *rd = 0; + unsigned char bits; + int len = 0; + struct sk_buff *skb = 0; + ib = (struct lance_init_block *) (dev->mem_start); + +#ifdef TEST_HITS + int i; + + printk("["); + for (i = 0; i < RX_RING_SIZE; i++) { + if (i == lp->rx_new) + printk("%s", + ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "_" : "X"); + else + printk("%s", + ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "." : "1"); + } + printk("]"); +#endif + + for (rd = &ib->brx_ring[lp->rx_new]; + !((bits = rd->rmd1_bits) & LE_R1_OWN); + rd = &ib->brx_ring[lp->rx_new]) { + + /* We got an incomplete frame? */ + if ((bits & LE_R1_POK) != LE_R1_POK) { + lp->stats.rx_over_errors++; + lp->stats.rx_errors++; + } else if (bits & LE_R1_ERR) { + /* Count only the end frame as a rx error, + * not the beginning + */ + if (bits & LE_R1_BUF) + lp->stats.rx_fifo_errors++; + if (bits & LE_R1_CRC) + lp->stats.rx_crc_errors++; + if (bits & LE_R1_OFL) + lp->stats.rx_over_errors++; + if (bits & LE_R1_FRA) + lp->stats.rx_frame_errors++; + if (bits & LE_R1_EOP) + lp->stats.rx_errors++; + } else { + len = (rd->mblength & 0xfff) - 4; + skb = dev_alloc_skb(len + 2); + + if (skb == 0) { + printk("%s: Memory squeeze, deferring packet.\n", + dev->name); + lp->stats.rx_dropped++; + rd->mblength = 0; + rd->rmd1_bits = LE_R1_OWN; + lp->rx_new = (lp->rx_new + 1) & RX_RING_MOD_MASK; + return 0; + } + lp->stats.rx_bytes += len; + + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align */ + skb_put(skb, len); /* make room */ + cp_from_buf(skb->data, + (char *) lp->rx_buf_ptr_cpu[lp->rx_new], + len); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + lp->stats.rx_packets++; + } + + /* Return the packet to the pool */ + rd->mblength = 0; + rd->length = -RX_BUFF_SIZE | 0xf000; + rd->rmd1_bits = LE_R1_OWN; + lp->rx_new = (lp->rx_new + 1) & RX_RING_MOD_MASK; + } + return 0; +} + +static int lance_tx(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_init_block *ib; + volatile struct lance_regs *ll = lp->ll; + volatile struct lance_tx_desc *td; + int i, j; + int status; + ib = (struct lance_init_block *) (dev->mem_start); + j = lp->tx_old; + + for (i = j; i != lp->tx_new; i = j) { + td = &ib->btx_ring[i]; + /* If we hit a packet not owned by us, stop */ + if (td->tmd1_bits & LE_T1_OWN) + break; + + if (td->tmd1_bits & LE_T1_ERR) { + status = td->misc; + + lp->stats.tx_errors++; + if (status & LE_T3_RTY) + lp->stats.tx_aborted_errors++; + if (status & LE_T3_LCOL) + lp->stats.tx_window_errors++; + + if (status & LE_T3_CLOS) { + lp->stats.tx_carrier_errors++; + printk("%s: Carrier Lost", dev->name); + /* Stop the lance */ + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + lance_init_ring(dev); + load_csrs(lp); + init_restart_lance(lp); + return 0; + } + /* Buffer errors and underflows turn off the + * transmitter, restart the adapter. + */ + if (status & (LE_T3_BUF | LE_T3_UFL)) { + lp->stats.tx_fifo_errors++; + + printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n", + dev->name); + /* Stop the lance */ + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + lance_init_ring(dev); + load_csrs(lp); + init_restart_lance(lp); + return 0; + } + } else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) { + /* + * So we don't count the packet more than once. + */ + td->tmd1_bits &= ~(LE_T1_POK); + + /* One collision before packet was sent. */ + if (td->tmd1_bits & LE_T1_EONE) + lp->stats.collisions++; + + /* More than one collision, be optimistic. */ + if (td->tmd1_bits & LE_T1_EMORE) + lp->stats.collisions += 2; + + lp->stats.tx_packets++; + } + j = (j + 1) & TX_RING_MOD_MASK; + } + lp->tx_old = j; + return 0; +} + +static void lance_interrupt(const int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *) dev_id; + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_regs *ll = lp->ll; + int csr0; + + if (dev->interrupt) + printk("%s: again\n", dev->name); + + dev->interrupt = 1; + + writereg(&ll->rap, LE_CSR0); + csr0 = ll->rdp; + + /* Acknowledge all the interrupt sources ASAP */ + writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT)); + + if ((csr0 & LE_C0_ERR)) { + /* Clear the error condition */ + writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | + LE_C0_CERR | LE_C0_MERR); + } + if (csr0 & LE_C0_RINT) + lance_rx(dev); + + if (csr0 & LE_C0_TINT) + lance_tx(dev); + + if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { + dev->tbusy = 0; + mark_bh(NET_BH); + } + if (csr0 & LE_C0_BABL) + lp->stats.tx_errors++; + + if (csr0 & LE_C0_MISS) + lp->stats.rx_errors++; + + if (csr0 & LE_C0_MERR) { + volatile unsigned long int_stat = *(unsigned long *) (system_base + IOCTL + SIR); + + printk("%s: Memory error, status %04x", dev->name, csr0); + + if (int_stat & LANCE_DMA_MEMRDERR) { + printk("%s: DMA error\n", dev->name); + int_stat |= LANCE_DMA_MEMRDERR; + /* + * re-enable LANCE DMA + */ + *(unsigned long *) (system_base + IOCTL + SSR) |= (1 << 16); + wbflush(); + } + writereg(&ll->rdp, LE_C0_STOP); + + lance_init_ring(dev); + load_csrs(lp); + init_restart_lance(lp); + dev->tbusy = 0; + } + writereg(&ll->rdp, LE_C0_INEA); + writereg(&ll->rdp, LE_C0_INEA); + dev->interrupt = 0; +} + +struct device *last_dev = 0; + +static int lance_open(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_regs *ll = lp->ll; + int status = 0; + + last_dev = dev; + + /* Associate IRQ with lance_interrupt */ + if (request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { + printk("Lance: Can't get irq %d\n", dev->irq); + return -EAGAIN; + } + /* Stop the Lance */ + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + + lance_init_ring(dev); + load_csrs(lp); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + status = init_restart_lance(lp); + + /* + * if (!status) + * MOD_INC_USE_COUNT; + */ + + return status; +} + +static int lance_close(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_regs *ll = lp->ll; + + dev->start = 0; + dev->tbusy = 1; + + /* Stop the card */ + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + + free_irq(dev->irq, (void *) dev); + /* + MOD_DEC_USE_COUNT; + */ + return 0; +} + +static inline int lance_reset(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_regs *ll = lp->ll; + int status; + + /* Stop the lance */ + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + + lance_init_ring(dev); + load_csrs(lp); + dev->trans_start = jiffies; + dev->interrupt = 0; + dev->start = 1; + dev->tbusy = 0; + status = init_restart_lance(lp); +#ifdef DEBUG_DRIVER + printk("Lance restart=%d\n", status); +#endif + return status; +} + +static int lance_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_regs *ll = lp->ll; + volatile struct lance_init_block *ib; + unsigned long flags; + int entry, skblen, len; + int status = 0; + static int outs; + ib = (struct lance_init_block *) (dev->mem_start); + + /* Transmitter timeout, serious problems */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + + if (tickssofar < 100) { + status = -1; + } else { + printk("%s: transmit timed out, status %04x, reset\n", + dev->name, ll->rdp); + lance_reset(dev); + } + return status; + } + /* Block a timer-based transmit from overlapping. */ + if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + printk("Transmitter access conflict.\n"); + return -1; + } + skblen = skb->len; + save_and_cli(flags); + if (!TX_BUFFS_AVAIL) { + restore_flags(flags); + return -1; + } + len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; + + lp->stats.tx_bytes += len; + + entry = lp->tx_new & TX_RING_MOD_MASK; + ib->btx_ring[entry].length = (-len); + ib->btx_ring[entry].misc = 0; + + cp_to_buf((char *) lp->tx_buf_ptr_cpu[entry], skb->data, skblen); + + /* Clear the slack of the packet, do I need this? */ + /* For a firewall its a good idea - AC */ +/* + if (len != skblen) + memset ((char *) &ib->tx_buf [entry][skblen], 0, (len - skblen) << 1); + */ + + /* Now, give the packet to the lance */ + ib->btx_ring[entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN); + lp->tx_new = (lp->tx_new + 1) & TX_RING_MOD_MASK; + + outs++; + /* Kick the lance: transmit now */ + writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + + if (TX_BUFFS_AVAIL) + dev->tbusy = 0; + + restore_flags(flags); + return status; +} + +static struct net_device_stats *lance_get_stats(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + + return &lp->stats; +} + +static void lance_load_multicast(struct device *dev) +{ + volatile struct lance_init_block *ib = (struct lance_init_block *) (dev->mem_start); + volatile u16 *mcast_table = (u16 *) & ib->filter; + struct dev_mc_list *dmi = dev->mc_list; + char *addrs; + int i, j, bit, byte; + u32 crc, poly = CRC_POLYNOMIAL_BE; + + /* set all multicast bits */ + if (dev->flags & IFF_ALLMULTI) { + ib->filter[0] = 0xffff; + ib->filter[2] = 0xffff; + ib->filter[4] = 0xffff; + ib->filter[6] = 0xffff; + return; + } + /* clear the multicast filter */ + ib->filter[0] = 0; + ib->filter[2] = 0; + ib->filter[4] = 0; + ib->filter[6] = 0; + + /* Add addresses */ + for (i = 0; i < dev->mc_count; i++) { + addrs = dmi->dmi_addr; + dmi = dmi->next; + + /* multicast address? */ + if (!(*addrs & 1)) + continue; + + crc = 0xffffffff; + for (byte = 0; byte < 6; byte++) + for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { + int test; + + test = ((bit ^ crc) & 0x01); + crc >>= 1; + + if (test) { + crc = crc ^ poly; + } + } + + crc = crc >> 26; + mcast_table[crc >> 3] |= 1 << (crc & 0xf); + } + return; +} + +static void lance_set_multicast(struct device *dev) +{ + struct lance_private *lp = (struct lance_private *) dev->priv; + volatile struct lance_init_block *ib; + volatile struct lance_regs *ll = lp->ll; + + ib = (struct lance_init_block *) (dev->mem_start); + + while (dev->tbusy) + schedule(); + set_bit(0, (void *) &dev->tbusy); + while (lp->tx_old != lp->tx_new) + schedule(); + + writereg(&ll->rap, LE_CSR0); + writereg(&ll->rdp, LE_C0_STOP); + + lance_init_ring(dev); + + if (dev->flags & IFF_PROMISC) { + ib->mode |= LE_MO_PROM; + } else { + ib->mode &= ~LE_MO_PROM; + lance_load_multicast(dev); + } + load_csrs(lp); + init_restart_lance(lp); + dev->tbusy = 0; +} + +__initfunc(static int dec_lance_init(struct device *dev, const int type)) +{ + static unsigned version_printed = 0; + struct lance_private *lp; + volatile struct lance_regs *ll; + int i; + unsigned long esar_base; + unsigned char *esar; + +#ifndef CONFIG_TC + system_base = KN01_LANCE_BASE; +#else + int slot; +#endif + + if (dec_lance_debug && version_printed++ == 0) + printk(version); + + if (dev == NULL) { + dev = init_etherdev(0, sizeof(struct lance_private) + 8); + } else { + dev->priv = kmalloc(sizeof(struct lance_private) + 8, + GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct lance_private) + 8); + + } + + /* Make certain the data structures used by the LANCE are aligned. */ + dev->priv = (void *) (((unsigned long) dev->priv + 7) & ~7); + lp = (struct lance_private *) dev->priv; + + switch (type) { +#ifdef CONFIG_TC + case ASIC_LANCE: + dev->base_addr = system_base + LANCE; + + /* buffer space for the on-board LANCE shared memory */ + /* + * FIXME: ugly hack! + */ + dev->mem_start = KSEG1ADDR(0x0020000); + dev->mem_end = dev->mem_start + 0x00020000; + dev->irq = ETHER; + esar_base = system_base + ESAR; + + /* + * setup the pointer arrays, this sucks [tm] :-( + */ + for (i = 0; i < RX_RING_SIZE; i++) { + lp->rx_buf_ptr_cpu[i] = (char *) (dev->mem_start + BUF_OFFSET_CPU + + 2 * i * RX_BUFF_SIZE); + lp->rx_buf_ptr_lnc[i] = (char *) (BUF_OFFSET_LNC + + i * RX_BUFF_SIZE); + } + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_buf_ptr_cpu[i] = (char *) (dev->mem_start + BUF_OFFSET_CPU + + 2 * RX_RING_SIZE * RX_BUFF_SIZE + + 2 * i * TX_BUFF_SIZE); + lp->tx_buf_ptr_lnc[i] = (char *) (BUF_OFFSET_LNC + + RX_RING_SIZE * RX_BUFF_SIZE + + i * TX_BUFF_SIZE); + } + + /* + * setup and enable IOASIC LANCE DMA + */ + lp->dma_ptr_reg = (unsigned long *) (system_base + IOCTL + LANCE_DMA_P); + *(lp->dma_ptr_reg) = PHYSADDR(dev->mem_start) << 3; + *(unsigned long *) (system_base + IOCTL + SSR) |= (1 << 16); + wbflush(); + + break; + case PMAD_LANCE: + slot = search_tc_card("PMAD-AA"); + claim_tc_card(slot); + + dev->mem_start = get_tc_base_addr(slot); + dev->base_addr = dev->mem_start + 0x100000; + dev->irq = get_tc_irq_nr(slot); + esar_base = dev->mem_start + 0x1c0002; + break; +#endif + case PMAX_LANCE: + dev->irq = ETHER; + dev->base_addr = KN01_LANCE_BASE; + dev->mem_start = KN01_LANCE_BASE + 0x01000000; + esar_base = KN01_RTC_BASE + 1; + /* + * setup the pointer arrays, this sucks [tm] :-( + */ + for (i = 0; i < RX_RING_SIZE; i++) { + lp->rx_buf_ptr_cpu[i] = + (char *) (dev->mem_start + BUF_OFFSET_CPU + + 2 * i * RX_BUFF_SIZE); + + lp->rx_buf_ptr_lnc[i] = + (char *) (BUF_OFFSET_LNC + + i * RX_BUFF_SIZE); + + } + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_buf_ptr_cpu[i] = + (char *) (dev->mem_start + BUF_OFFSET_CPU + + 2 * RX_RING_SIZE * RX_BUFF_SIZE + + 2 * i * TX_BUFF_SIZE); + lp->tx_buf_ptr_lnc[i] = (char *) (BUF_OFFSET_LNC + + RX_RING_SIZE * RX_BUFF_SIZE + + i * TX_BUFF_SIZE); + + } + break; + default: + printk("declance_init called with unknown type\n"); + return -ENODEV; + break; + } + + ll = (struct lance_regs *) dev->base_addr; + esar = (unsigned char *) esar_base; + + /* prom checks */ + /* First, check for test pattern */ + if (esar[0x60] != 0xff && esar[0x64] != 0x00 && + esar[0x68] != 0x55 && esar[0x6c] != 0xaa) { + printk("Ethernet station address prom not found!\n"); + return -ENODEV; + } + /* Check the prom contents */ + for (i = 0; i < 8; i++) { + if (esar[i * 4] != esar[0x3c - i * 4] && + esar[i * 4] != esar[0x40 + i * 4] && + esar[0x3c - i * 4] != esar[0x40 + i * 4]) { + printk("Something is wrong with the ethernet " + "station address prom!\n"); + return -ENODEV; + } + } + + /* Copy the ethernet address to the device structure, later to the + * lance initialization block so the lance gets it every time it's + * (re)initialized. + */ + switch (type) { + case ASIC_LANCE: + printk("%s: IOASIC onboard LANCE, addr = ", dev->name); + break; + case PMAD_LANCE: + printk("%s: PMAD-AA, addr = ", dev->name); + break; + case PMAX_LANCE: + printk("%s: PMAX onboard LANCE, addr = ", dev->name); + break; + } + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = esar[i * 4]; + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ',' : ':'); + } + + printk(" irq = %d\n", dev->irq); + + /* Fill the dev fields */ + + dev->open = lance_open; + dev->stop = lance_close; + dev->hard_start_xmit = lance_start_xmit; + dev->get_stats = lance_get_stats; + dev->set_multicast_list = lance_set_multicast; + dev->dma = 0; + + /* lp->ll is the location of the registers for lance card */ + lp->ll = ll; + + lp->name = lancestr; + + /* busmaster_regval (CSR3) should be zero according to the PMAD-AA + * specification. + */ + lp->busmaster_regval = 0; + lp->dev = dev; + + ether_setup(dev); +/* + #ifdef MODULE + dev->ifindex = dev_new_index(); + lp->next_module = root_lance_dev; + root_lance_dev = lp; + #endif + */ + return 0; +} + + +/* Find all the lance cards on the system and initialize them */ +__initfunc(int dec_lance_probe(struct device *dev)) +{ + static int called = 0; + +#ifdef CONFIG_TC + int slot = -1; + + if (TURBOCHANNEL) { + if (IOASIC && !called) { + called = 1; + type = ASIC_LANCE; + } else { + if ((slot = search_tc_card("PMAD-AA")) >= 0) { + type = PMAD_LANCE; + } else { + return -ENODEV; + } + } + } else { + if (!called) { + called = 1; + type = PMAX_LANCE; + } else { + return -ENODEV; + } + } +#else + if (!called && !TURBOCHANNEL) { + called = 1; + type = PMAX_LANCE; + } else { + return -ENODEV; + } +#endif + + return dec_lance_init(dev, type); +} + +/* + #ifdef MODULE + + int + init_module(void) + { + root_lance_dev = NULL; + return dec_lance_probe(NULL); + } + + void + cleanup_module(void) + { + struct lance_private *lp; + + while (root_lance_dev) { + lp = root_lance_dev->next_module; + + unregister_netdev(root_lance_dev->dev); + kfree(root_lance_dev->dev); + root_lance_dev = lp; + } + } + + #endif -* MODULE */ diff --git a/drivers/scsi/dec_esp.c b/drivers/scsi/dec_esp.c new file mode 100644 index 000000000..b1da29c72 --- /dev/null +++ b/drivers/scsi/dec_esp.c @@ -0,0 +1,299 @@ +/* + * dec_esp.c: Driver for SCSI chips on IOASIC based TURBOchannel DECstations + * + * TURBOchannel changes by Harald Koerfgen + * + * based on jazz_esp.c: + * Copyright (C) 1997 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * jazz_esp is based on David S. Miller's ESP driver and cyber_esp + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/blk.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" +#include "dec_esp.h" + +#include <asm/irq.h> +#include <asm/jazz.h> +#include <asm/jazzdma.h> +#include <asm/dma.h> + +#include <asm/pgtable.h> + +#include <asm/dec/tc.h> +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic_addrs.h> + +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_dump_state(struct NCR_ESP *esp); +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_ints_off(struct NCR_ESP *esp); +static void dma_ints_on(struct NCR_ESP *esp); +static int dma_irq_p(struct NCR_ESP *esp); +static int dma_ports_p(struct NCR_ESP *esp); +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_advance_sg (Scsi_Cmnd *sp); +static void dma_led_off(struct NCR_ESP *); +static void dma_led_on(struct NCR_ESP *); + + +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are trasfered to the ESP chip + * via PIO. + */ + +/***************************************************************** Detection */ +int dec_esp_detect(Scsi_Host_Template *tpnt) +{ + struct NCR_ESP *esp; + struct ConfigDev *esp_dev; + int slot; + + if ((slot = search_tc_card("PMAZ-AA")) >= 0) { + claim_tc_card(slot); + esp_dev = 0; + esp = esp_allocate(tpnt, (void *) esp_dev); + + /* Do command transfer with programmed I/O */ + esp->do_pio_cmds = 1; + + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = 0; + esp->dma_drain = 0; + esp->dma_invalidate = 0; + esp->dma_irq_entry = 0; + esp->dma_irq_exit = 0; + esp->dma_poll = 0; + esp->dma_reset = 0; + esp->dma_led_off = 0; + esp->dma_led_on = 0; + + /* virtual DMA functions */ + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; + esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; + esp->dma_advance_sg = &dma_advance_sg; + + + /* SCSI chip speed */ + esp->cfreq = get_tc_speed(); + + /* + * we don't give the address of DMA channel, but the number + * of DMA channel, so we can use the jazz DMA functions + * + */ + esp->dregs = JAZZ_SCSI_DMA; + + /* ESP register base */ + esp->eregs = (struct ESP_regs *)get_tc_base_addr(slot); + + /* Set the command buffer */ + esp->esp_command = (volatile unsigned char *)cmd_buffer; + + /* get virtual dma address for command buffer */ + esp->esp_command_dvma = (volatile unsigned char *)cmd_buffer; /* vdma_alloc(PHYSADDR(cmd_buffer), sizeof (cmd_buffer)); */ + + esp->irq = get_tc_irq_nr(slot); + request_irq(esp->irq, esp_intr, SA_INTERRUPT, "NCR 53C94 SCSI", + NULL); + + /* + * FIXME, look if the scsi id is availabe from NVRAM + */ + esp->scsi_id = 7; + + /* Check for differential SCSI-bus */ + /* What is this stuff? */ + esp->diff = 0; + + esp_initialize(esp); + + printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use); + esps_running = esps_in_use; + return esps_in_use; + } + return 0; +} + +/************************************************************* DMA Functions */ +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) +{ + return fifo_count; +} + +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + /* + * maximum DMA size is 1MB + */ + unsigned long sz = sp->SCp.this_residual; + if(sz > 0x100000) + sz = 0x100000; + return sz; +} + +static void dma_dump_state(struct NCR_ESP *esp) +{ + + ESPLOG(("esp%d: dma -- enable <%08x> residue <%08x\n", + esp->esp_id, vdma_get_enable((int)esp->dregs), vdma_get_resdiue((int)esp->dregs))); +} + +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) +{ +/* + dma_cache_wback_inv ((unsigned long)phys_to_virt(vdma_log2phys(vaddress)), length); + vdma_disable ((int)esp->dregs); + vdma_set_mode ((int)esp->dregs, DMA_MODE_READ); + vdma_set_addr ((int)esp->dregs, vaddress); + vdma_set_count ((int)esp->dregs, length); + vdma_enable ((int)esp->dregs); +*/ +} + +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) +{ +/* + dma_cache_wback_inv ((unsigned long)phys_to_virt(vdma_log2phys(vaddress)), length); + vdma_disable ((int)esp->dregs); + vdma_set_mode ((int)esp->dregs, DMA_MODE_WRITE); + vdma_set_addr ((int)esp->dregs, vaddress); + vdma_set_count ((int)esp->dregs, length); + vdma_enable ((int)esp->dregs); +*/ +} + +static void dma_ints_off(struct NCR_ESP *esp) +{ + disable_irq(esp->irq); +} + +static void dma_ints_on(struct NCR_ESP *esp) +{ + enable_irq(esp->irq); +} + +static int dma_irq_p(struct NCR_ESP *esp) +{ + return (esp->eregs->esp_status & ESP_STAT_INTR); +} + +static int dma_ports_p(struct NCR_ESP *esp) +{ +/* + int enable = vdma_get_enable((int)esp->dregs); + + return (enable & R4030_CHNL_ENABLE); +*/ +} + +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) +{ + /* + * On the Sparc, DMA_ST_WRITE means "move data from device to memory" + * so when (write) is true, it actually means READ! + */ +/* + if(write){ + dma_init_read(esp, addr, count); + } else { + dma_init_write(esp, addr, count); + } +*/ +} + +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +/* + sp->SCp.have_data_in = vdma_alloc(PHYSADDR(sp->SCp.buffer), sp->SCp.this_residual); + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); +*/ +} + +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +/* + int sz = sp->SCp.buffers_residual; + struct mmu_sglist *sg = (struct mmu_sglist *) sp->SCp.buffer; + + while (sz >= 0) { + sg[sz].dvma_addr = vdma_alloc(PHYSADDR(sg[sz].addr), sg[sz].len); + sz--; + } + sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address); +*/ +} + +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +/* + vdma_free(sp->SCp.have_data_in); +*/ +} + +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +/* + int sz = sp->use_sg - 1; + struct mmu_sglist *sg = (struct mmu_sglist *)sp->buffer; + + while(sz >= 0) { + vdma_free(sg[sz].dvma_addr); + sz--; + } +*/ +} + +static void dma_advance_sg (Scsi_Cmnd *sp) +{ +/* + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address); +*/ +} + +#define JAZZ_HDC_LED 0xe000d100 /* FIXME, find correct address */ + +static void dma_led_off(struct NCR_ESP *esp) +{ +#if 0 + *(unsigned char *)JAZZ_HDC_LED = 0; +#endif +} + +static void dma_led_on(struct NCR_ESP *esp) +{ +#if 0 + *(unsigned char *)JAZZ_HDC_LED = 1; +#endif +} diff --git a/drivers/scsi/dec_esp.h b/drivers/scsi/dec_esp.h new file mode 100644 index 000000000..738b90356 --- /dev/null +++ b/drivers/scsi/dec_esp.h @@ -0,0 +1,42 @@ +/* dec_esp.h: Defines and structures for the JAZZ SCSI driver. + * + * Copyright (C) 1998 Harald Koerfgen + * + * based on jazz_esp.h: + * Copyright (C) 1997 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#ifndef DEC_ESP_H +#define DEC_ESP_H + +#define EREGS_PAD(n) unchar n[3]; + +#include "NCR53C9x.h" + + +extern int dec_esp_detect(struct SHT *); +extern const char *esp_info(struct Scsi_Host *); +extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int esp_command(Scsi_Cmnd *); +extern int esp_abort(Scsi_Cmnd *); +extern int esp_reset(Scsi_Cmnd *, unsigned int); +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); + +#define SCSI_DEC_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "PMAZ-AA", \ + detect: dec_esp_detect, \ + info: esp_info, \ + command: esp_command, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, } + +#endif /* JAZZ_DEC_H */ diff --git a/drivers/tc/Makefile b/drivers/tc/Makefile new file mode 100644 index 000000000..b1f4c4644 --- /dev/null +++ b/drivers/tc/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +SUB_DIRS := +MOD_SUB_DIRS := +ALL_SUB_DIRS := + +L_TARGET := tc.a +L_OBJS := tc.o + +ifdef CONFIG_ZS +L_OBJS += zs.o +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/tc/tc.c b/drivers/tc/tc.c new file mode 100644 index 000000000..36183ff9c --- /dev/null +++ b/drivers/tc/tc.c @@ -0,0 +1,230 @@ +/* $Id: $ + * tc-init: We assume the TURBOchannel to be up and running so + * just probe for Modules and fill in the global data structure + * tc_bus. + * + * 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. + * + * Copyright (c) Harald Koerfgen, 1998 + * + */ + +#include <linux/string.h> +#include <asm/init.h> +#include <asm/addrspace.h> +#include <asm/errno.h> +#include <asm/dec/machtype.h> +#include <asm/dec/tcinfo.h> +#include <asm/dec/tcmodule.h> +#include <asm/dec/interrupts.h> + +#include <asm/ptrace.h> +#include <linux/kernel.h> + +#define TC_DEBUG + +slot_info tc_bus[MAX_SLOT]; +static int max_tcslot = 0; +static tcinfo *info = (tcinfo *)0; + +unsigned long system_base = 0; + +extern void (*dbe_board_handler)(struct pt_regs *regs); +extern unsigned long *(*rex_slot_address)(int); +extern void *(*rex_gettcinfo)(void); + +/* + * Interface to the world. Read comment in include/asm-mips/tc.h. + */ + +int search_tc_card(char *name) +{ + int slot; + slot_info *sip; + + for (slot = 0; slot <= max_tcslot; slot++) { + sip = &tc_bus[slot]; + if ((sip->flags & FREE) && (strncmp(sip->name, name, strlen(name)) == 0)) { + return slot; + } + } + + return -ENODEV; +} + +void claim_tc_card(int slot) +{ + if (tc_bus[slot].flags & IN_USE) { + printk("claim_tc_card: attempting to claim a card already in use\n"); + return; + } + tc_bus[slot].flags &= ~FREE; + tc_bus[slot].flags |= IN_USE; +} + +void release_tc_card(int slot) +{ + if (tc_bus[slot].flags & FREE) { + printk("release_tc_card: attempting to release a card already free\n"); + return; + } + tc_bus[slot].flags &= ~IN_USE; + tc_bus[slot].flags |= FREE; +} + +unsigned long get_tc_base_addr(int slot) +{ + return tc_bus[slot].base_addr; +} + +unsigned long get_tc_irq_nr(int slot) +{ + return tc_bus[slot].interrupt; +} + +unsigned long get_tc_speed(void) +{ + return 100000 * (10000 / (unsigned long)info->clk_period); +} + +/* + * Probing for TURBOchannel modules + */ +__initfunc(static void my_dbe_handler(struct pt_regs *regs)) +{ + regs->cp0_epc += 4; +} + +__initfunc(static void tc_probe(unsigned long startaddr, unsigned long size, int max_slot)) +{ + int i, slot; + long offset; + unsigned char *module; + void (*old_be_handler)(struct pt_regs *regs); + + /* Install our exception handler temporarily */ + + old_be_handler = dbe_board_handler; + dbe_board_handler = my_dbe_handler; + for (slot = 0; slot <= max_slot; slot++) { + module = (char *)(startaddr + slot * size); + offset = -1; + if (module[OLDCARD + PATTERN0] == 0x55 && module[OLDCARD + PATTERN1] == 0x00 + && module[OLDCARD + PATTERN2] == 0xaa && module[OLDCARD + PATTERN3] == 0xff) + offset = OLDCARD; + if (module[PATTERN0] == 0x55 && module[PATTERN1] == 0x00 + && module[PATTERN2] == 0xaa && module[PATTERN3] == 0xff) + offset = 0; + + if (offset != -1) { + tc_bus[slot].base_addr = (unsigned long)module; + for(i = 0; i < 8; i++) { + tc_bus[slot].firmware[i] = module[FIRM_VER + offset + 4 * i]; + tc_bus[slot].vendor[i] = module[VENDOR + offset + 4 * i]; + tc_bus[slot].name[i] = module[MODULE + offset + 4 * i]; + } + tc_bus[slot].firmware[8] = 0; + tc_bus[slot].vendor[8] = 0; + tc_bus[slot].name[8] = 0; + /* + * Looks unneccesary, but we may change + * TC? in the future + */ + switch (slot) { + case 0: + tc_bus[slot].interrupt = TC0; + case 1: + tc_bus[slot].interrupt = TC1; + case 2: + tc_bus[slot].interrupt = TC2; + /* + * Yuck! DS5000/200 onboard devices + */ + case 5: + tc_bus[slot].interrupt = SCSI_INT; + case 6: + tc_bus[slot].interrupt = ETHER; + default: + tc_bus[slot].interrupt = -1; + } + } + } + + dbe_board_handler = old_be_handler; +} + +/* + * the main entry + */ +__initfunc(void tc_init(void)) +{ + int tc_clock; + int i; + unsigned long slot0addr; + unsigned long slot_size; + + if (!TURBOCHANNEL) + return; + + for (i = 0; i < MAX_SLOT; i++) { + tc_bus[i].base_addr = 0; + tc_bus[i].name[0] = 0; + tc_bus[i].vendor[0] = 0; + tc_bus[i].firmware[0] = 0; + tc_bus[i].interrupt = -1; + tc_bus[i].flags = FREE; + } + + info = (tcinfo *) rex_gettcinfo(); + slot0addr = (unsigned long)KSEG1ADDR(rex_slot_address(0)); + + switch (mips_machtype) { + case MACH_DS5000_200: + max_tcslot = 6; + break; + case MACH_DS5000_1XX: + case MACH_DS5000_2X0: + max_tcslot = 2; + break; + case MACH_DS5000_XX: + default: + max_tcslot = 1; + break; + } + + tc_clock = 10000 / info->clk_period; + + if (TURBOCHANNEL && info->slot_size && slot0addr) { + printk("TURBOchannel rev. %1d at %2d.%1d MHz ", info->revision, + tc_clock / 10, tc_clock % 10); + printk("(%sparity)\n", info->parity ? "" : "no "); + + slot_size = info->slot_size << 20; + + tc_probe(slot0addr, slot_size, max_tcslot); + + /* + * All TURBOchannel DECstations have the onboard devices + * where the (max_tcslot + 1 or 2 on DS5k/xx) Option Module + * would be. + */ + if(mips_machtype == MACH_DS5000_XX) + i = 2; + else + i = 1; + + system_base = slot0addr + slot_size * (max_tcslot + i); + +#ifdef TC_DEBUG + for (i = 0; i <= max_tcslot; i++) + if (tc_bus[i].base_addr) { + printk(" slot %d: ", i); + printk("%s %s %s\n", tc_bus[i].vendor, + tc_bus[i].name, tc_bus[i].firmware); + } +#endif + } + +} diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c new file mode 100644 index 000000000..3dbb837a2 --- /dev/null +++ b/drivers/tc/zs.c @@ -0,0 +1,2108 @@ +/* + * decserial.c: Serial port driver for IOASIC DECsatations. + * + * Derived from drivers/macintosh/macserial.c by Harald Koerfgen. + * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. + * + * DECstation changes + * Copyright (C) 1998 Harald Koerfgen (Harald.Koerfgen@home.ivm.de) + * + * For the rest of the code the original Copyright applies: + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * Keyboard and mouse are not supported right now. If you want to change this, + * you might want to have a look at drivers/sbus/char/sunserial.c to see + * how this might be done. HK + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#ifdef CONFIG_SERIAL_CONSOLE +#include <linux/console.h> +#endif + +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/uaccess.h> +#include <asm/wbflush.h> +#include <asm/dec/interrupts.h> +#include <asm/dec/machtype.h> +#include <asm/dec/tc.h> +#include <asm/dec/ioasic_addrs.h> +#ifdef CONFIG_KGDB +#include <asm/kgdb.h> +#endif + +#include "zs.h" + + +/* + * It would be nice to dynamically allocate everything that + * depends on NUM_SERIAL, so we could support any number of + * Z8530s, but for now... + */ +#define NUM_SERIAL 2 /* Max number of ZS chips supported */ +#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */ + +#define RECOVERY_DELAY udelay(2) + +struct dec_zschannel zs_channels[NUM_CHANNELS]; + +struct dec_serial zs_soft[NUM_CHANNELS]; +int zs_channels_found; +struct dec_serial *zs_chain; /* list of all channels */ + +struct tty_struct zs_ttys[NUM_CHANNELS]; + +#ifdef CONFIG_SERIAL_CONSOLE +static struct console sercons; +#endif + +#ifdef CONFIG_KGDB +struct dec_zschannel *zs_kgdbchan; +static unsigned char scc_inittab[] = { + 9, 0x80, /* reset A side (CHRA) */ + 13, 0, /* set baud rate divisor */ + 12, 1, + 14, 1, /* baud rate gen enable, src=rtxc (BRENABL) */ + 11, 0x50, /* clocks = br gen (RCBR | TCBR) */ + 5, 0x6a, /* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */ + 4, 0x44, /* x16 clock, 1 stop (SB1 | X16CLK)*/ + 3, 0xc1, /* rx enable, 8 bits (RxENABLE | Rx8)*/ +}; +#endif + +static unsigned char zs_init_regs[16] __initdata = { + 0, /* write 0 */ + 0, /* write 1 */ + 0xf0, /* write 2 */ + (Rx8), /* write 3 */ + (X16CLK | SB1), /* write 4 */ + (Tx8), /* write 5 */ + 0, 0, 0, /* write 6, 7, 8 */ + (VIS), /* write 9 */ + (NRZ), /* write 10 */ + (TCBR | RCBR), /* write 11 */ + 0, 0, /* BRG time constant, write 12 + 13 */ + (BRSRC | BRENABL), /* write 14 */ + 0 /* write 15 */ +}; + +#define ZS_CLOCK 7372800 /* Z8530 RTxC input clock rate */ + +DECLARE_TASK_QUEUE(tq_zs_serial); + +struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * Debugging. + */ +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_PARANOIA_CHECK + +#define RS_STROBE_TIME 10 +#define RS_ISR_PASS_LIMIT 256 + +#define _INLINE_ inline + +static void probe_sccs(void); +static void change_speed(struct dec_serial *info); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +static struct tty_struct *serial_table[NUM_CHANNELS]; +static struct termios *serial_termios[NUM_CHANNELS]; +static struct termios *serial_termios_locked[NUM_CHANNELS]; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char tmp_buf[4096]; /* This is cheating */ +static struct semaphore tmp_buf_sem = MUTEX; + +static inline int serial_paranoia_check(struct dec_serial *info, + dev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%d, %d) in %s\n"; + static const char *badinfo = + "Warning: null mac_serial for (%d, %d) in %s\n"; + + if (!info) { + printk(badinfo, MAJOR(device), MINOR(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, MAJOR(device), MINOR(device), routine); + return 1; + } +#endif + return 0; +} + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 0, 0 }; + +/* + * Reading and writing Z8530 registers. + */ +static inline unsigned char read_zsreg(struct dec_zschannel *channel, + unsigned char reg) +{ + unsigned char retval; + + if (reg != 0) { + *channel->control = reg & 0xf; + wbflush(); RECOVERY_DELAY; + } + retval = *channel->control; + RECOVERY_DELAY; + return retval; +} + +static inline void write_zsreg(struct dec_zschannel *channel, + unsigned char reg, unsigned char value) +{ + if (reg != 0) { + *channel->control = reg & 0xf; + wbflush(); RECOVERY_DELAY; + } + *channel->control = value; + wbflush(); RECOVERY_DELAY; + return; +} + +static inline unsigned char read_zsdata(struct dec_zschannel *channel) +{ + unsigned char retval; + + retval = *channel->data; + RECOVERY_DELAY; + return retval; +} + +static inline void write_zsdata(struct dec_zschannel *channel, + unsigned char value) +{ + *channel->data = value; + wbflush(); RECOVERY_DELAY; + return; +} + +static inline void load_zsregs(struct dec_zschannel *channel, + unsigned char *regs) +{ +/* ZS_CLEARERR(channel); + ZS_CLEARFIFO(channel); */ + /* Load 'em up */ + write_zsreg(channel, R4, regs[R4]); + write_zsreg(channel, R3, regs[R3] & ~RxENABLE); + write_zsreg(channel, R5, regs[R5] & ~TxENAB); + write_zsreg(channel, R9, regs[R9]); + write_zsreg(channel, R1, regs[R1]); + write_zsreg(channel, R2, regs[R2]); + write_zsreg(channel, R10, regs[R10]); + write_zsreg(channel, R11, regs[R11]); + write_zsreg(channel, R12, regs[R12]); + write_zsreg(channel, R13, regs[R13]); + write_zsreg(channel, R14, regs[R14]); + write_zsreg(channel, R15, regs[R15]); + write_zsreg(channel, R3, regs[R3]); + write_zsreg(channel, R5, regs[R5]); + return; +} + +/* Sets or clears DTR/RTS on the requested line */ +static inline void zs_rtsdtr(struct dec_serial *ss, int set) +{ + if (ss->zs_channel != ss->zs_chan_a) { + if (set) + ss->zs_chan_a->curregs[5] |= (RTS | DTR); + else + ss->zs_chan_a->curregs[5] &= ~(RTS | DTR); + write_zsreg(ss->zs_chan_a, 5, ss->zs_chan_a->curregs[5]); + } + return; +} + +/* Utility routines for the Zilog */ +static inline int get_zsbaud(struct dec_serial *ss) +{ + struct dec_zschannel *channel = ss->zs_channel; + int brg; + + /* The baud rate is split up between two 8-bit registers in + * what is termed 'BRG time constant' format in my docs for + * the chip, it is a function of the clk rate the chip is + * receiving which happens to be constant. + */ + brg = (read_zsreg(channel, 13) << 8); + brg |= read_zsreg(channel, 12); + return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor))); +} + +/* On receive, this clears errors and the receiver interrupts */ +static inline void rs_recv_clear(struct dec_zschannel *zsc) +{ + write_zsreg(zsc, 0, ERR_RES); + write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */ +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void rs_sched_event(struct dec_serial *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_zs_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct dec_serial *info, + struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch, stat, flag; + + while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) { + + stat = read_zsreg(info->zs_channel, R1); + ch = read_zsdata(info->zs_channel); + +#ifdef CONFIG_KGDB + if (info->kgdb_channel) { + if (ch == 0x03 || ch == '$') + breakpoint(); + if (stat & (Rx_OVR|FRM_ERR|PAR_ERR)) + write_zsreg(info->zs_channel, 0, ERR_RES); + return; + } +#endif + if (!tty) + continue; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + static int flip_buf_ovf; + ++flip_buf_ovf; + continue; + } + tty->flip.count++; + { + static int flip_max_cnt; + if (flip_max_cnt < tty->flip.count) + flip_max_cnt = tty->flip.count; + } + if (stat & Rx_OVR) { + flag = TTY_OVERRUN; + } else if (stat & FRM_ERR) { + flag = TTY_FRAME; + } else if (stat & PAR_ERR) { + flag = TTY_PARITY; + } else + flag = 0; + if (flag) + /* reset the error indication */ + write_zsreg(info->zs_channel, 0, ERR_RES); + *tty->flip.flag_buf_ptr++ = flag; + *tty->flip.char_buf_ptr++ = ch; + } + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct dec_serial *info) +{ + if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0) + return; + info->tx_active = 0; + + if (info->x_char) { + /* Send next char */ + write_zsdata(info->zs_channel, info->x_char); + info->x_char = 0; + info->tx_active = 1; + return; + } + + if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped) { + write_zsreg(info->zs_channel, 0, RES_Tx_P); + return; + } + /* Send char */ + write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + info->tx_active = 1; + + if (info->xmit_cnt < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); +} + +static _INLINE_ void status_handle(struct dec_serial *info) +{ + unsigned char status; + + /* Get status from Read Register 0 */ + status = read_zsreg(info->zs_channel, 0); + + /* FIXEM: Check for DCD transitions */ + if (((status ^ info->read_reg_zero) & DCD) != 0 + && info->tty && !C_CLOCAL(info->tty)) { + if (status & DCD) { + wake_up_interruptible(&info->open_wait); + } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) { + if (info->tty) + tty_hangup(info->tty); + } + } + + /* Check for CTS transitions */ + if (info->tty && C_CRTSCTS(info->tty)) { + /* + * For some reason, on the Power Macintosh, + * it seems that the CTS bit is 1 when CTS is + * *negated* and 0 when it is asserted. + * The DCD bit doesn't seem to be inverted + * like this. + */ + if ((status & CTS) != 0) { + if (info->tx_stopped) { + info->tx_stopped = 0; + if (!info->tx_active) + transmit_chars(info); + } + } else { + info->tx_stopped = 1; + } + } + + /* Clear status condition... */ + write_zsreg(info->zs_channel, 0, RES_EXT_INT); + info->read_reg_zero = status; +} + +/* + * This is the serial driver's generic interrupt routine + */ +void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct dec_serial *info = (struct dec_serial *) dev_id; + unsigned char zs_intreg; + int shift; + + /* NOTE: The read register 3, which holds the irq status, + * does so for both channels on each chip. Although + * the status value itself must be read from the A + * channel and is only valid when read from channel A. + * Yes... broken hardware... + */ +#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) + + if (info->zs_chan_a == info->zs_channel) + shift = 3; /* Channel A */ + else + shift = 0; /* Channel B */ + + for (;;) { + zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; + if ((zs_intreg & CHAN_IRQMASK) == 0) + break; + + if (zs_intreg & CHBRxIP) { + receive_chars(info, regs); + } + if (zs_intreg & CHBTxIP) { + transmit_chars(info); + } + if (zs_intreg & CHBEXT) { + status_handle(info); + } + } +} + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + +#if 1 + save_flags(flags); cli(); + if (info->zs_channel->curregs[5] & TxENAB) { + info->zs_channel->curregs[5] &= ~TxENAB; + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + } + restore_flags(flags); +#endif +} + +static void rs_start(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_start")) + return; + + save_flags(flags); cli(); +#if 1 + if (info->xmit_cnt && info->xmit_buf && !(info->zs_channel->curregs[5] & TxENAB)) { + info->zs_channel->curregs[5] |= TxENAB; + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + } +#else + if (info->xmit_cnt && info->xmit_buf && !info->tx_active) { + transmit_chars(info); + } +#endif + restore_flags(flags); +} + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_zs_serial); +} + +static void do_softint(void *private_) +{ + struct dec_serial *info = (struct dec_serial *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +static void rs_timer(void) +{ +} + +static int startup(struct dec_serial * info) +{ + unsigned long flags; + + if (info->flags & ZILOG_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!info->xmit_buf) + return -ENOMEM; + } + + save_flags(flags); cli(); + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (irq %d)...", info->line, info->irq); +#endif + + /* + * Clear the receive FIFO. + */ + ZS_CLEARFIFO(info->zs_channel); + info->xmit_fifo_size = 1; + + /* + * Clear the interrupt registers. + */ + write_zsreg(info->zs_channel, 0, ERR_RES); + write_zsreg(info->zs_channel, 0, RES_H_IUS); + + /* + * Turn on RTS and DTR. + */ + zs_rtsdtr(info, 1); + + /* + * Finally, enable sequencing and interrupts + */ + info->zs_channel->curregs[1] = (info->zs_channel->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + info->zs_channel->curregs[3] |= (RxENABLE | Rx8); + info->zs_channel->curregs[5] |= (TxENAB | Tx8); + info->zs_channel->curregs[15] |= (DCDIE | CTSIE | TxUIE | BRKIE); + info->zs_channel->curregs[9] |= (VIS | MIE); + write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]); + write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + write_zsreg(info->zs_channel, 15, info->zs_channel->curregs[15]); + write_zsreg(info->zs_channel, 9, info->zs_channel->curregs[9]); + + /* + * And clear the interrupt registers again for luck. + */ + write_zsreg(info->zs_channel, 0, ERR_RES); + write_zsreg(info->zs_channel, 0, RES_H_IUS); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * Set the speed of the serial port + */ + change_speed(info); + + /* Save the current value of RR0 */ + info->read_reg_zero = read_zsreg(info->zs_channel, 0); + + info->flags |= ZILOG_INITIALIZED; + restore_flags(flags); + return 0; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct dec_serial * info) +{ + unsigned long flags; + + if (!(info->flags & ZILOG_INITIALIZED)) + return; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + info->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + info->zs_channel->curregs[1] = 0; + write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]); /* no interrupts */ + + info->zs_channel->curregs[3] &= ~RxENABLE; + write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); + + info->zs_channel->curregs[5] &= ~TxENAB; + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + if (!info->tty || C_HUPCL(info->tty)) { + info->zs_chan_a->curregs[5] &= ~(DTR | RTS); + write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); + } + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ZILOG_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct dec_serial *info) +{ + unsigned short port; + unsigned cflag; + int i; + int brg; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!(port = info->port)) + return; + i = cflag & CBAUD; + + save_flags(flags); cli(); + info->zs_baud = baud_table[i]; + info->clk_divisor = 16; + + switch (info->zs_baud) { + default: + info->zs_channel->curregs[4] = X16CLK; + brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); + info->zs_channel->curregs[12] = (brg & 255); + info->zs_channel->curregs[13] = ((brg >> 8) & 255); + } + + /* byte size and parity */ + info->zs_channel->curregs[3] &= ~RxNBITS_MASK; + info->zs_channel->curregs[5] &= ~TxNBITS_MASK; + switch (cflag & CSIZE) { + case CS5: + info->zs_channel->curregs[3] |= Rx5; + info->zs_channel->curregs[5] |= Tx5; + break; + case CS6: + info->zs_channel->curregs[3] |= Rx6; + info->zs_channel->curregs[5] |= Tx6; + break; + case CS7: + info->zs_channel->curregs[3] |= Rx7; + info->zs_channel->curregs[5] |= Tx7; + break; + case CS8: + default: /* defaults to 8 bits */ + info->zs_channel->curregs[3] |= Rx8; + info->zs_channel->curregs[5] |= Tx8; + break; + } + + info->zs_channel->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); + if (cflag & CSTOPB) { + info->zs_channel->curregs[4] |= SB2; + } else { + info->zs_channel->curregs[4] |= SB1; + } + if (cflag & PARENB) { + info->zs_channel->curregs[4] |= PAR_ENA; + } + if (!(cflag & PARODD)) { + info->zs_channel->curregs[4] |= PAR_EVEN; + } + + if (!(cflag & CLOCAL)) { + if (!(info->zs_channel->curregs[15] & DCDIE)) + info->read_reg_zero = read_zsreg(info->zs_channel, 0); + info->zs_channel->curregs[15] |= DCDIE; + } else + info->zs_channel->curregs[15] &= ~DCDIE; + if (cflag & CRTSCTS) { + info->zs_channel->curregs[15] |= CTSIE; + if ((read_zsreg(info->zs_channel, 0) & CTS) != 0) + info->tx_stopped = 1; + } else { + info->zs_channel->curregs[15] &= ~CTSIE; + info->tx_stopped = 0; + } + + /* Load up the new values */ + load_zsregs(info->zs_channel, info->zs_channel->curregs); + + restore_flags(flags); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped || + !info->xmit_buf) + return; + + /* Enable transmitter */ + save_flags(flags); cli(); + transmit_chars(info); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit_buf) + return 0; + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + down(&tmp_buf_sem); + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + up(&tmp_buf_sem); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + + if (info->xmit_cnt && !tty->stopped && !info->tx_stopped + && !info->tx_active) + transmit_chars(info); + restore_flags(flags); + return total; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return info->xmit_cnt; +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + sti(); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) { + save_flags(flags); cli(); + info->x_char = STOP_CHAR(tty); + if (!info->tx_active) + transmit_chars(info); + restore_flags(flags); + } + + if (C_CRTSCTS(tty)) { + /* + * Here we want to turn off the RTS line. On Macintoshes, + * we only get the DTR line, which goes to both DTR and + * RTS on the modem. RTS doesn't go out to the serial + * port socket. So you should make sure your modem is + * set to ignore DTR if you're using CRTSCTS. + */ + save_flags(flags); cli(); + info->zs_chan_a->curregs[5] &= ~(DTR | RTS); + write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); + restore_flags(flags); + } +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + save_flags(flags); cli(); + if (info->x_char) + info->x_char = 0; + else { + info->x_char = START_CHAR(tty); + if (!info->tx_active) + transmit_chars(info); + } + restore_flags(flags); + } + + if (C_CRTSCTS(tty)) { + /* Assert RTS and DTR lines */ + save_flags(flags); cli(); + info->zs_chan_a->curregs[5] |= DTR | RTS; + write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); + restore_flags(flags); + } +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct dec_serial * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->port; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + return copy_to_user(retinfo,&tmp,sizeof(*retinfo)); +} + +static int set_serial_info(struct dec_serial * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct dec_serial old_info; + int retval = 0; + + if (!new_info) + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); + old_info = *info; + + if (!suser()) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ZILOG_USR_MASK) != + (info->flags & ~ZILOG_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ZILOG_USR_MASK) | + (new_serial.flags & ZILOG_USR_MASK)); + info->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->flags = ((info->flags & ~ZILOG_FLAGS) | + (new_serial.flags & ZILOG_FLAGS)); + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + +check_and_exit: + retval = startup(info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct dec_serial * info, unsigned int *value) +{ + unsigned char status; + + cli(); + status = read_zsreg(info->zs_channel, 0); + sti(); + put_user(status,value); + return 0; +} + +static int get_modem_info(struct dec_serial *info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + + cli(); + control = info->zs_chan_a->curregs[5]; + status = read_zsreg(info->zs_channel, 0); + sti(); + result = ((control & RTS) ? TIOCM_RTS: 0) + | ((control & DTR) ? TIOCM_DTR: 0) + | ((status & DCD) ? TIOCM_CAR: 0) + | ((status & CTS) ? 0: TIOCM_CTS); + put_user(result,value); + return 0; +} + +static int set_modem_info(struct dec_serial *info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg, bits; + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + get_user(arg, value); + bits = (arg & TIOCM_RTS? RTS: 0) + (arg & TIOCM_DTR? DTR: 0); + cli(); + switch (cmd) { + case TIOCMBIS: + info->zs_chan_a->curregs[5] |= bits; + break; + case TIOCMBIC: + info->zs_chan_a->curregs[5] &= ~bits; + break; + case TIOCMSET: + info->zs_chan_a->curregs[5] = (info->zs_chan_a->curregs[5] & ~(DTR | RTS)) | bits; + break; + default: + sti(); + return -EINVAL; + } + write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); + sti(); + return 0; +} + +/* + * rs_break - turn transmit break condition on/off + */ +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct dec_serial *info = (struct dec_serial *) tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + if (!info->port) + return; + + save_flags(flags); cli(); + if (break_state == -1) + info->zs_channel->curregs[5] |= SND_BRK; + else + info->zs_channel->curregs[5] &= ~SND_BRK; + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + restore_flags(flags); +} + +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct dec_serial * info = (struct dec_serial *)tty->driver_data; + +#ifdef CONFIG_KGDB + if (info->kgdb_channel) + return -ENODEV; +#endif + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct)); + if (error) + return error; + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERGETLSR: /* Get line status register */ + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + else + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct dec_serial)); + if (error) + return error; + copy_from_user((struct dec_serial *) arg, + info, sizeof(struct dec_serial)); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct dec_serial *info = (struct dec_serial *)tty->driver_data; + int was_stopped; + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + was_stopped = info->tx_stopped; + + change_speed(info); + + if (was_stopped && !info->tx_stopped) + rs_start(tty); +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. + * Wait for the last remaining data to be sent. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct dec_serial * info = (struct dec_serial *)tty->driver_data; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, info->count); +#endif + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + restore_flags(flags); + return; + } + info->flags |= ZILOG_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ZILOG_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ZILOG_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receiver and receive interrupts. + */ + info->zs_channel->curregs[3] &= ~RxENABLE; + write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); + info->zs_channel->curregs[1] = 0; /* disable any rx ints */ + write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]); + ZS_CLEARFIFO(info->zs_channel); + if (info->flags & ZILOG_INITIALIZED) { + /* + * Before we drop DTR, make sure the SCC transmitter + * has completely drained. + */ + rs_wait_until_sent(tty, info->timeout); + } + + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| + ZILOG_CLOSING); + wake_up_interruptible(&info->close_wait); + restore_flags(flags); +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct dec_serial *info = (struct dec_serial *) tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout) + char_time = MIN(char_time, timeout); + while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + current->state = TASK_RUNNING; +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void rs_hangup(struct tty_struct *tty) +{ + struct dec_serial * info = (struct dec_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct dec_serial *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ZILOG_CLOSING) { + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ZILOG_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ZILOG_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ZILOG_CALLOUT_ACTIVE) && + (info->flags & ZILOG_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ZILOG_CALLOUT_ACTIVE) && + (info->flags & ZILOG_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ZILOG_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ZILOG_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ZILOG_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ZILOG_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + 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, info->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(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + info->line, info->count); +#endif + cli(); + if (!tty_hung_up_p(filp)) + info->count--; + sti(); + info->blocked_open++; + while (1) { + cli(); + if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + zs_rtsdtr(info, 1); + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ZILOG_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ZILOG_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && + !(info->flags & ZILOG_CLOSING) && + (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + if (retval) + return retval; + info->flags |= ZILOG_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its ZILOG structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct dec_serial *info; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= zs_channels_found)) + return -ENODEV; + info = zs_soft + line; + +#ifdef CONFIG_KGDB + if (info->kgdb_channel) + return -ENODEV; +#endif + if (serial_paranoia_check(info, tty->device, "rs_open")) + return -ENODEV; +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->count); +#endif + + info->count++; + tty->driver_data = info; + info->tty = tty; + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ZILOG_CLOSING)) { + if (info->flags & ZILOG_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ZILOG_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); + } +#ifdef CONFIG_SERIAL_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info); + } +#endif + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif +/* tty->low_latency = 1; */ + return 0; +} + +/* Finally, routines used to initialize the serial driver. */ + +__initfunc(static void show_serial_version(void)) +{ + printk("DECstation Z8530 serial driver version 0.03\n"); +} + +/* Initialize Z8530s zs_channels + */ + +__initfunc(static void probe_sccs(void)) +{ + struct dec_serial **pp; + int i, n, n_chips = 0, n_channels, chip, channel; + unsigned long flags; + + /* + * did we get here by accident? + */ + if(!IOASIC) { + printk("Not on JUNKIO machine, skipping probe_sccs\n"); + return; + } + + /* + * When serial console is activated, tc_init has not been called yet + * and system_base is undefined. Unfortunately we have to hardcode + * system_base for this case :-(. HK + */ + switch(mips_machtype) { + case MACH_DS5000_2X0: + system_base = 0xbf800000; + n_chips = 2; + break; + case MACH_DS5000_1XX: + system_base = 0xbc000000; + n_chips = 2; + break; + case MACH_DS5000_XX: + system_base = 0xbc000000; + n_chips = 1; + break; + } + + pp = &zs_chain; + + n_channels = 0; + + for (chip = 0; chip < n_chips; chip++) { + for (channel = 0; channel <= 1; channel++) { + /* + * The sccs reside on the high byte of the 16 bit IOBUS + */ + zs_channels[n_channels].control = (volatile unsigned char *) + system_base + (0 == chip ? SCC0 : SCC1) + (0 == channel ? 1 : 9); + zs_channels[n_channels].data = zs_channels[n_channels].control + 4; + zs_soft[n_channels].zs_channel = &zs_channels[n_channels]; + zs_soft[n_channels].irq = SERIAL; + + if (0 == channel) + zs_soft[n_channels].zs_chan_a = &zs_channels[n_channels+1]; + else + zs_soft[n_channels].zs_chan_a = &zs_channels[n_channels]; + + *pp = &zs_soft[n_channels]; + pp = &zs_soft[n_channels].zs_next; + n_channels++; + } + } + + *pp = 0; + zs_channels_found = n_channels; + + for (n = 0; n < zs_channels_found; n++) { + for (i = 0; i < 16; i++) { + zs_soft[n].zs_channel->curregs[i] = zs_init_regs[i]; + } + } + +/* save_and_cli(flags); + for (n = 0; n < zs_channels_found; n++) { + if (((int)zs_channels[n].control & 0xf) == 1) { + write_zsreg(zs_soft[channel].zs_chan_a, R9, FHWRES); + udelay(10000); + write_zsreg(zs_soft[channel].zs_chan_a, R9, 0); + } + load_zsregs(zs_soft[n].zs_channel, zs_soft[n].zs_channel->curregs); + } + restore_flags(flags); */ +} + +/* zs_init inits the driver */ +__initfunc(int zs_init(void)) +{ + int channel, i; + unsigned long flags; + struct dec_serial *info; + + if(!IOASIC) + return -ENODEV; + + /* Setup base handler, and timer table. */ + init_bh(SERIAL_BH, do_serial_bh); + timer_table[RS_TIMER].fn = rs_timer; + timer_table[RS_TIMER].expires = 0; + + /* Find out how many Z8530 SCCs we have */ + if (zs_chain == 0) + probe_sccs(); + + show_serial_version(); + + /* Initialize the tty_driver structure */ + /* Not all of this is exactly right for us. */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = zs_channels_found; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + serial_driver.wait_until_sent = rs_wait_until_sent; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + save_flags(flags); cli(); + + for (channel = 0; channel < zs_channels_found; ++channel) { +#ifdef CONFIG_KGDB + if (zs_soft[channel].kgdb_channel) { + continue; + } +#endif + zs_soft[channel].clk_divisor = 16; + zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); + + if (request_irq(SERIAL, rs_interrupt, SA_SHIRQ, + "SCC", &zs_soft[channel])) + printk(KERN_ERR "decserial: can't get irq %d\n", + SERIAL); + + /* If console serial line, then enable interrupts. */ +/* if (zs_soft[channel].is_cons) { + write_zsreg(zs_soft[channel].zs_channel, R1, + (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB)); + write_zsreg(zs_soft[channel].zs_channel, R9, + (VIS | MIE)); + } +*/ + } + + for (info = zs_chain, i = 0; info; info = info->zs_next, i++) + { +#ifdef CONFIG_KGDB + if (info->kgdb_channel) { + continue; + } +#endif + info->magic = SERIAL_MAGIC; + info->port = (int) info->zs_channel->control; + info->line = i; + info->tty = 0; + info->custom_divisor = 16; + info->close_delay = 50; + info->closing_wait = 3000; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios =callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + printk("tty%02d at 0x%08x (irq = %d)", info->line, + info->port, info->irq); + printk(" is a Z85C30 SCC\n"); + } + + restore_flags(flags); + + return 0; +} + +/* + * register_serial and unregister_serial allows for serial ports to be + * configured at run-time, to support PCMCIA modems. + */ +/* PowerMac: Unused at this time, just here to make things link. */ +int register_serial(struct serial_struct *req) +{ + return -1; +} + +void unregister_serial(int line) +{ + return; +} + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_CONSOLE + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ + +/* This is for console output */ +static void +zs_console_putchar(struct dec_serial *info, char ch) +{ + int loops = 10000; + unsigned long flags; + + if(!info->zs_channel) + return; + + save_flags(flags); cli(); + + while (!(*(info->zs_channel->control) & Tx_BUF_EMP) && --loops) + RECOVERY_DELAY; + *(info->zs_channel->data) = ch; + wbflush(); RECOVERY_DELAY; + + restore_flags(flags); +} + +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + struct dec_serial *info; + int i; + unsigned char nine; + + info = zs_soft + co->index; + +#if 0 + /* + * disable master interrupt if necessary + */ + nine = info->zs_channel->curregs[9]; + if(nine & MIE) + write_zsreg(info->zs_channel, R9, nine & ~MIE); +#endif + /* + * do it + */ + for (i = 0; i < count; i++, s++) { + if(*s == '\n') + zs_console_putchar(info, '\r'); + zs_console_putchar(info, *s); + } + /* + * restore master interrupt enable + */ +#if 0 + write_zsreg(info->zs_channel, R9, nine); +#endif +} + +/* + * Receive character from the serial port + */ +static int serial_console_wait_key(struct console *co) +{ + return 0; +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +__initfunc(static int serial_console_setup(struct console *co, char *options)) +{ + struct dec_serial *info; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + char *s; + unsigned long flags; + + if(!IOASIC) + return -ENODEV; + + info = zs_soft + co->index; + + if (zs_chain == 0) + probe_sccs(); + + info->is_cons = 1; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) + parity = *s++; + if (*s) + bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; +#if 1 + save_and_cli(flags); + + /* + * Turn on RTS and DTR. + */ + zs_rtsdtr(info, 1); + + /* + * Finally, enable sequencing + */ + info->zs_channel->curregs[3] |= (RxENABLE | Rx8); + info->zs_channel->curregs[5] |= (TxENAB | Tx8); + info->zs_channel->curregs[9] |= (VIS); + write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); + write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); + write_zsreg(info->zs_channel, 9, info->zs_channel->curregs[9]); + + /* + * Clear the interrupt registers. + */ + write_zsreg(info->zs_channel, 0, ERR_RES); + write_zsreg(info->zs_channel, 0, RES_H_IUS); + + /* + * Set the speed of the serial port + */ + change_speed(info); + + /* Save the current value of RR0 */ + info->read_reg_zero = read_zsreg(info->zs_channel, 0); + + zs_soft[co->index].clk_divisor = 16; + zs_soft[co->index].zs_baud = get_zsbaud(&zs_soft[co->index]); + + restore_flags(flags); +#endif + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +/* + * Register console. + */ +__initfunc (long zs_serial_console_init(long kmem_start, long kmem_end)) +{ + register_console(&sercons); + return kmem_start; +} +#endif /* ifdef CONFIG_SERIAL_CONSOLE */ + +#ifdef CONFIG_KGDB +/* These are for receiving and sending characters under the kgdb + * source level kernel debugger. + */ +void putDebugChar(char kgdb_char) +{ + struct dec_zschannel *chan = zs_kgdbchan; + while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0) + RECOVERY_DELAY; + write_zsdata(chan, kgdb_char); +} +char getDebugChar(void) +{ + struct dec_zschannel *chan = zs_kgdbchan; + while((read_zsreg(chan, 0) & Rx_CH_AV) == 0) + eieio(); /*barrier();*/ + return read_zsdata(chan); +} +void kgdb_interruptible(int yes) +{ + struct dec_zschannel *chan = zs_kgdbchan; + int one, nine; + nine = read_zsreg(chan, 9); + if (yes == 1) { + one = EXT_INT_ENAB|INT_ALL_Rx; + nine |= MIE; + printk("turning serial ints on\n"); + } else { + one = RxINT_DISAB; + nine &= ~MIE; + printk("turning serial ints off\n"); + } + write_zsreg(chan, 1, one); + write_zsreg(chan, 9, nine); +} +/* This sets up the serial port we're using, and turns on + * interrupts for that channel, so kgdb is usable once we're done. + */ +static inline void kgdb_chaninit(struct dec_zschannel *ms, int intson, int bps) +{ + int brg; + int i, x; + volatile char *sccc = ms->control; + brg = BPS_TO_BRG(bps, ZS_CLOCK/16); + printk("setting bps on kgdb line to %d [brg=%x]\n", bps, brg); + for (i = 20000; i != 0; --i) { + x = *sccc; eieio(); + } + for (i = 0; i < sizeof(scc_inittab); ++i) { + write_zsreg(ms, scc_inittab[i], scc_inittab[i+1]); + i++; + } +} +/* This is called at boot time to prime the kgdb serial debugging + * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1 + * for /dev/ttyb which is determined in setup_arch() from the + * boot command line flags. + */ +__initfunc(void zs_kgdb_hook(int tty_num)) +{ + /* Find out how many Z8530 SCCs we have */ + if (zs_chain == 0) + probe_sccs(); + zs_soft[tty_num].zs_channel = &zs_channels[tty_num]; + zs_kgdbchan = zs_soft[tty_num].zs_channel; + zs_soft[tty_num].change_needed = 0; + zs_soft[tty_num].clk_divisor = 16; + zs_soft[tty_num].zs_baud = 38400; + zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ + zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */ + /* Turn on transmitter/receiver at 8-bits/char */ + kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400); + printk("KGDB: on channel %d initialized\n", tty_num); + set_debug_traps(); /* init stub */ +} +#endif /* ifdef CONFIG_KGDB */ diff --git a/drivers/tc/zs.h b/drivers/tc/zs.h new file mode 100644 index 000000000..d45ce05ed --- /dev/null +++ b/drivers/tc/zs.h @@ -0,0 +1,405 @@ +/* + * macserial.h: Definitions for the Macintosh Z8530 serial driver. + * + * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras. + * + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ +#ifndef _DECSERIAL_H +#define _DECSERIAL_H + +#define NUM_ZSREGS 16 + +struct serial_struct { + int type; + int line; + int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char reserved_char[2]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + int reserved[4]; +}; + +/* + * For the close wait times, 0 means wait forever for serial port to + * flush its output. 65535 means don't wait at all. + */ +#define ZILOG_CLOSING_WAIT_INF 0 +#define ZILOG_CLOSING_WAIT_NONE 65535 + +/* + * Definitions for ZILOG_struct (and serial_struct) flags field + */ +#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes + on the callout port */ +#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ +#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ + +#define ZILOG_SPD_MASK 0x0030 +#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ + +#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ + +#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ +#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ +#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ +#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ + +#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */ +#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged + * users can set or reset */ + +/* Internal flags used only by kernel/chr_drv/serial.c */ +#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */ +#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ +#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ +#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ +#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */ +#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */ +#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */ + +/* Software state per channel */ + +#ifdef __KERNEL__ +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +struct dec_zschannel { + volatile unsigned char *control; + volatile unsigned char *data; + + /* Current write register values */ + unsigned char curregs[NUM_ZSREGS]; +}; + +struct dec_serial { + struct dec_serial *zs_next; /* For IRQ servicing chain */ + struct dec_zschannel *zs_channel; /* Channel registers */ + struct dec_zschannel *zs_chan_a; /* A side registers */ + unsigned char read_reg_zero; + + char soft_carrier; /* Use soft carrier on this channel */ + char break_abort; /* Is serial console in, so process brk/abrt */ + char kgdb_channel; /* Kgdb is running on this channel */ + char is_cons; /* Is this our console. */ + unsigned char tx_active; /* character is being xmitted */ + unsigned char tx_stopped; /* output is suspended */ + + /* We need to know the current clock divisor + * to read the bps rate the chip has currently + * loaded. + */ + unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ + int zs_baud; + + char change_needed; + + int magic; + int baud_base; + int port; + int irq; + int flags; /* defined in tty.h */ + int type; /* UART type */ + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int line; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; +}; + + +#define SERIAL_MAGIC 0x5301 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE 4096 + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +#endif /* __KERNEL__ */ + +/* Conversion routines to/from brg time constants from/to bits + * per second. + */ +#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +/* The Zilog register set */ + +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENABLE 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxNBITS_MASK 0xc0 + +/* Write Register 4 */ + +#define PAR_ENA 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ +#define SB_MASK 0xc + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ +#define XCLK_MASK 0xC0 + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxNBITS_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define SOFTACK 0x20 /* Software Interrupt Acknowledge */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENABL 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define ZCIE 2 /* Zero count IE */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define FRM_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* Misc macros */ +#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES)) +#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \ + garbage = read_zsdata(channel); \ + garbage = read_zsdata(channel); \ + garbage = read_zsdata(channel); \ + } while(0) + +#endif /* !(_DECSERIAL_H) */ diff --git a/include/asm-mips/baget/baget.h b/include/asm-mips/baget/baget.h new file mode 100644 index 000000000..774493965 --- /dev/null +++ b/include/asm-mips/baget/baget.h @@ -0,0 +1,69 @@ +/* $Id$ + * baget.h: Definitions specific to Baget/MIPS machines. + * + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + */ +#ifndef _MIPS_BAGET_H +#define _MIPS_BAGET_H + +#include "vic.h" +#include "vac.h" + +#define VIC_BASE 0xBFFC0000 +#define VAC_BASE 0xBFFD0000 + + +/* Baget interrupt registers and their sizes */ + +struct baget_int_reg { + unsigned long address; + int size; /* in bytes */ +}; +#define BAGET_INT_NONE {0,0} + +#define BAGET_INT0_ACK {0xbffa0003,1} +#define BAGET_INT1_ACK {0xbffa0008,4} +#define BAGET_INT5_ACK {0xbff00000,1} + +#define BAGET_WRERR_ACK ((volatile char*)0xbff00000) + + +/* Baget address spaces */ + +#define BAGET_A24M_BASE 0xFC000000 /* VME-master A24 base address */ +#define BAGET_A24S_BASE 0x00000000 /* VME-slave A24 base address */ +#define BAGET_A24S_MASK 0x00c00000 /* VME-slave A24 address mask */ +#define BAGET_GSW_BASE 0xf000 /* global switches address base */ +#define BAGET_MSW_BASE(P) (0xe000+(P)*0x100) /* module switches address base */ + +#define BAGET_LED_BASE ((volatile short *)(0xbffd0000 + 0x00001800)) + +#define BAGET_PIL_NR 8 +#define BAGET_IRQ_NR NR_IRQS /* 64 */ +#define BAGET_IRQ_MASK(x) ((NR_IRQS-1) & (x)) + +#define BAGET_FPU_IRQ 0x26 +#define BAGET_VIC_TIMER_IRQ 0x32 +#define BAGET_VAC_TIMER_IRQ 0x37 +#define BAGET_BSM_IRQ 0x3C + +#define BAGET_LANCE_MEM_BASE 0xfcf10000 +#define BAGET_LANCE_MEM_SIZE 0x10000 +#define BAGET_LANCE_IO_BASE 0xbffeff00 + +#define BALO_OFFSET 0x400000 /* sync with ld.script.balo */ +#define BALO_SIZE 0x200000 /* sync with image segs size */ + +/* move it to the right place, somehere in include/asm */ +#define CAUSE_DBE 0x1C +#define CAUSE_MASK 0x7C + +/* Simple debug fascilities */ +extern void outc(char); +extern void outs(char *); +extern void baget_write(char *s, int l); +extern int baget_printk(const char *, ...); +extern void balo_printf( char *f, ... ); +extern void balo_hungup(void); + +#endif /* !(_MIPS_BAGET_H) */ diff --git a/include/asm-mips/baget/vac.h b/include/asm-mips/baget/vac.h new file mode 100644 index 000000000..4d1614773 --- /dev/null +++ b/include/asm-mips/baget/vac.h @@ -0,0 +1,208 @@ +/* $Id$ + * + * vac.h: Various VIC controller defines. The VIC is a VME controller + * used in Baget/MIPS series. + * + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + */ +#ifndef _MIPS_VAC_H +#define _MIPS_VAC_H + +#define VAC_SLSEL1_MASK 0x000 +#define VAC_SLSEL1_BASE 0x100 +#define VAC_SLSEL0_MASK 0x200 +#define VAC_SLSEL0_BASE 0x300 +#define VAC_ICFSEL_BASE 0x400 +#define VAC_ICFSEL_GLOBAL_VAL(x) (((x)>>8)&0xff) +#define VAC_ICFSEL_MODULE_VAL(x) ((x)&0xff) +#define VAC_DRAM_MASK 0x500 +#define VAC_BNDR2 0x600 +#define VAC_BNDR3 0x700 +#define VAC_A24_BASE 0x800 +#define VAC_A24_MASK (0x3f<<9) +#define VAC_A24_D32_ENABLE (1<<8) +#define VAC_A24_A24_CACHINH (1<<7) +#define VAC_A24_A16D32_ENABLE (1<<6) +#define VAC_A24_A16D32 (1<<5) +#define VAC_A24_DATAPATH (1<<4) +#define VAC_A24_IO_CACHINH (1<<3) +#define VAC_REG1 0x900 +#define VAC_REG2 0xA00 +#define VAC_REG3 0xB00 +#define VAC_REG_WORD (1<<15) +#define VAC_REG_ASIZ1 (1<<14) +#define VAC_REG_ASIZ0 (1<<13) +#define VAC_REG_ASIZ_VAL(x) (((x)>>13)&3) +#define VAC_REG_CACHINH (1<<12) +#define VAC_REG_INACTIVE (0<<10) +#define VAC_REG_SHARED (1<<10) +#define VAC_REG_VSB (2<<10) +#define VAC_REG_MWB (3<<10) +#define VAC_REG_MASK (3<<10) +#define VAC_REG_MODE(x) (((x)>>10)&3) +#define VAC_IOSEL4_CTRL 0xC00 +#define VAC_IOSEL5_CTRL 0xD00 +#define VAC_SHRCS_CTRL 0xE00 +#define VAC_EPROMCS_CTRL 0xF00 +#define VAC_IOSEL0_CTRL 0x1000 +#define VAC_IOSEL1_CTRL 0x1100 +#define VAC_IOSEL2_CTRL 0x1200 +#define VAC_IOSEL3_CTRL 0x1300 +#define VAC_CTRL_IOWR (1<<0) +#define VAC_CTRL_IORD (1<<1) +#define VAC_CTRL_DELAY_IOSELI(x) (((x)&3)<<2) +#define VAC_CTRL_DELAY_IOSELI_VAL(x) (((x)>>2)&3) +#define VAC_CTRL_DELAY_IOWR(x) (((x)&3)<<4) +#define VAC_CTRL_DELAY_IOWR_VAL(x) (((x)>>4)&3) +#define VAC_CTRL_DELAY_IORD(x) (((x)&3)<<6) +#define VAC_CTRL_DELAY_IORD_VAL(x) (((x)>>6)&3) +#define VAC_CTRL_RECOVERY_IOSELI(x) ((((x)-1)&7)<<8) +#define VAC_CTRL_RECOVERY_IOSELI_VAL(x) ((((x)>>8)&7)+1) +#define VAC_CTRL_DSACK0 (1<<11) +#define VAC_CTRL_DSACK1 (1<<12) +#define VAC_CTRL_DELAY_DSACKI(x) ((((x)-1)&7)<<13) +#define VAC_CTRL_DELAY_DSACKI_VAL(x) ((((x)>>13)&7)+1) +#define VAC_DECODE_CTRL 0x1400 +#define VAC_DECODE_FPUCS (1<<0) +#define VAC_DECODE_CPUCLK(x) (((x)&3)<<1) +#define VAC_DECODE_CPUCLK_VAL(x) (((x)>>1)&3) +#define VAC_DECODE_RDR_SLSEL0 (1<<3) +#define VAC_DECODE_RDR_SLSEL1 (1<<4) +#define VAC_DECODE_DSACK (1<<5) +#define VAC_DECODE_QFY_BNDR (1<<6) +#define VAC_DECODE_QFY_ICFSEL (1<<7) +#define VAC_DECODE_QFY_SLSEL1 (1<<8) +#define VAC_DECODE_QFY_SLSEL0 (1<<9) +#define VAC_DECODE_CMP_SLSEL1_LO (1<<10) +#define VAC_DECODE_CMP_SLSEL1_HI (1<<11) +#define VAC_DECODE_CMP_SLSEL1_VAL(x) (((x)>>10)&3) +#define VAC_DECODE_DRAMCS (3<<12) +#define VAC_DECODE_SHRCS (2<<12) +#define VAC_DECODE_VSBSEL (1<<12) +#define VAC_DECODE_EPROMCS (0<<12) +#define VAC_DECODE_MODE_VAL(x) (((x)>>12)&3) +#define VAC_DECODE_QFY_DRAMCS (1<<14) +#define VAC_DECODE_DSACKI (1<<15) +#define VAC_INT_STATUS 0x1500 +#define VAC_INT_CTRL 0x1600 +#define VAC_INT_CTRL_TIMER_PIO11 (3<<0) +#define VAC_INT_CTRL_TIMER_PIO10 (2<<0) +#define VAC_INT_CTRL_TIMER_PIO7 (1<<0) +#define VAC_INT_CTRL_TIMER_DISABLE (0<<0) +#define VAC_INT_CTRL_UART_B_PIO11 (3<<2) +#define VAC_INT_CTRL_UART_B_PIO10 (2<<2) +#define VAC_INT_CTRL_UART_B_PIO7 (1<<2) +#define VAC_INT_CTRL_UART_B_DISABLE (0<<2) +#define VAC_INT_CTRL_UART_A_PIO11 (3<<4) +#define VAC_INT_CTRL_UART_A_PIO10 (2<<4) +#define VAC_INT_CTRL_UART_A_PIO7 (1<<4) +#define VAC_INT_CTRL_UART_A_DISABLE (0<<4) +#define VAC_INT_CTRL_MBOX_PIO11 (3<<6) +#define VAC_INT_CTRL_MBOX_PIO10 (2<<6) +#define VAC_INT_CTRL_MBOX_PIO7 (1<<6) +#define VAC_INT_CTRL_MBOX_DISABLE (0<<6) +#define VAC_INT_CTRL_PIO4_PIO11 (3<<8) +#define VAC_INT_CTRL_PIO4_PIO10 (2<<8) +#define VAC_INT_CTRL_PIO4_PIO7 (1<<8) +#define VAC_INT_CTRL_PIO4_DISABLE (0<<8) +#define VAC_INT_CTRL_PIO7_PIO11 (3<<10) +#define VAC_INT_CTRL_PIO7_PIO10 (2<<10) +#define VAC_INT_CTRL_PIO7_PIO7 (1<<10) +#define VAC_INT_CTRL_PIO7_DISABLE (0<<10) +#define VAC_INT_CTRL_PIO8_PIO11 (3<<12) +#define VAC_INT_CTRL_PIO8_PIO10 (2<<12) +#define VAC_INT_CTRL_PIO8_PIO7 (1<<12) +#define VAC_INT_CTRL_PIO8_DISABLE (0<<12) +#define VAC_INT_CTRL_PIO9_PIO11 (3<<14) +#define VAC_INT_CTRL_PIO9_PIO10 (2<<14) +#define VAC_INT_CTRL_PIO9_PIO7 (1<<14) +#define VAC_INT_CTRL_PIO9_DISABLE (0<<14) +#define VAC_DEV_LOC 0x1700 +#define VAC_DEV_LOC_IOSEL(x) (1<<(x)) +#define VAC_PIO_DATA_OUT 0x1800 +#define VAC_PIO_PIN 0x1900 +#define VAC_PIO_DIRECTION 0x1A00 +#define VAC_PIO_DIR_OUT(x) (1<<(x)) +#define VAC_PIO_DIR_IN(x) (0<<(x)) +#define VAC_PIO_DIR_FCIACK (1<<14) +#define VAC_PIO_FUNC 0x1B00 +#define VAC_PIO_FUNC_UART_A_TX (1<<0) +#define VAC_PIO_FUNC_UART_A_RX (1<<1) +#define VAC_PIO_FUNC_UART_B_TX (1<<2) +#define VAC_PIO_FUNC_UART_B_RX (1<<3) +#define VAC_PIO_FUNC_IORD (1<<4) +#define VAC_PIO_FUNC_IOWR (1<<5) +#define VAC_PIO_FUNC_IOSEL3 (1<<6) +#define VAC_PIO_FUNC_IRQ7 (1<<7) +#define VAC_PIO_FUNC_IOSEL4 (1<<8) +#define VAC_PIO_FUNC_IOSEL5 (1<<9) +#define VAC_PIO_FUNC_IRQ10 (1<<10) +#define VAC_PIO_FUNC_IRQ11 (1<<11) +#define VAC_PIO_FUNC_OUT (1<<12) +#define VAC_PIO_FUNC_IOSEL2 (1<<13) +#define VAC_PIO_FUNC_DELAY (1<<14) +#define VAC_PIO_FUNC_FCIACK (1<<15) +#define VAC_CPU_CLK_DIV 0x1C00 +#define VAC_UART_A_MODE 0x1D00 +#define VAC_UART_MODE_PARITY_ENABLE (1<<15) /* Inversed in manual ? */ +#define VAC_UART_MODE_PARITY_ODD (1<<14) /* Inversed in manual ? */ +#define VAC_UART_MODE_8BIT_CHAR (1<<13) +#define VAC_UART_MODE_BAUD(x) (((x)&7)<<10) +#define VAC_UART_MODE_CHAR_RX_ENABLE (1<<9) +#define VAC_UART_MODE_CHAR_TX_ENABLE (1<<8) +#define VAC_UART_MODE_TX_ENABLE (1<<7) +#define VAC_UART_MODE_RX_ENABLE (1<<6) +#define VAC_UART_MODE_SEND_BREAK (1<<5) +#define VAC_UART_MODE_LOOPBACK (1<<4) +#define VAC_UART_MODE_INITIAL (VAC_UART_MODE_8BIT_CHAR | \ + VAC_UART_MODE_TX_ENABLE | \ + VAC_UART_MODE_RX_ENABLE | \ + VAC_UART_MODE_CHAR_TX_ENABLE | \ + VAC_UART_MODE_CHAR_RX_ENABLE | \ + VAC_UART_MODE_BAUD(5)) /* 9600/4 */ +#define VAC_UART_A_TX 0x1E00 +#define VAC_UART_B_MODE 0x1F00 +#define VAC_UART_A_RX 0x2000 +#define VAC_UART_RX_ERR_BREAK (1<<10) +#define VAC_UART_RX_ERR_FRAME (1<<9) +#define VAC_UART_RX_ERR_PARITY (1<<8) +#define VAC_UART_RX_DATA_MASK (0xff) +#define VAC_UART_B_RX 0x2100 +#define VAC_UART_B_TX 0x2200 +#define VAC_UART_A_INT_MASK 0x2300 +#define VAC_UART_INT_RX_READY (1<<15) +#define VAC_UART_INT_RX_FULL (1<<14) +#define VAC_UART_INT_RX_BREAK_CHANGE (1<<13) +#define VAC_UART_INT_RX_ERRS (1<<12) +#define VAC_UART_INT_TX_READY (1<<11) +#define VAC_UART_INT_TX_EMPTY (1<<10) +#define VAC_UART_B_INT_MASK 0x2400 +#define VAC_UART_A_INT_STATUS 0x2500 +#define VAC_UART_STATUS_RX_READY (1<<15) +#define VAC_UART_STATUS_RX_FULL (1<<14) +#define VAC_UART_STATUS_RX_BREAK_CHANGE (1<<13) +#define VAC_UART_STATUS_RX_ERR_PARITY (1<<12) +#define VAC_UART_STATUS_RX_ERR_FRAME (1<<11) +#define VAC_UART_STATUS_RX_ERR_OVERRUN (1<<10) +#define VAC_UART_STATUS_TX_READY (1<<9) +#define VAC_UART_STATUS_TX_EMPTY (1<<8) +#define VAC_UART_STATUS_INTS (0xff<<8) +#define VAC_UART_B_INT_STATUS 0x2600 +#define VAC_TIMER_DATA 0x2700 +#define VAC_TIMER_CTRL 0x2800 +#define VAC_TIMER_ONCE (1<<15) +#define VAC_TIMER_ENABLE (1<<14) +#define VAC_TIMER_PRESCALE(x) (((x)&0x3F)<<8) +#define VAC_ID 0x2900 + + +#ifndef __LANGUAGE_ASSEMBLY__ + +#define vac_inb(p) (*(volatile unsigned char *)(VAC_BASE + (p))) +#define vac_outb(v,p) (*((volatile unsigned char *)(VAC_BASE + (p))) = v) +#define vac_inw(p) (*(volatile unsigned short*)(VAC_BASE + (p))) +#define vac_outw(v,p) (*((volatile unsigned short*)(VAC_BASE + (p))) = v) + +#endif /* __LANGUAGE_ASSEMBLY__ */ + +#endif /* !(_MIPS_VAC_H) */ diff --git a/include/asm-mips/baget/vic.h b/include/asm-mips/baget/vic.h new file mode 100644 index 000000000..f995066ab --- /dev/null +++ b/include/asm-mips/baget/vic.h @@ -0,0 +1,193 @@ +/* $Id$ + * + * vic.h: Various VIC controller defines. The VIC is an interrupt controller + * used in Baget/MIPS series. + * + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + */ +#ifndef _MIPS_VIC_H +#define _MIPS_VIC_H + +#define VIC_VME_II 0x3 +#define VIC_VME_INT1 0x7 +#define VIC_VME_INT2 0xB +#define VIC_VME_INT3 0xF +#define VIC_VME_INT4 0x13 +#define VIC_VME_INT5 0x17 +#define VIC_VME_INT6 0x1B +#define VIC_VME_INT7 0x1F +#define VIC_DMA_INT 0x23 +#define VIC_LINT1 0x27 +#define VIC_LINT2 0x2B +#define VIC_LINT3 0x2F +#define VIC_LINT4 0x33 +#define VIC_LINT5 0x37 +#define VIC_LINT6 0x3B +#define VIC_LINT7 0x3F +#define VIC_ICGS_INT 0x43 +#define VIC_ICMS_INT 0x47 +#define VIC_INT_IPL(lev) ((~(lev))&0x7) +#define VIC_INT_ACTIVE (1<<3) +#define VIC_INT_AUTO (0<<4) +#define VIC_INT_NOAUTO (1<<4) +#define VIC_INT_LEVEL (0<<5) +#define VIC_INT_EDGE (1<<5) +#define VIC_INT_LOW (0<<6) +#define VIC_INT_HIGH (1<<6) +#define VIC_INT_ENABLE (0<<7) +#define VIC_INT_DISABLE (1<<7) +#define VIC_INT_SWITCH(x) (1<<(((x)&0x3)+4)) +#define VIC_ERR_INT 0x4B +#define VIC_ERR_INT_SYSFAIL_ACTIVE (1<<3) +#define VIC_ERR_INT_SYSFAIL (1<<4) +#define VIC_ERR_INT_TIMO (1<<5) +#define VIC_ERR_INT_WRPOST (1<<6) +#define VIC_ERR_INT_ACFAIL (1<<7) +#define VIC_ICGS_BASE 0x4F +#define VIC_ICMS_BASE 0x53 +#define VIC_ICxS_BASE_GSWITCH_MASK 0x3 +#define VIC_ICxS_BASE_ID(x) (((x)&0x3f)<<2) +#define VIC_LOCAL_BASE 0x57 +#define VIC_LOCAL_BASE_LINT_MASK 0x7 +#define VIC_LOCAL_BASE_ID(x) (((x)&0x1f)<<3) +#define VIC_ERR_BASE 0x5B +#define VIC_ERR_BASE_ACFAIL 0 +#define VIC_ERR_BASE_WRPOST 1 +#define VIC_ERR_BASE_TIMO 2 +#define VIC_ERR_BASE_SYSFAIL 3 +#define VIC_ERR_BASE_VMEACK 4 +#define VIC_ERR_BASE_DMA 5 +#define VIC_ERR_BASE_ID(x) (((x)&0x1f)<<3) +#define VIC_ICS 0x5F +#define VIC_IC0 0x63 +#define VIC_IC1 0x67 +#define VIC_IC2 0x6B +#define VIC_IC3 0x6F +#define VIC_IC4 0x73 +#define VIC_ID 0x77 +#define VIC_IC6 0x7B +#define VIC_IC6_IRESET_STATUS (1<<7) +#define VIC_IC6_HALT_STATUS (1<<6) +#define VIC_IC6_SYSRESET (3<<0) +#define VIC_IC6_RESET (2<<0) +#define VIC_IC6_HALT (1<<0) +#define VIC_IC6_RUN (0<<0) +#define VIC_IC7 0x7F +#define VIC_IC7_SYSFAIL (1<<7) +#define VIC_IC7_RESET (1<<6) +#define VIC_IC7_VME_MASTER (1<<5) +#define VIC_IC7_SEMSET(x) ((1<<(x))&0x1f) +#define VIC_VME_REQ 0x83 +#define VIC_VME_BASE1 0x87 +#define VIC_VME_BASE2 0x8B +#define VIC_VME_BASE3 0x8F +#define VIC_VME_BASE4 0x93 +#define VIC_VME_BASE5 0x97 +#define VIC_VME_BASE6 0x9B +#define VIC_VME_BASE7 0x9F +#define VIC_XFER_TIMO 0xA3 +#define VIC_XFER_TIMO_VME_PERIOD_INF (7<<5) +#define VIC_XFER_TIMO_VME_PERIOD_512 (6<<5) +#define VIC_XFER_TIMO_VME_PERIOD_256 (5<<5) +#define VIC_XFER_TIMO_VME_PERIOD_128 (4<<5) +#define VIC_XFER_TIMO_VME_PERIOD_64 (3<<5) +#define VIC_XFER_TIMO_VME_PERIOD_32 (2<<5) +#define VIC_XFER_TIMO_VME_PERIOD_16 (1<<5) +#define VIC_XFER_TIMO_VME_PERIOD_4 (0<<5) +#define VIC_XFER_TIMO_VME_PERIOD_VAL(x) (((x)>>5)&7) +#define VIC_XFER_TIMO_LOCAL_PERIOD_INF (7<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_512 (6<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_256 (5<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_128 (4<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_64 (3<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_32 (2<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_16 (1<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_4 (0<<2) +#define VIC_XFER_TIMO_LOCAL_PERIOD_VAL(x) (((x)>>2)&7) +#define VIC_XFER_TIMO_ARB (1<<1) +#define VIC_XFER_TIMO_VME (1<<0) +#define VIC_LOCAL_TIM 0xA7 +#define VIC_LOCAL_TIM_PAS_ASSERT(x) (((x)-2)&0xf) +#define VIC_LOCAL_TIM_PAS_ASSERT_VAL(x) (((x)&0xf)+2) +#define VIC_LOCAT_TIM_DS_DEASSERT(x) ((((x)-1)&1)<<4) +#define VIC_LOCAT_TIM_DS_DEASSERT_VAL(x) ((((x)>>4)&1)+1) +#define VIC_LOCAL_TIM_PAS_DEASSERT(x) ((((x)-1)&0x7)<<5) +#define VIC_LOCAL_TIM_PAS_DEASSERT_VAL(x) ((((x)>>5)&0x7)+1) +#define VIC_BXFER_DEF 0xAB +#define VIC_BXFER_DEF_VME_CROSS (1<<3) +#define VIC_BXFER_DEF_LOCAL_CROSS (1<<2) +#define VIC_BXFER_DEF_AMSR (1<<1) +#define VIC_BXFER_DEF_DUAL (1<<0) +#define VIC_IFACE_CFG 0xAF +#define VIC_IFACE_CFG_RMC3 (1<<7) +#define VIC_IFACE_CFG_RMC2 (1<<6) +#define VIC_IFACE_CFG_RMC1 (1<<5) +#define VIC_IFACE_CFG_HALT (1<<4) +#define VIC_IFACE_CFG_NOHALT (0<<4) +#define VIC_IFACE_CFG_NORMC (1<<3) +#define VIC_IFACE_CFG_DEADLOCK_VAL(x) (((x)>>3)&3) +#define VIC_IFACE_CFG_MSTAB (1<<2) +#define VIC_IFACE_CFG_TURBO (1<<1) +#define VIC_IFACE_CFG_NOTURBO (0<<1) +#define VIC_IFACE_CFG_VME (1<<0) +#define VIC_REQ_CFG 0xB3 +#define VIC_REQ_CFG_FAIRNESS_DISABLED 0 +#define VIC_REQ_CFG_FAIRNESS_ENABLED 1 +#define VIC_REQ_CFG_TIMO_DISABLED 0xf +#define VIC_REQ_CFG_DRAM_REFRESH (1<<4) +#define VIC_REQ_CFG_LEVEL(x) (((x)&3)<<5) +#define VIC_REQ_CFG_PRIO_ARBITRATION (1<<7) +#define VIC_REQ_CFG_RR_ARBITRATION (0<<7) +#define VIC_AMS 0xB7 +#define VIC_AMS_AM_2_0 (1<<7) +#define VIC_AMS_AM_5_3 (1<<6) +#define VIC_AMS_CODE(x) ((x)&0x1f) +#define VIC_BERR_STATUS 0xBB +#define VIC_DMA_STATUS 0xBF +#define VIC_SS0CR0 0xC3 +#define VIC_SS1CR0 0xCB +#define VIC_SSxCR0_LOCAL_XFER_ACCEL (2) +#define VIC_SSxCR0_LOCAL_XFER_SINGLE (1) +#define VIC_SSxCR0_LOCAL_XFER_NONE (0) +#define VIC_SSxCR0_A32 (0<<2) +#define VIC_SSxCR0_A24 (1<<2) +#define VIC_SSxCR0_A16 (2<<2) +#define VIC_SSxCR0_USER (3<<2) +#define VIC_SSxCR0_D32 (1<<4) +#define VIC_SSxCR0_SUPER (1<<5) +#define VIC_SS0CR0_TIMER_FREQ_MASK (3<<6) +#define VIC_SS0CR0_TIMER_FREQ_NONE (0<<6) +#define VIC_SS0CR0_TIMER_FREQ_50HZ (1<<6) +#define VIC_SS0CR0_TIMER_FREQ_1000HZ (2<<6) +#define VIC_SS0CR0_TIMER_FREQ_100HZ (3<<6) +#define VIC_SS1CR0_MASTER_WRPOST (1<<6) +#define VIC_SS1CR0_SLAVE_WRPOST (1<<7) +#define VIC_SS0CR1 0xC7 +#define VIC_SS1CR1 0xCF +#define VIC_SSxCR1_TF2(x) (((x)&0xf)<<4) +#define VIC_SSxCR1_TF1(x) ((x)&0xf) +#define VIC_RELEASE 0xD3 +#define VIC_RELEASE_BLKXFER_BLEN(x) ((x)&0x1f) +#define VIC_RELEASE_ROR (0<<6) +#define VIC_RELEASE_RWD (1<<6) +#define VIC_RELEASE_ROC (2<<6) +#define VIC_RELEASE_BCAP (3<<6) +#define VIC_BXFER_CTRL 0xD7 +#define VIC_BXFER_CTRL_MODULE (1<<7) +#define VIC_BXFER_CTRL_LOCAL (1<<6) +#define VIC_BXFER_CTRL_MOVEM (1<<5) +#define VIC_BXFER_CTRL_READ (1<<4) +#define VIC_BXFER_CTRL_WRITE (0<<4) +#define VIC_BXFER_CTRL_INTERLEAVE(x) ((x)&0xf) +#define VIC_BXFER_LEN_LO 0xDB +#define VIC_BXFER_LEN_HI 0xDF +#define VIC_SYS_RESET 0xE3 + +#ifndef __LANGUAGE_ASSEMBLY__ + +#define vic_inb(p) (*(volatile unsigned char *)(VIC_BASE + (p))) +#define vic_outb(v,p) (*((volatile unsigned char *)(VIC_BASE + (p))) = v) + +#endif /* __LANGUAGE_ASSEMBLY__ */ + +#endif /* !(_MIPS_VIC_H) */ diff --git a/include/asm-mips/dec/interrupts.h b/include/asm-mips/dec/interrupts.h new file mode 100644 index 000000000..5f99727cc --- /dev/null +++ b/include/asm-mips/dec/interrupts.h @@ -0,0 +1,79 @@ +/* + * Miscellaneous definitions used to initialise the interrupt vector table + * with the machine-specific interrupt routines. + * + * 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. + * + * Copyright (C) 1997 by Paul M. Antoine. + * reworked 1998 by Harald Koerfgen. + */ + +#ifndef __ASM_DEC_INTERRUPTS_H +#define __ASM_DEC_INTERRUPTS_H + +/* + * DECstation Interrupts + */ + +/* + * This list reflects the priority of the Interrupts. + * Exception: on kmins we have to handle Memory Error + * Interrupts before the TC Interrupts. + */ +#define CLOCK 0 +#define SCSI_INT 1 +#define ETHER 2 +#define SERIAL 3 +#define TC0 4 +#define TC1 5 +#define TC2 6 +#define MEMORY 7 +#define FPU 8 +#define HALT 9 + +#define NR_INTS 10 + +#ifndef _LANGUAGE_ASSEMBLY +/* + * Data structure to hide the differences between the DECstation Interrupts + * + * If asic_mask == NULL, the interrupt is directly handled by the CPU. + * Otherwise this Interrupt is handled the IRQ Controller. + */ + +typedef struct +{ + unsigned int cpu_mask; /* checking and enabling interrupts in CP0 */ + unsigned int iemask; /* enabling interrupts in IRQ Controller */ +} decint_t; + +/* + * Interrupt table structure to hide differences between different + * systems such. + */ +extern void *cpu_ivec_tbl[8]; +extern long cpu_mask_tbl[8]; +extern long cpu_irq_nr[8]; +extern long asic_irq_nr[32]; +extern long asic_mask_tbl[32]; + +/* + * Common interrupt routine prototypes for all DECStations + */ +extern void dec_intr_unimplemented(void); +extern void dec_intr_fpu(void); +extern void dec_intr_rtc(void); + +extern void kn02_io_int(void); +extern void kn02ba_io_int(void); +extern void kn03_io_int(void); + +extern void intr_halt(void); + +extern void asic_intr_unimplemented(void); + +#endif +#endif + diff --git a/include/asm-mips/dec/ioasic_addrs.h b/include/asm-mips/dec/ioasic_addrs.h new file mode 100644 index 000000000..a2044782e --- /dev/null +++ b/include/asm-mips/dec/ioasic_addrs.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * Definitions for the address map in the JUNKIO Asic + * + * Created with Information from: + * + * "DEC 3000 300/400/500/600/700/800/900 AXP Models System Programmer's Manual" + * + * and the Mach Sources + */ + +#ifndef IOASIC_ADDRS_H +#define IOASIC_ADDRS_H + +#define CHUNK_SIZE 0x00040000 + +#define SYSTEM_ROM 00*CHUNK_SIZE /* ??? */ +#define IOCTL 01*CHUNK_SIZE +#define ESAR 02*CHUNK_SIZE +#define LANCE 03*CHUNK_SIZE +#define SCC0 04*CHUNK_SIZE +#define VDAC_HI 05*CHUNK_SIZE /* maxine only */ +#define SCC1 06*CHUNK_SIZE +#define VDAC_LO 07*CHUNK_SIZE /* maxine only */ +#define TOY 08*CHUNK_SIZE +#define ISDN 09*CHUNK_SIZE /* maxine only */ +#define ERRADDR 09*CHUNK_SIZE /* 3maxplus only */ +#define CHKSYN 10*CHUNK_SIZE /* 3maxplus only */ +#define ACCESS_BUS 10*CHUNK_SIZE /* maxine only */ +#define MCR 11*CHUNK_SIZE /* 3maxplus only */ +#define FLOPPY 11*CHUNK_SIZE /* maxine only */ +#define SCSI 12*CHUNK_SIZE +#define FLOPPY_DMA 13*CHUNK_SIZE /* maxine only */ +#define SCSI_DMA 14*CHUNK_SIZE +#define RESERVED_4 15*CHUNK_SIZE + +/* + * Offsets for IOCTL registers (relative to (system_base + IOCTL)) + */ +#define SCSI_DMA_P 0x00 /* SCSI DMA Pointer */ +#define SCSI_DMA_BP 0x10 /* SCSI DMA Buffer Pointer */ +#define LANCE_DMA_P 0x20 /* LANCE DMA Pointer */ +#define SCC0_T_DMA_P 0x30 /* Communication Port 1 Transmit DMA Pointer */ +#define SCC0_R_DMA_P 0x40 /* Communication Port 1 Receive DMA Pointer */ +#define SCC1_T_DMA_P 0x50 /* Communication Port 2 Transmit DMA Pointer */ +#define SCC1_R_DMA_P 0x60 /* Communication Port 2 Receive DMA Pointer */ +#define FLOPPY_DMA_P 0x70 /* Floppy DMA Pointer */ +#define ISDN_T_DMA_P 0x80 /* ISDN Transmit DMA Pointer */ +#define ISDN_T_DMA_BP 0x90 /* ISDN Transmit DMA Buffer Pointer */ +#define ISDN_R_DMA_P 0xa0 /* ISDN Receive DMA Pointer */ +#define ISDN_R_DMA_BP 0xb0 /* ISDN Receive DMA Buffer Pointer */ + +#define SSR 0x100 /* System Support Register */ +#define SIR 0x110 /* System Interrupt Register */ +#define SIMR 0x120 /* System Interrupt Mask Register */ + +/* + * These come from mach, meaning unkown yet + */ +#define SCSI_SCR 0x1b0 +#define SCSI_SDR0 0x1c0 +#define SCSI_SDR1 0x1d0 + +#endif diff --git a/include/asm-mips/dec/ioasic_ints.h b/include/asm-mips/dec/ioasic_ints.h new file mode 100644 index 000000000..e1f61f1cb --- /dev/null +++ b/include/asm-mips/dec/ioasic_ints.h @@ -0,0 +1,108 @@ +/* + * 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. + * + * Definitions for the interrupt related bits in the JUNKIO Asic + * interrupt status register (and the interrupt mask register, of course) + * + * Created with Information from: + * + * "DEC 3000 300/400/500/600/700/800/900 AXP Models System Programmer's Manual" + * + * and the Mach Sources + */ + +/* + * the upper 16 bits are common to all JUNKIO machines + * (except the FLOPPY and ISDN bits, which are Maxine sepcific) + */ +#define SCC0_TRANS_PAGEEND 0x80000000 /* Serial DMA Errors */ +#define SCC0_TRANS_MEMRDERR 0x40000000 /* see below */ +#define SCC0_RECV_HALFPAGE 0x20000000 +#define SCC0_RECV_PAGOVRRUN 0x10000000 +#define SCC1_TRANS_PAGEEND 0x08000000 /* end of page reached */ +#define SCC1_TRANS_MEMRDERR 0x04000000 /* SCC1 DMA memory err */ +#define SCC1_RECV_HALFPAGE 0x02000000 /* SCC1 half page */ +#define SCC1_RECV_PAGOVRRUN 0x01000000 /* SCC1 receive overrun */ +#define FLOPPY_DMA_ERROR 0x00800000 /* FDI DMA error */ +#define ISDN_TRANS_PTR_LOADED 0x00400000 /* xmitbuf ptr loaded */ +#define ISDN_RECV_PTR_LOADED 0x00200000 /* rcvbuf ptr loaded */ +#define ISDN_DMA_MEMRDERR 0x00100000 /* read or ovrrun error */ +#define SCSI_PTR_LOADED 0x00080000 +#define SCSI_PAGOVRRUN 0x00040000 /* page overrun? */ +#define SCSI_DMA_MEMRDERR 0x00020000 +#define LANCE_DMA_MEMRDERR 0x00010000 + +/* + * the lower 16 bits are system specific + */ + +/* + * The following three seem to be in common + */ +#define SCSI_CHIP 0x00000200 +#define LANCE_CHIP 0x00000100 +#define SCC1_CHIP 0x00000080 /* NOT on maxine */ +#define SCC0_CHIP 0x00000040 + +/* + * The rest is different + */ + +/* kmin aka 3min aka kn02ba aka DS5000_1xx */ +#define KMIN_TIMEOUT 0x00001000 /* CPU IO-Write Timeout */ +#define KMIN_CLOCK 0x00000020 +#define KMIN_SCSI_FIFO 0x00000004 /* SCSI Data Ready */ + +/* kn02ca aka maxine */ +#define MAXINE_FLOPPY 0x00008000 /* FDI Interrupt */ +#define MAXINE_TC0 0x00001000 /* TC Option 0 */ +#define MAXINE_ISDN 0x00000800 /* ISDN Chip */ +#define MAXINE_FLOPPY_HDS 0x00000080 /* Floppy Status */ +#define MAXINE_TC1 0x00000020 /* TC Option 1 */ +#define MAXINE_FLOPPY_XDS 0x00000010 /* Floppy Status */ +#define MAXINE_VINT 0x00000008 /* Video Frame */ +#define MAXINE_N_VINT 0x00000004 /* Not Video frame */ +#define MAXINE_DTOP_TRANS 0x00000002 /* DTI Xmit-Rdy */ +#define MAXINE_DTOP_RECV 0x00000001 /* DTI Recv-Available */ + +/* kn03 aka 3max+ aka DS5000_2x0 */ +#define KN03_TC2 0x00004000 +#define KN03_TC1 0x00002000 +#define KN03_TC0 0x00001000 +#define KN03_SCSI_FIFO 0x00000004 /* ??? Info from Mach */ + +/* + * Now form groups, i.e. all serial interrupts, all SCSI interrupts and so on. + */ +#define SERIAL_INTS (SCC0_TRANS_PAGEEND | SCC0_TRANS_MEMRDERR | \ + SCC0_RECV_HALFPAGE | SCC0_RECV_PAGOVRRUN | \ + SCC1_TRANS_PAGEEND | SCC1_TRANS_MEMRDERR | \ + SCC1_RECV_HALFPAGE | SCC1_RECV_PAGOVRRUN | \ + SCC1_CHIP | SCC0_CHIP) + +#define XINE_SERIAL_INTS (SCC0_TRANS_PAGEEND | SCC0_TRANS_MEMRDERR | \ + SCC0_RECV_HALFPAGE | SCC0_RECV_PAGOVRRUN | \ + SCC0_CHIP) + +#define SCSI_INTS (SCSI_PTR_LOADED | SCSI_PAGOVRRUN | \ + SCSI_DMA_MEMRDERR | SCSI_CHIP) + +#define KMIN_SCSI_INTS (SCSI_PTR_LOADED | SCSI_PAGOVRRUN | \ + SCSI_DMA_MEMRDERR | SCSI_CHIP | KMIN_SCSI_FIFO) + +#define LANCE_INTS (LANCE_DMA_MEMRDERR | LANCE_CHIP) + +/* + * For future use ... + */ +#define XINE_FLOPPY_INTS (MAXINE_FLOPPY | MAXINE_FLOPPY_HDS | \ + FLOPPY_DMA_ERROR | MAXINE_FLOPPY_XDS) + +#define XINE_ISDN_INTS (MAXINE_ISDN | ISDN_TRANS_PTR_LOADED | \ + ISDN_RECV_PTR_LOADED | ISDN_DMA_MEMRDERR) + +#define XINE_DTOP_INTS (MAXINE_DTOP_TRANS | DTOP_RECV | \ + ISDN_TRANS_PTR_LOADED | ISDN_RECV_PTR_LOADED | \ + ISDN_DMA_MEMRDERR) diff --git a/include/asm-mips/dec/kn01.h b/include/asm-mips/dec/kn01.h new file mode 100644 index 000000000..61a46e843 --- /dev/null +++ b/include/asm-mips/dec/kn01.h @@ -0,0 +1,28 @@ +/* + * Hardware info about DEC DECstation DS2100/3100 systems (otherwise known + * as pmax or kn01. + * + * 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. + * + * Copyright (C) 1995,1996 by Paul M. Antoine, some code and definitions + * are by curteousy of Chris Fraser. + * + * This file is under construction - you were warned! + */ +#ifndef __ASM_MIPS_DEC_KN01_H +#define __ASM_MIPS_DEC_KN01_H + +#include <asm/addrspace.h> + +/* + * Some port addresses... + * FIXME: these addresses are incomplete and need tidying up! + */ + +#define KN01_LANCE_BASE (KSEG1ADDR(0x18000000)) /* 0xB8000000 */ +#define KN01_DZ11_BASE (KSEG1ADDR(0x1c000000)) /* 0xBC000000 */ +#define KN01_RTC_BASE (KSEG1ADDR(0x1d000000)) /* 0xBD000000 */ + +#endif /* __ASM_MIPS_DEC_KN01_H */ diff --git a/include/asm-mips/dec/kn02.h b/include/asm-mips/dec/kn02.h new file mode 100644 index 000000000..9888eb49f --- /dev/null +++ b/include/asm-mips/dec/kn02.h @@ -0,0 +1,41 @@ +/* + * Hardware info about DEC DECstation 5000/2xx systems (otherwise known + * as 3max or kn02. + * + * 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. + * + * Copyright (C) 1995,1996 by Paul M. Antoine, some code and definitions + * are by curteousy of Chris Fraser. + * + * This file is under construction - you were warned! + */ +#ifndef __ASM_MIPS_DEC_KN02_H +#define __ASM_MIPS_DEC_KN02_H + +#include <asm/addrspace.h> + +/* + * Motherboard regs (kseg1 addresses) + */ +#define KN02_CSR_ADDR KSEG1ADDR(0x1ff00000) /* system control & status reg */ + +/* + * Some port addresses... + * FIXME: these addresses are incomplete and need tidying up! + */ +#define KN02_RTC_BASE KSEG1ADDR(0x1fe80000) +#define KN02_DZ11_BASE KSEG1ADDR(0x1fe00000) + +/* + * Interrupt enable Bits + */ +#define KN02_SLOT0 (1<<16) +#define KN02_SLOT1 (1<<17) +#define KN02_SLOT2 (1<<18) +#define KN02_SLOT5 (1<<21) +#define KN02_SLOT6 (1<<22) +#define KN02_SLOT7 (1<<23) + +#endif /* __ASM_MIPS_DEC_KN02_H */ diff --git a/include/asm-mips/dec/kn02xa.h b/include/asm-mips/dec/kn02xa.h new file mode 100644 index 000000000..b72367fec --- /dev/null +++ b/include/asm-mips/dec/kn02xa.h @@ -0,0 +1,34 @@ +/* + * Hardware info about DEC DECstation 5000/1xx systems (otherwise known + * as 3min or kn02ba. Apllies to the Personal DECstations 5000/xx (otherwise known + * as maxine or kn02ca) as well. + * + * 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. + * + * Copyright (C) 1995,1996 by Paul M. Antoine, some code and definitions + * are by curteousy of Chris Fraser. + * + * These are addresses which have to be known early in the boot process. + * For other addresses refer to tc.h ioasic_addrs.h and friends. + */ +#ifndef __ASM_MIPS_DEC_KN02XA_H +#define __ASM_MIPS_DEC_KN02XA_H + +#include <asm/addrspace.h> + +/* + * Motherboard regs (kseg1 addresses) + */ +#define KN02XA_SSR_ADDR KSEG1ADDR(0x1c040100) /* system control & status reg */ +#define KN02XA_SIR_ADDR KSEG1ADDR(0x1c040110) /* system interrupt reg */ +#define KN02XA_SIRM_ADDR KSEG1ADDR(0x1c040120) /* system interrupt mask reg */ + +/* + * Some port addresses... + * FIXME: these addresses are incomplete and need tidying up! + */ +#define KN02XA_RTC_BASE (KSEG1ADDR(0x1c000000 + 0x200000)) /* ASIC + SL8 */ + +#endif /* __ASM_MIPS_DEC_KN02XA_H */ diff --git a/include/asm-mips/dec/kn03.h b/include/asm-mips/dec/kn03.h new file mode 100644 index 000000000..87ccae4b2 --- /dev/null +++ b/include/asm-mips/dec/kn03.h @@ -0,0 +1,33 @@ +/* + * Hardware info about DEC DECstation 5000/2x0 systems (otherwise known + * as 3max+ or kn03. + * + * 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. + * + * Copyright (C) 1995,1996 by Paul M. Antoine, some code and definitions + * are by curteousy of Chris Fraser. + * + * These are addresses which have to be known early in the boot process. + * For other addresses refer to tc.h ioasic_addrs.h and friends. + */ +#ifndef __ASM_MIPS_DEC_KN03_H +#define __ASM_MIPS_DEC_KN03_H + +#include <asm/addrspace.h> + +/* + * Motherboard regs (kseg1 addresses) + */ +#define KN03_SSR_ADDR KSEG1ADDR(0x1f840100) /* system control & status reg */ +#define KN03_SIR_ADDR KSEG1ADDR(0x1f840110) /* system interrupt reg */ +#define KN03_SIRM_ADDR KSEG1ADDR(0x1f840120) /* system interrupt mask reg */ + +/* + * Some port addresses... + * FIXME: these addresses are incomplete and need tidying up! + */ +#define KN03_RTC_BASE (KSEG1ADDR(0x1f800000 + 0x200000)) /* ASIC + SL8 */ + +#endif /* __ASM_MIPS_DEC_KN03_H */ diff --git a/include/asm-mips/dec/machtype.h b/include/asm-mips/dec/machtype.h new file mode 100644 index 000000000..ed4335d19 --- /dev/null +++ b/include/asm-mips/dec/machtype.h @@ -0,0 +1,20 @@ +/* + * Various machine type definitions + * + * 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. + * + * Copyright (c) 1998 Harald Koerfgen + */ +#include <asm/bootinfo.h> + +#define TURBOCHANNEL (mips_machtype == MACH_DS5000_200 || \ + mips_machtype == MACH_DS5000_1XX || \ + mips_machtype == MACH_DS5000_XX || \ + mips_machtype == MACH_DS5000_2X0) + +#define IOASIC (mips_machtype == MACH_DS5000_1XX || \ + mips_machtype == MACH_DS5000_XX || \ + mips_machtype == MACH_DS5000_2X0) + diff --git a/include/asm-mips/dec/tc.h b/include/asm-mips/dec/tc.h new file mode 100644 index 000000000..64fb03374 --- /dev/null +++ b/include/asm-mips/dec/tc.h @@ -0,0 +1,43 @@ +/* + * Interface to the TURBOchannel related routines + * + * 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. + * + * Copyright (c) 1998 Harald Koerfgen + */ +#ifndef ASM_TC_H +#define ASM_TC_H + +extern unsigned long system_base; + +/* + * Search for a TURBOchannel Option Module + * with a certain name. Returns slot number + * of the first card not in use or -ENODEV + * if none found. + */ +extern int search_tc_card(const char *); +/* + * Marks the card in slot as used + */ +extern void claim_tc_card(int); +/* + * Marks the card in slot as free + */ +extern void release_tc_card(int); +/* + * Return base address of card in slot + */ +extern unsigned long get_tc_base_addr(int); +/* + * Return interrupt number of slot + */ +extern unsigned long get_tc_irq_nr(int); +/* + * Return TURBOchannel clock frequency in hz + */ +extern unsigned long get_tc_speed(void); + +#endif diff --git a/include/asm-mips/dec/tcinfo.h b/include/asm-mips/dec/tcinfo.h new file mode 100644 index 000000000..72ecc894a --- /dev/null +++ b/include/asm-mips/dec/tcinfo.h @@ -0,0 +1,47 @@ +/* + * Various TURBOchannel related stuff + * + * 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. + * + * Information obtained through the get_tcinfo prom call + * created from: + * + * TURBOchannel Firmware Specification + * + * EK-TCAAD-FS-004 + * from Digital Equipment Corporation + * + * Copyright (c) 1998 Harald Koerfgen + */ + +typedef struct { + int revision; + int clk_period; + int slot_size; + int io_timeout; + int dma_range; + int max_dma_burst; + int parity; + int reserved[4]; +} tcinfo; + +#define MAX_SLOT 7 + +typedef struct { + unsigned long base_addr; + unsigned char name[9]; + unsigned char vendor[9]; + unsigned char firmware[9]; + int interrupt; + int flags; +} slot_info; + +/* + * Values for flags + */ +#define FREE 1<<0 +#define IN_USE 1<<1 + + diff --git a/include/asm-mips/dec/tcmodule.h b/include/asm-mips/dec/tcmodule.h new file mode 100644 index 000000000..26c5a5e29 --- /dev/null +++ b/include/asm-mips/dec/tcmodule.h @@ -0,0 +1,35 @@ +/* + * 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. + * + * Offsets for the ROM header locations for + * TURBOchannel cards + * + * created from: + * + * TURBOchannel Firmware Specification + * + * EK-TCAAD-FS-004 + * from Digital Equipment Corporation + * + * Jan.1998 Harald Koerfgen + */ + +#define OLDCARD 0x3c0000 + +#define ROM_WIDTH 0x3e0 +#define ROM_STRIDE 0x3e4 +#define ROM_SIZE 0x3e8 +#define SLOT_SIZE 0x3ec +#define PATTERN0 0x3f0 +#define PATTERN1 0x3f4 +#define PATTERN2 0x3f8 +#define PATTERN3 0x3fc +#define FIRM_VER 0x400 +#define VENDOR 0x420 +#define MODULE 0x440 +#define FIRM_TYPE 0x460 +#define FLAGS 0x470 + +#define ROM_OBJECTS 0x480 |