From a6afd9f3e819de4795fcd356e5bfad446e4323f2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Feb 2011 16:14:56 -0800 Subject: tty: move a number of tty drivers from drivers/char/ to drivers/tty/ As planned by Arnd Bergmann, this moves the following drivers from drivers/char/ to drivers/tty/ as that's where they really belong: amiserial nozomi synclink rocket cyclades moxa mxser isicom bfin_jtag_comm Cc: Arnd Bergmann Cc: Alan Cox Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 172 - drivers/char/Makefile | 11 - drivers/char/amiserial.c | 2178 ----------- drivers/char/bfin_jtag_comm.c | 366 -- drivers/char/cyclades.c | 4200 --------------------- drivers/char/isicom.c | 1736 --------- drivers/char/moxa.c | 2092 ----------- drivers/char/moxa.h | 304 -- drivers/char/mxser.c | 2757 -------------- drivers/char/mxser.h | 150 - drivers/char/nozomi.c | 1993 ---------- drivers/char/rocket.c | 3199 ---------------- drivers/char/rocket.h | 111 - drivers/char/rocket_int.h | 1214 ------ drivers/char/synclink.c | 8119 ----------------------------------------- drivers/char/synclink_gt.c | 5161 -------------------------- drivers/char/synclinkmp.c | 5600 ---------------------------- drivers/tty/Kconfig | 171 + drivers/tty/Makefile | 13 + drivers/tty/amiserial.c | 2178 +++++++++++ drivers/tty/bfin_jtag_comm.c | 366 ++ drivers/tty/cyclades.c | 4200 +++++++++++++++++++++ drivers/tty/isicom.c | 1736 +++++++++ drivers/tty/moxa.c | 2092 +++++++++++ drivers/tty/moxa.h | 304 ++ drivers/tty/mxser.c | 2757 ++++++++++++++ drivers/tty/mxser.h | 150 + drivers/tty/nozomi.c | 1993 ++++++++++ drivers/tty/rocket.c | 3199 ++++++++++++++++ drivers/tty/rocket.h | 111 + drivers/tty/rocket_int.h | 1214 ++++++ drivers/tty/synclink.c | 8119 +++++++++++++++++++++++++++++++++++++++++ drivers/tty/synclink_gt.c | 5161 ++++++++++++++++++++++++++ drivers/tty/synclinkmp.c | 5600 ++++++++++++++++++++++++++++ 34 files changed, 39364 insertions(+), 39363 deletions(-) delete mode 100644 drivers/char/amiserial.c delete mode 100644 drivers/char/bfin_jtag_comm.c delete mode 100644 drivers/char/cyclades.c delete mode 100644 drivers/char/isicom.c delete mode 100644 drivers/char/moxa.c delete mode 100644 drivers/char/moxa.h delete mode 100644 drivers/char/mxser.c delete mode 100644 drivers/char/mxser.h delete mode 100644 drivers/char/nozomi.c delete mode 100644 drivers/char/rocket.c delete mode 100644 drivers/char/rocket.h delete mode 100644 drivers/char/rocket_int.h delete mode 100644 drivers/char/synclink.c delete mode 100644 drivers/char/synclink_gt.c delete mode 100644 drivers/char/synclinkmp.c create mode 100644 drivers/tty/amiserial.c create mode 100644 drivers/tty/bfin_jtag_comm.c create mode 100644 drivers/tty/cyclades.c create mode 100644 drivers/tty/isicom.c create mode 100644 drivers/tty/moxa.c create mode 100644 drivers/tty/moxa.h create mode 100644 drivers/tty/mxser.c create mode 100644 drivers/tty/mxser.h create mode 100644 drivers/tty/nozomi.c create mode 100644 drivers/tty/rocket.c create mode 100644 drivers/tty/rocket.h create mode 100644 drivers/tty/rocket_int.h create mode 100644 drivers/tty/synclink.c create mode 100644 drivers/tty/synclink_gt.c create mode 100644 drivers/tty/synclinkmp.c (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 9b9ab867f50e..1adfac6a7b0b 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -15,36 +15,6 @@ config DEVKMEM kind of kernel debugging operations. When in doubt, say "N". -config BFIN_JTAG_COMM - tristate "Blackfin JTAG Communication" - depends on BLACKFIN - help - Add support for emulating a TTY device over the Blackfin JTAG. - - To compile this driver as a module, choose M here: the - module will be called bfin_jtag_comm. - -config BFIN_JTAG_COMM_CONSOLE - bool "Console on Blackfin JTAG" - depends on BFIN_JTAG_COMM=y - -config SERIAL_NONSTANDARD - bool "Non-standard serial port support" - depends on HAS_IOMEM - ---help--- - Say Y here if you have any non-standard serial boards -- boards - which aren't supported using the standard "dumb" serial driver. - This includes intelligent serial boards such as Cyclades, - Digiboards, etc. These are usually used for systems that need many - serial ports because they serve many terminals or dial-in - connections. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about non-standard serial boards. - - Most people can say N here. - config COMPUTONE tristate "Computone IntelliPort Plus serial support" depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) @@ -60,50 +30,6 @@ config COMPUTONE To compile this driver as module, choose M here: the module will be called ip2. -config ROCKETPORT - tristate "Comtrol RocketPort support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) - help - This driver supports Comtrol RocketPort and RocketModem PCI boards. - These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or - modems. For information about the RocketPort/RocketModem boards - and this driver read . - - To compile this driver as a module, choose M here: the - module will be called rocket. - - If you want to compile this driver into the kernel, say Y here. If - you don't have a Comtrol RocketPort/RocketModem card installed, say N. - -config CYCLADES - tristate "Cyclades async mux support" - depends on SERIAL_NONSTANDARD && (PCI || ISA) - select FW_LOADER - ---help--- - This driver supports Cyclades Z and Y multiserial boards. - You would need something like this to connect more than two modems to - your Linux box, for instance in order to become a dial-in server. - - For information about the Cyclades-Z card, read - . - - To compile this driver as a module, choose M here: the - module will be called cyclades. - - If you haven't heard about it, it's safe to say N. - -config CYZ_INTR - bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)" - depends on EXPERIMENTAL && CYCLADES - help - The Cyclades-Z family of multiport cards allows 2 (two) driver op - modes: polling and interrupt. In polling mode, the driver will check - the status of the Cyclades-Z ports every certain amount of time - (which is called polling cycle and is configurable). In interrupt - mode, it will use an interrupt line (IRQ) in order to check the - status of the Cyclades-Z ports. The default op mode is polling. If - unsure, say N. - config DIGIEPCA tristate "Digiboard Intelligent Async Support" depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) @@ -119,94 +45,6 @@ config DIGIEPCA To compile this driver as a module, choose M here: the module will be called epca. -config MOXA_INTELLIO - tristate "Moxa Intellio support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) - select FW_LOADER - help - Say Y here if you have a Moxa Intellio multiport serial card. - - To compile this driver as a module, choose M here: the - module will be called moxa. - -config MOXA_SMARTIO - tristate "Moxa SmartIO support v. 2.0" - depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) - help - Say Y here if you have a Moxa SmartIO multiport serial card and/or - want to help develop a new version of this driver. - - This is upgraded (1.9.1) driver from original Moxa drivers with - changes finally resulting in PCI probing. - - This driver can also be built as a module. The module will be called - mxser. If you want to do that, say M here. - -config ISI - tristate "Multi-Tech multiport card support (EXPERIMENTAL)" - depends on SERIAL_NONSTANDARD && PCI - select FW_LOADER - help - This is a driver for the Multi-Tech cards which provide several - serial ports. The driver is experimental and can currently only be - built as a module. The module will be called isicom. - If you want to do that, choose M here. - -config SYNCLINK - tristate "Microgate SyncLink card support" - depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API - help - Provides support for the SyncLink ISA and PCI multiprotocol serial - adapters. These adapters support asynchronous and HDLC bit - synchronous communication up to 10Mbps (PCI adapter). - - This driver can only be built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called synclink. If you want to do that, say M - here. - -config SYNCLINKMP - tristate "SyncLink Multiport support" - depends on SERIAL_NONSTANDARD && PCI - help - Enable support for the SyncLink Multiport (2 or 4 ports) - serial adapter, running asynchronous and HDLC communications up - to 2.048Mbps. Each ports is independently selectable for - RS-232, V.35, RS-449, RS-530, and X.21 - - This driver may be built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called synclinkmp. If you want to do that, say M - here. - -config SYNCLINK_GT - tristate "SyncLink GT/AC support" - depends on SERIAL_NONSTANDARD && PCI - help - Support for SyncLink GT and SyncLink AC families of - synchronous and asynchronous serial adapters - manufactured by Microgate Systems, Ltd. (www.microgate.com) - -config N_HDLC - tristate "HDLC line discipline support" - depends on SERIAL_NONSTANDARD - help - Allows synchronous HDLC communications with tty device drivers that - support synchronous HDLC such as the Microgate SyncLink adapter. - - This driver can be built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called n_hdlc. If you want to do that, say M - here. - -config N_GSM - tristate "GSM MUX line discipline support (EXPERIMENTAL)" - depends on EXPERIMENTAL - depends on NET - help - This line discipline provides support for the GSM MUX protocol and - presents the mux as a set of 61 individual tty devices. - config RISCOM8 tristate "SDL RISCom/8 card support" depends on SERIAL_NONSTANDARD @@ -296,16 +134,6 @@ config ISTALLION To compile this driver as a module, choose M here: the module will be called istallion. -config NOZOMI - tristate "HSDPA Broadband Wireless Data Card - Globe Trotter" - depends on PCI && EXPERIMENTAL - help - If you have a HSDPA driver Broadband Wireless Data Card - - Globe Trotter PCMCIA card, say Y here. - - To compile this driver as a module, choose M here, the module - will be called nozomi. - config A2232 tristate "Commodore A2232 serial support (EXPERIMENTAL)" depends on EXPERIMENTAL && ZORRO && BROKEN diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 5bc765d4c3ca..f5dc7c9bce6b 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -5,29 +5,18 @@ obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o -obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o -obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_SERIAL167) += serial167.o -obj-$(CONFIG_CYCLADES) += cyclades.o obj-$(CONFIG_STALLION) += stallion.o obj-$(CONFIG_ISTALLION) += istallion.o -obj-$(CONFIG_NOZOMI) += nozomi.o obj-$(CONFIG_DIGIEPCA) += epca.o obj-$(CONFIG_SPECIALIX) += specialix.o -obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o -obj-$(CONFIG_MOXA_SMARTIO) += mxser.o obj-$(CONFIG_COMPUTONE) += ip2/ obj-$(CONFIG_RISCOM8) += riscom8.o -obj-$(CONFIG_ISI) += isicom.o -obj-$(CONFIG_SYNCLINK) += synclink.o -obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o -obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o -obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_RAW_DRIVER) += raw.o diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c deleted file mode 100644 index f214e5022472..000000000000 --- a/drivers/char/amiserial.c +++ /dev/null @@ -1,2178 +0,0 @@ -/* - * linux/drivers/char/amiserial.c - * - * Serial driver for the amiga builtin port. - * - * This code was created by taking serial.c version 4.30 from kernel - * release 2.3.22, replacing all hardware related stuff with the - * corresponding amiga hardware actions, and removing all irrelevant - * code. As a consequence, it uses many of the constants and names - * associated with the registers and bits of 16550 compatible UARTS - - * but only to keep track of status, etc in the state variables. It - * was done this was to make it easier to keep the code in line with - * (non hardware specific) changes to serial.c. - * - * The port is registered with the tty driver as minor device 64, and - * therefore other ports should should only use 65 upwards. - * - * Richard Lucock 28/12/99 - * - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, - * 1998, 1999 Theodore Ts'o - * - */ - -/* - * Serial driver configuration section. Here are the various options: - * - * SERIAL_PARANOIA_CHECK - * Check the magic number for the async_structure where - * ever possible. - */ - -#include - -#undef SERIAL_PARANOIA_CHECK -#define SERIAL_DO_RESTART - -/* Set of debugging defines */ - -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - -/* Sanity checks */ - -#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) -#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s) -#else -#define DBG_CNT(s) -#endif - -/* - * End of serial driver configuration section. - */ - -#include - -#include -#include -#include -#include -static char *serial_version = "4.30"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include - -#define custom amiga_custom -static char *serial_name = "Amiga-builtin serial driver"; - -static struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -static struct async_struct *IRQ_ports; - -static unsigned char current_ctl_bits; - -static void change_speed(struct async_struct *info, struct ktermios *old); -static void rs_wait_until_sent(struct tty_struct *tty, int timeout); - - -static struct serial_state rs_table[1]; - -#define NR_PORTS ARRAY_SIZE(rs_table) - -#include - -#define serial_isroot() (capable(CAP_SYS_ADMIN)) - - -static inline int serial_paranoia_check(struct async_struct *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null async_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* some serial hardware definitions */ -#define SDR_OVRUN (1<<15) -#define SDR_RBF (1<<14) -#define SDR_TBE (1<<13) -#define SDR_TSRE (1<<12) - -#define SERPER_PARENB (1<<15) - -#define AC_SETCLR (1<<15) -#define AC_UARTBRK (1<<11) - -#define SER_DTR (1<<7) -#define SER_RTS (1<<6) -#define SER_DCD (1<<5) -#define SER_CTS (1<<4) -#define SER_DSR (1<<3) - -static __inline__ void rtsdtr_ctrl(int bits) -{ - ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR)); -} - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rs_stop(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - if (info->IER & UART_IER_THRI) { - info->IER &= ~UART_IER_THRI; - /* disable Tx interrupt and remove any pending interrupts */ - custom.intena = IF_TBE; - mb(); - custom.intreq = IF_TBE; - mb(); - } - local_irq_restore(flags); -} - -static void rs_start(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_start")) - return; - - local_irq_save(flags); - if (info->xmit.head != info->xmit.tail - && info->xmit.buf - && !(info->IER & UART_IER_THRI)) { - info->IER |= UART_IER_THRI; - custom.intena = IF_SETCLR | IF_TBE; - mb(); - /* set a pending Tx Interrupt, transmitter should restart now */ - custom.intreq = IF_SETCLR | IF_TBE; - mb(); - } - local_irq_restore(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static void rs_sched_event(struct async_struct *info, - int event) -{ - info->event |= 1 << event; - tasklet_schedule(&info->tlet); -} - -static void receive_chars(struct async_struct *info) -{ - int status; - int serdatr; - struct tty_struct *tty = info->tty; - unsigned char ch, flag; - struct async_icount *icount; - int oe = 0; - - icount = &info->state->icount; - - status = UART_LSR_DR; /* We obviously have a character! */ - serdatr = custom.serdatr; - mb(); - custom.intreq = IF_RBF; - mb(); - - if((serdatr & 0x1ff) == 0) - status |= UART_LSR_BI; - if(serdatr & SDR_OVRUN) - status |= UART_LSR_OE; - - ch = serdatr & 0xff; - icount->rx++; - -#ifdef SERIAL_DEBUG_INTR - printk("DR%02x:%02x...", ch, status); -#endif - flag = TTY_NORMAL; - - /* - * We don't handle parity or frame errors - but I have left - * the code in, since I'm not sure that the errors can't be - * detected. - */ - - if (status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE)) { - /* - * For statistics only - */ - if (status & UART_LSR_BI) { - status &= ~(UART_LSR_FE | UART_LSR_PE); - icount->brk++; - } else if (status & UART_LSR_PE) - icount->parity++; - else if (status & UART_LSR_FE) - icount->frame++; - if (status & UART_LSR_OE) - icount->overrun++; - - /* - * Now check to see if character should be - * ignored, and mask off conditions which - * should be ignored. - */ - if (status & info->ignore_status_mask) - goto out; - - status &= info->read_status_mask; - - if (status & (UART_LSR_BI)) { -#ifdef SERIAL_DEBUG_INTR - printk("handling break...."); -#endif - flag = TTY_BREAK; - if (info->flags & ASYNC_SAK) - do_SAK(tty); - } else if (status & UART_LSR_PE) - flag = TTY_PARITY; - else if (status & UART_LSR_FE) - flag = TTY_FRAME; - if (status & UART_LSR_OE) { - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - oe = 1; - } - } - tty_insert_flip_char(tty, ch, flag); - if (oe == 1) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_flip_buffer_push(tty); -out: - return; -} - -static void transmit_chars(struct async_struct *info) -{ - custom.intreq = IF_TBE; - mb(); - if (info->x_char) { - custom.serdat = info->x_char | 0x100; - mb(); - info->state->icount.tx++; - info->x_char = 0; - return; - } - if (info->xmit.head == info->xmit.tail - || info->tty->stopped - || info->tty->hw_stopped) { - info->IER &= ~UART_IER_THRI; - custom.intena = IF_TBE; - mb(); - return; - } - - custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100; - mb(); - info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1); - info->state->icount.tx++; - - if (CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - -#ifdef SERIAL_DEBUG_INTR - printk("THRE..."); -#endif - if (info->xmit.head == info->xmit.tail) { - custom.intena = IF_TBE; - mb(); - info->IER &= ~UART_IER_THRI; - } -} - -static void check_modem_status(struct async_struct *info) -{ - unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); - unsigned char dstatus; - struct async_icount *icount; - - /* Determine bits that have changed */ - dstatus = status ^ current_ctl_bits; - current_ctl_bits = status; - - if (dstatus) { - icount = &info->state->icount; - /* update input line counters */ - if (dstatus & SER_DSR) - icount->dsr++; - if (dstatus & SER_DCD) { - icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - !(status & SER_DCD)) - hardpps(); -#endif - } - if (dstatus & SER_CTS) - icount->cts++; - wake_up_interruptible(&info->delta_msr_wait); - } - - if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { -#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) - printk("ttyS%d CD now %s...", info->line, - (!(status & SER_DCD)) ? "on" : "off"); -#endif - if (!(status & SER_DCD)) - wake_up_interruptible(&info->open_wait); - else { -#ifdef SERIAL_DEBUG_OPEN - printk("doing serial hangup..."); -#endif - if (info->tty) - tty_hangup(info->tty); - } - } - if (info->flags & ASYNC_CTS_FLOW) { - if (info->tty->hw_stopped) { - if (!(status & SER_CTS)) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx start..."); -#endif - info->tty->hw_stopped = 0; - info->IER |= UART_IER_THRI; - custom.intena = IF_SETCLR | IF_TBE; - mb(); - /* set a pending Tx Interrupt, transmitter should restart now */ - custom.intreq = IF_SETCLR | IF_TBE; - mb(); - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - return; - } - } else { - if ((status & SER_CTS)) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx stop..."); -#endif - info->tty->hw_stopped = 1; - info->IER &= ~UART_IER_THRI; - /* disable Tx interrupt and remove any pending interrupts */ - custom.intena = IF_TBE; - mb(); - custom.intreq = IF_TBE; - mb(); - } - } - } -} - -static irqreturn_t ser_vbl_int( int irq, void *data) -{ - /* vbl is just a periodic interrupt we tie into to update modem status */ - struct async_struct * info = IRQ_ports; - /* - * TBD - is it better to unregister from this interrupt or to - * ignore it if MSI is clear ? - */ - if(info->IER & UART_IER_MSI) - check_modem_status(info); - return IRQ_HANDLED; -} - -static irqreturn_t ser_rx_int(int irq, void *dev_id) -{ - struct async_struct * info; - -#ifdef SERIAL_DEBUG_INTR - printk("ser_rx_int..."); -#endif - - info = IRQ_ports; - if (!info || !info->tty) - return IRQ_NONE; - - receive_chars(info); - info->last_active = jiffies; -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif - return IRQ_HANDLED; -} - -static irqreturn_t ser_tx_int(int irq, void *dev_id) -{ - struct async_struct * info; - - if (custom.serdatr & SDR_TBE) { -#ifdef SERIAL_DEBUG_INTR - printk("ser_tx_int..."); -#endif - - info = IRQ_ports; - if (!info || !info->tty) - return IRQ_NONE; - - transmit_chars(info); - info->last_active = jiffies; -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif - } - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------- - * Here ends the serial 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_softint(unsigned long private_) -{ - struct async_struct *info = (struct async_struct *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - -/* - * --------------------------------------------------------------- - * Low level utility subroutines for the serial driver: routines to - * figure out the appropriate timeout for an interrupt chain, routines - * to initialize and startup a serial port, and routines to shutdown a - * serial port. Useful stuff like that. - * --------------------------------------------------------------- - */ - -static int startup(struct async_struct * info) -{ - unsigned long flags; - int retval=0; - unsigned long page; - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - local_irq_save(flags); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - goto errout; - } - - if (info->xmit.buf) - free_page(page); - else - info->xmit.buf = (unsigned char *) page; - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d ...", info->line); -#endif - - /* Clear anything in the input buffer */ - - custom.intreq = IF_RBF; - mb(); - - retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info); - if (retval) { - if (serial_isroot()) { - if (info->tty) - set_bit(TTY_IO_ERROR, - &info->tty->flags); - retval = 0; - } - goto errout; - } - - /* enable both Rx and Tx interrupts */ - custom.intena = IF_SETCLR | IF_RBF | IF_TBE; - mb(); - info->IER = UART_IER_MSI; - - /* remember current state of the DCD and CTS bits */ - current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); - - IRQ_ports = info; - - info->MCR = 0; - if (info->tty->termios->c_cflag & CBAUD) - info->MCR = SER_DTR | SER_RTS; - rtsdtr_ctrl(info->MCR); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit.head = info->xmit.tail = 0; - - /* - * Set up the tty->alt_speed kludge - */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - } - - /* - * and set the speed of the serial port - */ - change_speed(info, NULL); - - info->flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; - -errout: - local_irq_restore(flags); - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct async_struct * info) -{ - unsigned long flags; - struct serial_state *state; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - state = info->state; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d ....\n", info->line); -#endif - - local_irq_save(flags); /* Disable interrupts */ - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be waken up - */ - wake_up_interruptible(&info->delta_msr_wait); - - IRQ_ports = NULL; - - /* - * Free the IRQ, if necessary - */ - free_irq(IRQ_AMIGA_VERTB, info); - - if (info->xmit.buf) { - free_page((unsigned long) info->xmit.buf); - info->xmit.buf = NULL; - } - - info->IER = 0; - custom.intena = IF_RBF | IF_TBE; - mb(); - - /* disable break condition */ - custom.adkcon = AC_UARTBRK; - mb(); - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - info->MCR &= ~(SER_DTR|SER_RTS); - rtsdtr_ctrl(info->MCR); - - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(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 async_struct *info, - struct ktermios *old_termios) -{ - int quot = 0, baud_base, baud; - unsigned cflag, cval = 0; - int bits; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return; - cflag = info->tty->termios->c_cflag; - - /* Byte size is always 8 bits plus parity bit if requested */ - - cval = 3; bits = 10; - if (cflag & CSTOPB) { - cval |= 0x04; - bits++; - } - if (cflag & PARENB) { - cval |= UART_LCR_PARITY; - bits++; - } - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(info->tty); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ - baud_base = info->state->baud_base; - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; - else { - if (baud == 134) - /* Special case since 134 is really 134.5 */ - quot = (2*baud_base / 269); - else if (baud) - quot = baud_base / baud; - } - /* If the quotient is zero refuse the change */ - if (!quot && old_termios) { - /* FIXME: Will need updating for new tty in the end */ - info->tty->termios->c_cflag &= ~CBAUD; - info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); - baud = tty_get_baud_rate(info->tty); - if (!baud) - baud = 9600; - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; - else { - if (baud == 134) - /* Special case since 134 is really 134.5 */ - quot = (2*baud_base / 269); - else if (baud) - quot = baud_base / baud; - } - } - /* As a last resort, if the quotient is zero, default to 9600 bps */ - if (!quot) - quot = baud_base / 9600; - info->quot = quot; - info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - if (info->flags & ASYNC_HARDPPS_CD) - info->IER |= UART_IER_MSI; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - } else - info->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - /* TBD: - * Does clearing IER_MSI imply that we should disable the VBL interrupt ? - */ - - /* - * Set up parity check flag - */ - - info->read_status_mask = UART_LSR_OE | UART_LSR_DR; - if (I_INPCK(info->tty)) - info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) - info->read_status_mask |= UART_LSR_BI; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (I_IGNBRK(info->tty)) { - info->ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= UART_LSR_OE; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - info->ignore_status_mask |= UART_LSR_DR; - local_irq_save(flags); - - { - short serper; - - /* Set up the baud rate */ - serper = quot - 1; - - /* Enable or disable parity bit */ - - if(cval & UART_LCR_PARITY) - serper |= (SERPER_PARENB); - - custom.serper = serper; - mb(); - } - - info->LCR = cval; /* Save LCR */ - local_irq_restore(flags); -} - -static int rs_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct async_struct *info; - unsigned long flags; - - info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return 0; - - if (!info->xmit.buf) - return 0; - - local_irq_save(flags); - if (CIRC_SPACE(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) == 0) { - local_irq_restore(flags); - return 0; - } - - info->xmit.buf[info->xmit.head++] = ch; - info->xmit.head &= SERIAL_XMIT_SIZE-1; - local_irq_restore(flags); - return 1; -} - -static void rs_flush_chars(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) - return; - - if (info->xmit.head == info->xmit.tail - || tty->stopped - || tty->hw_stopped - || !info->xmit.buf) - return; - - local_irq_save(flags); - info->IER |= UART_IER_THRI; - custom.intena = IF_SETCLR | IF_TBE; - mb(); - /* set a pending Tx Interrupt, transmitter should restart now */ - custom.intreq = IF_SETCLR | IF_TBE; - mb(); - local_irq_restore(flags); -} - -static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count) -{ - int c, ret = 0; - struct async_struct *info; - unsigned long flags; - - info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!info->xmit.buf) - return 0; - - local_irq_save(flags); - while (1) { - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) { - break; - } - memcpy(info->xmit.buf + info->xmit.head, buf, c); - info->xmit.head = ((info->xmit.head + c) & - (SERIAL_XMIT_SIZE-1)); - buf += c; - count -= c; - ret += c; - } - local_irq_restore(flags); - - if (info->xmit.head != info->xmit.tail - && !tty->stopped - && !tty->hw_stopped - && !(info->IER & UART_IER_THRI)) { - info->IER |= UART_IER_THRI; - local_irq_disable(); - custom.intena = IF_SETCLR | IF_TBE; - mb(); - /* set a pending Tx Interrupt, transmitter should restart now */ - custom.intreq = IF_SETCLR | IF_TBE; - mb(); - local_irq_restore(flags); - } - return ret; -} - -static int rs_write_room(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -static int rs_chars_in_buffer(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -static void rs_flush_buffer(struct tty_struct *tty) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - local_irq_save(flags); - info->xmit.head = info->xmit.tail = 0; - local_irq_restore(flags); - tty_wakeup(tty); -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void rs_send_xchar(struct tty_struct *tty, char ch) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_send_char")) - return; - - info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - - /* Check this ! */ - local_irq_save(flags); - if(!(custom.intenar & IF_TBE)) { - custom.intena = IF_SETCLR | IF_TBE; - mb(); - /* set a pending Tx Interrupt, transmitter should restart now */ - custom.intreq = IF_SETCLR | IF_TBE; - mb(); - } - local_irq_restore(flags); - - info->IER |= UART_IER_THRI; - } -} - -/* - * ------------------------------------------------------------ - * 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 async_struct *info = 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->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) - rs_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) - info->MCR &= ~SER_RTS; - - local_irq_save(flags); - rtsdtr_ctrl(info->MCR); - local_irq_restore(flags); -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct async_struct *info = 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->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - rs_send_xchar(tty, START_CHAR(tty)); - } - if (tty->termios->c_cflag & CRTSCTS) - info->MCR |= SER_RTS; - local_irq_save(flags); - rtsdtr_ctrl(info->MCR); - local_irq_restore(flags); -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct async_struct * info, - struct serial_struct __user * retinfo) -{ - struct serial_struct tmp; - struct serial_state *state = info->state; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tty_lock(); - tmp.type = state->type; - tmp.line = state->line; - tmp.port = state->port; - tmp.irq = state->irq; - tmp.flags = state->flags; - tmp.xmit_fifo_size = state->xmit_fifo_size; - tmp.baud_base = state->baud_base; - tmp.close_delay = state->close_delay; - tmp.closing_wait = state->closing_wait; - tmp.custom_divisor = state->custom_divisor; - tty_unlock(); - if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct async_struct * info, - struct serial_struct __user * new_info) -{ - struct serial_struct new_serial; - struct serial_state old_state, *state; - unsigned int change_irq,change_port; - int retval = 0; - - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) - return -EFAULT; - - tty_lock(); - state = info->state; - old_state = *state; - - change_irq = new_serial.irq != state->irq; - change_port = (new_serial.port != state->port); - if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) { - tty_unlock(); - return -EINVAL; - } - - if (!serial_isroot()) { - if ((new_serial.baud_base != state->baud_base) || - (new_serial.close_delay != state->close_delay) || - (new_serial.xmit_fifo_size != state->xmit_fifo_size) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (state->flags & ~ASYNC_USR_MASK))) - return -EPERM; - state->flags = ((state->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - state->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if (new_serial.baud_base < 9600) { - tty_unlock(); - return -EINVAL; - } - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - state->baud_base = new_serial.baud_base; - state->flags = ((state->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | - (info->flags & ASYNC_INTERNAL_FLAGS)); - state->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ/100; - state->closing_wait = new_serial.closing_wait * HZ/100; - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - -check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { - if (((old_state.flags & ASYNC_SPD_MASK) != - (state->flags & ASYNC_SPD_MASK)) || - (old_state.custom_divisor != state->custom_divisor)) { - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - change_speed(info, NULL); - } - } else - retval = startup(info); - tty_unlock(); - 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 async_struct * info, unsigned int __user *value) -{ - unsigned char status; - unsigned int result; - unsigned long flags; - - local_irq_save(flags); - status = custom.serdatr; - mb(); - local_irq_restore(flags); - result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - - -static int rs_tiocmget(struct tty_struct *tty) -{ - struct async_struct * info = tty->driver_data; - unsigned char control, status; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - control = info->MCR; - local_irq_save(flags); - status = ciab.pra; - local_irq_restore(flags); - return ((control & SER_RTS) ? TIOCM_RTS : 0) - | ((control & SER_DTR) ? TIOCM_DTR : 0) - | (!(status & SER_DCD) ? TIOCM_CAR : 0) - | (!(status & SER_DSR) ? TIOCM_DSR : 0) - | (!(status & SER_CTS) ? TIOCM_CTS : 0); -} - -static int rs_tiocmset(struct tty_struct *tty, unsigned int set, - unsigned int clear) -{ - struct async_struct * info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - local_irq_save(flags); - if (set & TIOCM_RTS) - info->MCR |= SER_RTS; - if (set & TIOCM_DTR) - info->MCR |= SER_DTR; - if (clear & TIOCM_RTS) - info->MCR &= ~SER_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~SER_DTR; - rtsdtr_ctrl(info->MCR); - local_irq_restore(flags); - return 0; -} - -/* - * rs_break() --- routine which turns the break handling on or off - */ -static int rs_break(struct tty_struct *tty, int break_state) -{ - struct async_struct * info = tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_break")) - return -EINVAL; - - local_irq_save(flags); - if (break_state == -1) - custom.adkcon = AC_SETCLR | AC_UARTBRK; - else - custom.adkcon = AC_UARTBRK; - mb(); - local_irq_restore(flags); - return 0; -} - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int rs_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct async_struct *info = tty->driver_data; - struct async_icount cnow; - unsigned long flags; - - local_irq_save(flags); - cnow = info->state->icount; - local_irq_restore(flags); - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - -static int rs_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct async_struct * info = tty->driver_data; - struct async_icount cprev, cnow; /* kernel counter temps */ - void __user *argp = (void __user *)arg; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(info, argp); - case TIOCSSERIAL: - return set_serial_info(info, argp); - case TIOCSERCONFIG: - return 0; - - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, argp); - - case TIOCSERGSTRUCT: - if (copy_to_user(argp, - info, sizeof(struct async_struct))) - return -EFAULT; - return 0; - - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - local_irq_save(flags); - /* note the counters on entry */ - cprev = info->state->icount; - local_irq_restore(flags); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - local_irq_save(flags); - cnow = info->state->icount; /* atomic copy */ - local_irq_restore(flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - - case TIOCSERGWILD: - case TIOCSERSWILD: - /* "setserial -W" is called in Debian boot */ - printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); - return 0; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct async_struct *info = tty->driver_data; - unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; - - change_speed(info, old_termios); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(cflag & CBAUD)) { - info->MCR &= ~(SER_DTR|SER_RTS); - local_irq_save(flags); - rtsdtr_ctrl(info->MCR); - local_irq_restore(flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (cflag & CBAUD)) { - info->MCR |= SER_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { - info->MCR |= SER_RTS; - } - local_irq_save(flags); - rtsdtr_ctrl(info->MCR); - local_irq_restore(flags); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_start(tty); - } - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_close(struct tty_struct *tty, struct file * filp) -{ - struct async_struct * info = tty->driver_data; - struct serial_state *state; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) - return; - - state = info->state; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - DBG_CNT("before DEC-hung"); - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, state->count); -#endif - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->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, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); - state->count = 0; - } - if (state->count) { - DBG_CNT("before DEC-2"); - local_irq_restore(flags); - return; - } - info->flags |= ASYNC_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != ASYNC_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, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->read_status_mask &= ~UART_LSR_DR; - if (info->flags & ASYNC_INITIALIZED) { - /* disable receive interrupts */ - custom.intena = IF_RBF; - mb(); - /* clear any pending receive interrupt */ - custom.intreq = IF_RBF; - mb(); - - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - rs_wait_until_sent(tty, info->timeout); - } - shutdown(info); - rs_flush_buffer(tty); - - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct async_struct * info = tty->driver_data; - unsigned long orig_jiffies, char_time; - int tty_was_locked = tty_locked(); - int lsr; - - if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - orig_jiffies = jiffies; - - /* - * tty_wait_until_sent is called from lots of places, - * with or without the BTM. - */ - if (!tty_was_locked) - tty_lock(); - /* - * 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. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - 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_t(unsigned long, char_time, timeout); - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2*info->timeout) - timeout = 2*info->timeout; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - while(!((lsr = custom.serdatr) & SDR_TSRE)) { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("serdatr = %d (jiff=%lu)...", lsr, jiffies); -#endif - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - __set_current_state(TASK_RUNNING); - if (!tty_was_locked) - tty_unlock(); -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rs_hangup(struct tty_struct *tty) -{ - struct async_struct * info = tty->driver_data; - struct serial_state *state = info->state; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - state = info->state; - - rs_flush_buffer(tty); - shutdown(info); - info->event = 0; - state->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct async_struct *info) -{ -#ifdef DECLARE_WAITQUEUE - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue wait = { current, NULL }; -#endif - struct serial_state *state = info->state; - int retval; - int do_clocal = 0, extra_count = 0; - unsigned long flags; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif - } - - /* - * 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))) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - 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, state->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", - state->line, state->count); -#endif - local_irq_save(flags); - if (!tty_hung_up_p(filp)) { - extra_count = 1; - state->count--; - } - local_irq_restore(flags); - info->blocked_open++; - while (1) { - local_irq_save(flags); - if (tty->termios->c_cflag & CBAUD) - rtsdtr_ctrl(SER_DTR|SER_RTS); - local_irq_restore(flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || (!(ciab.pra & SER_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, state->count); -#endif - tty_unlock(); - schedule(); - tty_lock(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (extra_count) - state->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int get_async_struct(int line, struct async_struct **ret_info) -{ - struct async_struct *info; - struct serial_state *sstate; - - sstate = rs_table + line; - sstate->count++; - if (sstate->info) { - *ret_info = sstate->info; - return 0; - } - info = kzalloc(sizeof(struct async_struct), GFP_KERNEL); - if (!info) { - sstate->count--; - return -ENOMEM; - } -#ifdef DECLARE_WAITQUEUE - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); -#endif - info->magic = SERIAL_MAGIC; - info->port = sstate->port; - info->flags = sstate->flags; - info->xmit_fifo_size = sstate->xmit_fifo_size; - info->line = line; - tasklet_init(&info->tlet, do_softint, (unsigned long)info); - info->state = sstate; - if (sstate->info) { - kfree(info); - *ret_info = sstate->info; - return 0; - } - *ret_info = sstate->info = info; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int rs_open(struct tty_struct *tty, struct file * filp) -{ - struct async_struct *info; - int retval, line; - - line = tty->index; - if ((line < 0) || (line >= NR_PORTS)) { - return -ENODEV; - } - retval = get_async_struct(line, &info); - if (retval) { - return retval; - } - tty->driver_data = info; - info->tty = tty; - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s, count = %d\n", tty->name, info->state->count); -#endif - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - /* - * If the port is the middle of closing, bail out now - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_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; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s successful...", tty->name); -#endif - return 0; -} - -/* - * /proc fs routines.... - */ - -static inline void line_info(struct seq_file *m, struct serial_state *state) -{ - struct async_struct *info = state->info, scr_info; - char stat_buf[30], control, status; - unsigned long flags; - - seq_printf(m, "%d: uart:amiga_builtin",state->line); - - /* - * Figure out the current RS-232 lines - */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ - - info->magic = SERIAL_MAGIC; - info->flags = state->flags; - info->quot = 0; - info->tty = NULL; - } - local_irq_save(flags); - status = ciab.pra; - control = info ? info->MCR : status; - local_irq_restore(flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if(!(control & SER_RTS)) - strcat(stat_buf, "|RTS"); - if(!(status & SER_CTS)) - strcat(stat_buf, "|CTS"); - if(!(control & SER_DTR)) - strcat(stat_buf, "|DTR"); - if(!(status & SER_DSR)) - strcat(stat_buf, "|DSR"); - if(!(status & SER_DCD)) - strcat(stat_buf, "|CD"); - - if (info->quot) { - seq_printf(m, " baud:%d", state->baud_base / info->quot); - } - - seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx); - - if (state->icount.frame) - seq_printf(m, " fe:%d", state->icount.frame); - - if (state->icount.parity) - seq_printf(m, " pe:%d", state->icount.parity); - - if (state->icount.brk) - seq_printf(m, " brk:%d", state->icount.brk); - - if (state->icount.overrun) - seq_printf(m, " oe:%d", state->icount.overrun); - - /* - * Last thing is the RS-232 status lines - */ - seq_printf(m, " %s\n", stat_buf+1); -} - -static int rs_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); - line_info(m, &rs_table[0]); - return 0; -} - -static int rs_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, rs_proc_show, NULL); -} - -static const struct file_operations rs_proc_fops = { - .owner = THIS_MODULE, - .open = rs_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* - * --------------------------------------------------------------------- - * rs_init() and friends - * - * rs_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static void show_serial_version(void) -{ - printk(KERN_INFO "%s version %s\n", serial_name, serial_version); -} - - -static const struct tty_operations serial_ops = { - .open = rs_open, - .close = rs_close, - .write = rs_write, - .put_char = rs_put_char, - .flush_chars = rs_flush_chars, - .write_room = rs_write_room, - .chars_in_buffer = rs_chars_in_buffer, - .flush_buffer = rs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = rs_set_termios, - .stop = rs_stop, - .start = rs_start, - .hangup = rs_hangup, - .break_ctl = rs_break, - .send_xchar = rs_send_xchar, - .wait_until_sent = rs_wait_until_sent, - .tiocmget = rs_tiocmget, - .tiocmset = rs_tiocmset, - .get_icount = rs_get_icount, - .proc_fops = &rs_proc_fops, -}; - -/* - * The serial driver boot-time initialization code! - */ -static int __init amiga_serial_probe(struct platform_device *pdev) -{ - unsigned long flags; - struct serial_state * state; - int error; - - serial_driver = alloc_tty_driver(1); - if (!serial_driver) - return -ENOMEM; - - IRQ_ports = NULL; - - show_serial_version(); - - /* Initialize the tty_driver structure */ - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = "amiserial"; - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - 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; - tty_set_operations(serial_driver, &serial_ops); - - error = tty_register_driver(serial_driver); - if (error) - goto fail_put_tty_driver; - - state = rs_table; - state->magic = SSTATE_MAGIC; - state->port = (int)&custom.serdatr; /* Just to give it a value */ - state->line = 0; - state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; - state->icount.cts = state->icount.dsr = - state->icount.rng = state->icount.dcd = 0; - state->icount.rx = state->icount.tx = 0; - state->icount.frame = state->icount.parity = 0; - state->icount.overrun = state->icount.brk = 0; - - printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", - state->line); - - /* Hardware set up */ - - state->baud_base = amiga_colorclock; - state->xmit_fifo_size = 1; - - /* set ISRs, and then disable the rx interrupts */ - error = request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state); - if (error) - goto fail_unregister; - - error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED, - "serial RX", state); - if (error) - goto fail_free_irq; - - local_irq_save(flags); - - /* turn off Rx and Tx interrupts */ - custom.intena = IF_RBF | IF_TBE; - mb(); - - /* clear any pending interrupt */ - custom.intreq = IF_RBF | IF_TBE; - mb(); - - local_irq_restore(flags); - - /* - * set the appropriate directions for the modem control flags, - * and clear RTS and DTR - */ - ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ - ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ - - platform_set_drvdata(pdev, state); - - return 0; - -fail_free_irq: - free_irq(IRQ_AMIGA_TBE, state); -fail_unregister: - tty_unregister_driver(serial_driver); -fail_put_tty_driver: - put_tty_driver(serial_driver); - return error; -} - -static int __exit amiga_serial_remove(struct platform_device *pdev) -{ - int error; - struct serial_state *state = platform_get_drvdata(pdev); - struct async_struct *info = state->info; - - /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - tasklet_kill(&info->tlet); - if ((error = tty_unregister_driver(serial_driver))) - printk("SERIAL: failed to unregister serial driver (%d)\n", - error); - put_tty_driver(serial_driver); - - rs_table[0].info = NULL; - kfree(info); - - free_irq(IRQ_AMIGA_TBE, rs_table); - free_irq(IRQ_AMIGA_RBF, rs_table); - - platform_set_drvdata(pdev, NULL); - - return error; -} - -static struct platform_driver amiga_serial_driver = { - .remove = __exit_p(amiga_serial_remove), - .driver = { - .name = "amiga-serial", - .owner = THIS_MODULE, - }, -}; - -static int __init amiga_serial_init(void) -{ - return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); -} - -module_init(amiga_serial_init); - -static void __exit amiga_serial_exit(void) -{ - platform_driver_unregister(&amiga_serial_driver); -} - -module_exit(amiga_serial_exit); - - -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) - -/* - * ------------------------------------------------------------ - * Serial console driver - * ------------------------------------------------------------ - */ - -static void amiga_serial_putc(char c) -{ - custom.serdat = (unsigned char)c | 0x100; - while (!(custom.serdatr & 0x2000)) - barrier(); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console must be locked when we get here. - */ -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - unsigned short intena = custom.intenar; - - custom.intena = IF_TBE; - - while (count--) { - if (*s == '\n') - amiga_serial_putc('\r'); - amiga_serial_putc(*s++); - } - - custom.intena = IF_SETCLR | (intena & IF_TBE); -} - -static struct tty_driver *serial_console_device(struct console *c, int *index) -{ - *index = 0; - return serial_driver; -} - -static struct console sercons = { - .name = "ttyS", - .write = serial_console_write, - .device = serial_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Register console. - */ -static int __init amiserial_console_init(void) -{ - register_console(&sercons); - return 0; -} -console_initcall(amiserial_console_init); - -#endif /* CONFIG_SERIAL_CONSOLE && !MODULE */ - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:amiga-serial"); diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c deleted file mode 100644 index 16402445f2b2..000000000000 --- a/drivers/char/bfin_jtag_comm.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * TTY over Blackfin JTAG Communication - * - * Copyright 2008-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#define DRV_NAME "bfin-jtag-comm" -#define DEV_NAME "ttyBFJC" -#define pr_fmt(fmt) DRV_NAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) - -/* See the Debug/Emulation chapter in the HRM */ -#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */ -#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */ -#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */ -#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */ - -static inline uint32_t bfin_write_emudat(uint32_t emudat) -{ - __asm__ __volatile__("emudat = %0;" : : "d"(emudat)); - return emudat; -} - -static inline uint32_t bfin_read_emudat(void) -{ - uint32_t emudat; - __asm__ __volatile__("%0 = emudat;" : "=d"(emudat)); - return emudat; -} - -static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d) -{ - return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24)); -} - -#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */ -#define CIRC_MASK (CIRC_SIZE - 1) -#define circ_empty(circ) ((circ)->head == (circ)->tail) -#define circ_free(circ) CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE) -#define circ_cnt(circ) CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE) -#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK]) - -static struct tty_driver *bfin_jc_driver; -static struct task_struct *bfin_jc_kthread; -static struct tty_struct * volatile bfin_jc_tty; -static unsigned long bfin_jc_count; -static DEFINE_MUTEX(bfin_jc_tty_mutex); -static volatile struct circ_buf bfin_jc_write_buf; - -static int -bfin_jc_emudat_manager(void *arg) -{ - uint32_t inbound_len = 0, outbound_len = 0; - - while (!kthread_should_stop()) { - /* no one left to give data to, so sleep */ - if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) { - pr_debug("waiting for readers\n"); - __set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - __set_current_state(TASK_RUNNING); - } - - /* no data available, so just chill */ - if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) { - pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n", - inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head); - if (inbound_len) - schedule(); - else - schedule_timeout_interruptible(HZ); - continue; - } - - /* if incoming data is ready, eat it */ - if (bfin_read_DBGSTAT() & EMUDIF) { - struct tty_struct *tty; - mutex_lock(&bfin_jc_tty_mutex); - tty = (struct tty_struct *)bfin_jc_tty; - if (tty != NULL) { - uint32_t emudat = bfin_read_emudat(); - if (inbound_len == 0) { - pr_debug("incoming length: 0x%08x\n", emudat); - inbound_len = emudat; - } else { - size_t num_chars = (4 <= inbound_len ? 4 : inbound_len); - pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); - inbound_len -= num_chars; - tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars); - tty_flip_buffer_push(tty); - } - } - mutex_unlock(&bfin_jc_tty_mutex); - } - - /* if outgoing data is ready, post it */ - if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) { - if (outbound_len == 0) { - outbound_len = circ_cnt(&bfin_jc_write_buf); - bfin_write_emudat(outbound_len); - pr_debug("outgoing length: 0x%08x\n", outbound_len); - } else { - struct tty_struct *tty; - int tail = bfin_jc_write_buf.tail; - size_t ate = (4 <= outbound_len ? 4 : outbound_len); - uint32_t emudat = - bfin_write_emudat_chars( - circ_byte(&bfin_jc_write_buf, tail + 0), - circ_byte(&bfin_jc_write_buf, tail + 1), - circ_byte(&bfin_jc_write_buf, tail + 2), - circ_byte(&bfin_jc_write_buf, tail + 3) - ); - bfin_jc_write_buf.tail += ate; - outbound_len -= ate; - mutex_lock(&bfin_jc_tty_mutex); - tty = (struct tty_struct *)bfin_jc_tty; - if (tty) - tty_wakeup(tty); - mutex_unlock(&bfin_jc_tty_mutex); - pr_debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate); - } - } - } - - __set_current_state(TASK_RUNNING); - return 0; -} - -static int -bfin_jc_open(struct tty_struct *tty, struct file *filp) -{ - mutex_lock(&bfin_jc_tty_mutex); - pr_debug("open %lu\n", bfin_jc_count); - ++bfin_jc_count; - bfin_jc_tty = tty; - wake_up_process(bfin_jc_kthread); - mutex_unlock(&bfin_jc_tty_mutex); - return 0; -} - -static void -bfin_jc_close(struct tty_struct *tty, struct file *filp) -{ - mutex_lock(&bfin_jc_tty_mutex); - pr_debug("close %lu\n", bfin_jc_count); - if (--bfin_jc_count == 0) - bfin_jc_tty = NULL; - wake_up_process(bfin_jc_kthread); - mutex_unlock(&bfin_jc_tty_mutex); -} - -/* XXX: we dont handle the put_char() case where we must handle count = 1 */ -static int -bfin_jc_circ_write(const unsigned char *buf, int count) -{ - int i; - count = min(count, circ_free(&bfin_jc_write_buf)); - pr_debug("going to write chunk of %i bytes\n", count); - for (i = 0; i < count; ++i) - circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i]; - bfin_jc_write_buf.head += i; - return i; -} - -#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE -# define console_lock() -# define console_unlock() -#endif -static int -bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - int i; - console_lock(); - i = bfin_jc_circ_write(buf, count); - console_unlock(); - wake_up_process(bfin_jc_kthread); - return i; -} - -static void -bfin_jc_flush_chars(struct tty_struct *tty) -{ - wake_up_process(bfin_jc_kthread); -} - -static int -bfin_jc_write_room(struct tty_struct *tty) -{ - return circ_free(&bfin_jc_write_buf); -} - -static int -bfin_jc_chars_in_buffer(struct tty_struct *tty) -{ - return circ_cnt(&bfin_jc_write_buf); -} - -static void -bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout) -{ - unsigned long expire = jiffies + timeout; - while (!circ_empty(&bfin_jc_write_buf)) { - if (signal_pending(current)) - break; - if (time_after(jiffies, expire)) - break; - } -} - -static const struct tty_operations bfin_jc_ops = { - .open = bfin_jc_open, - .close = bfin_jc_close, - .write = bfin_jc_write, - /*.put_char = bfin_jc_put_char,*/ - .flush_chars = bfin_jc_flush_chars, - .write_room = bfin_jc_write_room, - .chars_in_buffer = bfin_jc_chars_in_buffer, - .wait_until_sent = bfin_jc_wait_until_sent, -}; - -static int __init bfin_jc_init(void) -{ - int ret; - - bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME); - if (IS_ERR(bfin_jc_kthread)) - return PTR_ERR(bfin_jc_kthread); - - ret = -ENOMEM; - - bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0; - bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL); - if (!bfin_jc_write_buf.buf) - goto err; - - bfin_jc_driver = alloc_tty_driver(1); - if (!bfin_jc_driver) - goto err; - - bfin_jc_driver->owner = THIS_MODULE; - bfin_jc_driver->driver_name = DRV_NAME; - bfin_jc_driver->name = DEV_NAME; - bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL; - bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL; - bfin_jc_driver->init_termios = tty_std_termios; - tty_set_operations(bfin_jc_driver, &bfin_jc_ops); - - ret = tty_register_driver(bfin_jc_driver); - if (ret) - goto err; - - pr_init(KERN_INFO DRV_NAME ": initialized\n"); - - return 0; - - err: - put_tty_driver(bfin_jc_driver); - kfree(bfin_jc_write_buf.buf); - kthread_stop(bfin_jc_kthread); - return ret; -} -module_init(bfin_jc_init); - -static void __exit bfin_jc_exit(void) -{ - kthread_stop(bfin_jc_kthread); - kfree(bfin_jc_write_buf.buf); - tty_unregister_driver(bfin_jc_driver); - put_tty_driver(bfin_jc_driver); -} -module_exit(bfin_jc_exit); - -#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK) -static void -bfin_jc_straight_buffer_write(const char *buf, unsigned count) -{ - unsigned ate = 0; - while (bfin_read_DBGSTAT() & EMUDOF) - continue; - bfin_write_emudat(count); - while (ate < count) { - while (bfin_read_DBGSTAT() & EMUDOF) - continue; - bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]); - ate += 4; - } -} -#endif - -#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE -static void -bfin_jc_console_write(struct console *co, const char *buf, unsigned count) -{ - if (bfin_jc_kthread == NULL) - bfin_jc_straight_buffer_write(buf, count); - else - bfin_jc_circ_write(buf, count); -} - -static struct tty_driver * -bfin_jc_console_device(struct console *co, int *index) -{ - *index = co->index; - return bfin_jc_driver; -} - -static struct console bfin_jc_console = { - .name = DEV_NAME, - .write = bfin_jc_console_write, - .device = bfin_jc_console_device, - .flags = CON_ANYTIME | CON_PRINTBUFFER, - .index = -1, -}; - -static int __init bfin_jc_console_init(void) -{ - register_console(&bfin_jc_console); - return 0; -} -console_initcall(bfin_jc_console_init); -#endif - -#ifdef CONFIG_EARLY_PRINTK -static void __init -bfin_jc_early_write(struct console *co, const char *buf, unsigned int count) -{ - bfin_jc_straight_buffer_write(buf, count); -} - -static struct __initdata console bfin_jc_early_console = { - .name = "early_BFJC", - .write = bfin_jc_early_write, - .flags = CON_ANYTIME | CON_PRINTBUFFER, - .index = -1, -}; - -struct console * __init -bfin_jc_early_init(unsigned int port, unsigned int cflag) -{ - return &bfin_jc_early_console; -} -#endif - -MODULE_AUTHOR("Mike Frysinger "); -MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c deleted file mode 100644 index c99728f0cd9f..000000000000 --- a/drivers/char/cyclades.c +++ /dev/null @@ -1,4200 +0,0 @@ -#undef BLOCKMOVE -#define Z_WAKE -#undef Z_EXT_CHARS_IN_BUFFER - -/* - * linux/drivers/char/cyclades.c - * - * This file contains the driver for the Cyclades async multiport - * serial boards. - * - * Initially written by Randolph Bentson . - * Modified and maintained by Marcio Saito . - * - * Copyright (C) 2007-2009 Jiri Slaby - * - * Much of the design and some of the code came from serial.c - * which was copyright (C) 1991, 1992 Linus Torvalds. It was - * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, - * and then fixed as suggested by Michael K. Johnson 12/12/92. - * Converted to pci probing and cleaned up by Jiri Slaby. - * - */ - -#define CY_VERSION "2.6" - -/* If you need to install more boards than NR_CARDS, change the constant - in the definition below. No other change is necessary to support up to - eight boards. Beyond that you'll have to extend cy_isa_addresses. */ - -#define NR_CARDS 4 - -/* - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 256 - -#define ZO_V1 0 -#define ZO_V2 1 -#define ZE_V1 2 - -#define SERIAL_PARANOIA_CHECK -#undef CY_DEBUG_OPEN -#undef CY_DEBUG_THROTTLE -#undef CY_DEBUG_OTHER -#undef CY_DEBUG_IO -#undef CY_DEBUG_COUNT -#undef CY_DEBUG_DTR -#undef CY_DEBUG_WAIT_UNTIL_SENT -#undef CY_DEBUG_INTERRUPTS -#undef CY_16Y_HACK -#undef CY_ENABLE_MONITORING -#undef CY_PCI_DEBUG - -/* - * Include section - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -static void cy_send_xchar(struct tty_struct *tty, char ch); - -#ifndef SERIAL_XMIT_SIZE -#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) -#endif - -#define STD_COM_FLAGS (0) - -/* firmware stuff */ -#define ZL_MAX_BLOCKS 16 -#define DRIVER_VERSION 0x02010203 -#define RAM_SIZE 0x80000 - -enum zblock_type { - ZBLOCK_PRG = 0, - ZBLOCK_FPGA = 1 -}; - -struct zfile_header { - char name[64]; - char date[32]; - char aux[32]; - u32 n_config; - u32 config_offset; - u32 n_blocks; - u32 block_offset; - u32 reserved[9]; -} __attribute__ ((packed)); - -struct zfile_config { - char name[64]; - u32 mailbox; - u32 function; - u32 n_blocks; - u32 block_list[ZL_MAX_BLOCKS]; -} __attribute__ ((packed)); - -struct zfile_block { - u32 type; - u32 file_offset; - u32 ram_offset; - u32 size; -} __attribute__ ((packed)); - -static struct tty_driver *cy_serial_driver; - -#ifdef CONFIG_ISA -/* This is the address lookup table. The driver will probe for - Cyclom-Y/ISA boards at all addresses in here. If you want the - driver to probe addresses at a different address, add it to - this table. If the driver is probing some other board and - causing problems, remove the offending address from this table. -*/ - -static unsigned int cy_isa_addresses[] = { - 0xD0000, - 0xD2000, - 0xD4000, - 0xD6000, - 0xD8000, - 0xDA000, - 0xDC000, - 0xDE000, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) - -static long maddr[NR_CARDS]; -static int irq[NR_CARDS]; - -module_param_array(maddr, long, NULL, 0); -module_param_array(irq, int, NULL, 0); - -#endif /* CONFIG_ISA */ - -/* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. -*/ -static struct cyclades_card cy_card[NR_CARDS]; - -static int cy_next_channel; /* next minor available */ - -/* - * This is used to look up the divisor speeds and the timeouts - * We're normally limited to 15 distinct baud rates. The extra - * are accessed via settings in info->port.flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI - * 20 - */ -static const int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, - 230400, 0 -}; - -static const char baud_co_25[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 -}; - -static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, - 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00 -}; - -static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ - 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, - 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, - 0x21 -}; - -static const char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, - 0x07 -}; - -/* - * The Cyclades driver implements HW flow control as any serial driver. - * The cyclades_port structure member rflow and the vector rflow_thr - * allows us to take advantage of a special feature in the CD1400 to avoid - * data loss even when the system interrupt latency is too high. These flags - * are to be used only with very special applications. Setting these flags - * requires the use of a special cable (DTR and RTS reversed). In the new - * CD1400-based boards (rev. 6.00 or later), there is no need for special - * cables. - */ - -static const char rflow_thr[] = { /* rflow threshold */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a -}; - -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static const unsigned int cy_chip_offset[] = { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 -}; - -/* PCI related definitions */ - -#ifdef CONFIG_PCI -static const struct pci_device_id cy_pci_dev_id[] = { - /* PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, - /* PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, - /* 4Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, - /* 4Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, - /* 8Y PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, - /* 8Y PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, - /* Z PCI < 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, - /* Z PCI > 1Mb */ - { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, - { } /* end of table */ -}; -MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); -#endif - -static void cy_start(struct tty_struct *); -static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); -static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); -#ifdef CONFIG_ISA -static unsigned detect_isa_irq(void __iomem *); -#endif /* CONFIG_ISA */ - -#ifndef CONFIG_CYZ_INTR -static void cyz_poll(unsigned long); - -/* The Cyclades-Z polling cycle is defined by this variable */ -static long cyz_polling_cycle = CZ_DEF_POLL; - -static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0); - -#else /* CONFIG_CYZ_INTR */ -static void cyz_rx_restart(unsigned long); -static struct timer_list cyz_rx_full_timer[NR_PORTS]; -#endif /* CONFIG_CYZ_INTR */ - -static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) -{ - struct cyclades_card *card = port->card; - - cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); -} - -static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) -{ - struct cyclades_card *card = port->card; - - return readb(port->u.cyy.base_addr + (reg << card->bus_index)); -} - -static inline bool cy_is_Z(struct cyclades_card *card) -{ - return card->num_chips == (unsigned int)-1; -} - -static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr) -{ - return readl(&ctl_addr->init_ctrl) & (1 << 17); -} - -static inline bool cyz_fpga_loaded(struct cyclades_card *card) -{ - return __cyz_fpga_loaded(card->ctl_addr.p9060); -} - -static inline bool cyz_is_loaded(struct cyclades_card *card) -{ - struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; - - return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) && - readl(&fw_id->signature) == ZFIRM_ID; -} - -static inline int serial_paranoia_check(struct cyclades_port *info, - const char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - if (!info) { - printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) " - "in %s\n", name, routine); - return 1; - } - - if (info->magic != CYCLADES_MAGIC) { - printk(KERN_WARNING "cyc Warning: bad magic number for serial " - "struct (%s) in %s\n", name, routine); - return 1; - } -#endif - return 0; -} - -/***********************************************************/ -/********* Start of block of Cyclom-Y specific code ********/ - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - - This function is only called from inside spinlock-protected code. - */ -static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) -{ - void __iomem *ccr = base_addr + (CyCCR << index); - unsigned int i; - - /* Check to see that the previous command has completed */ - for (i = 0; i < 100; i++) { - if (readb(ccr) == 0) - break; - udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if (i == 100) - return -1; - - /* Issue the new command */ - cy_writeb(ccr, cmd); - - return 0; -} - -static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) -{ - return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, - port->card->bus_index); -} - -#ifdef CONFIG_ISA -/* ISA interrupt detection code */ -static unsigned detect_isa_irq(void __iomem *address) -{ - int irq; - unsigned long irqs, flags; - int save_xir, save_car; - int index = 0; /* IRQ probing is only for ISA */ - - /* forget possible initially masked and pending IRQ */ - irq = probe_irq_off(probe_irq_on()); - - /* Clear interrupts on the board first */ - cy_writeb(address + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - - irqs = probe_irq_on(); - /* Wait ... */ - msleep(5); - - /* Enable the Tx interrupts on the CD1400 */ - local_irq_save(flags); - cy_writeb(address + (CyCAR << index), 0); - __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); - - cy_writeb(address + (CyCAR << index), 0); - cy_writeb(address + (CySRER << index), - readb(address + (CySRER << index)) | CyTxRdy); - local_irq_restore(flags); - - /* Wait ... */ - msleep(5); - - /* Check which interrupt is in use */ - irq = probe_irq_off(irqs); - - /* Clean up */ - save_xir = (u_char) readb(address + (CyTIR << index)); - save_car = readb(address + (CyCAR << index)); - cy_writeb(address + (CyCAR << index), (save_xir & 0x3)); - cy_writeb(address + (CySRER << index), - readb(address + (CySRER << index)) & ~CyTxRdy); - cy_writeb(address + (CyTIR << index), (save_xir & 0x3f)); - cy_writeb(address + (CyCAR << index), (save_car)); - cy_writeb(address + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - - return (irq > 0) ? irq : 0; -} -#endif /* CONFIG_ISA */ - -static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_struct *tty; - int len, index = cinfo->bus_index; - u8 ivr, save_xir, channel, save_car, data, char_count; - -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); -#endif - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyRIR << index)); - channel = save_xir & CyIRChannel; - info = &cinfo->ports[channel + chip * 4]; - save_car = cyy_readb(info, CyCAR); - cyy_writeb(info, CyCAR, save_xir); - ivr = cyy_readb(info, CyRIVR) & CyIVRMask; - - tty = tty_port_tty_get(&info->port); - /* if there is nowhere to put the data, discard it */ - if (tty == NULL) { - if (ivr == CyIVRRxEx) { /* exception */ - data = cyy_readb(info, CyRDSR); - } else { /* normal character reception */ - char_count = cyy_readb(info, CyRDCR); - while (char_count--) - data = cyy_readb(info, CyRDSR); - } - goto end; - } - /* there is an open port for this data */ - if (ivr == CyIVRRxEx) { /* exception */ - data = cyy_readb(info, CyRDSR); - - /* For statistics only */ - if (data & CyBREAK) - info->icount.brk++; - else if (data & CyFRAME) - info->icount.frame++; - else if (data & CyPARITY) - info->icount.parity++; - else if (data & CyOVERRUN) - info->icount.overrun++; - - if (data & info->ignore_status_mask) { - info->icount.rx++; - tty_kref_put(tty); - return; - } - if (tty_buffer_request_room(tty, 1)) { - if (data & info->read_status_mask) { - if (data & CyBREAK) { - tty_insert_flip_char(tty, - cyy_readb(info, CyRDSR), - TTY_BREAK); - info->icount.rx++; - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); - } else if (data & CyFRAME) { - tty_insert_flip_char(tty, - cyy_readb(info, CyRDSR), - TTY_FRAME); - info->icount.rx++; - info->idle_stats.frame_errs++; - } else if (data & CyPARITY) { - /* Pieces of seven... */ - tty_insert_flip_char(tty, - cyy_readb(info, CyRDSR), - TTY_PARITY); - info->icount.rx++; - info->idle_stats.parity_errs++; - } else if (data & CyOVERRUN) { - tty_insert_flip_char(tty, 0, - TTY_OVERRUN); - info->icount.rx++; - /* If the flip buffer itself is - overflowing, we still lose - the next incoming character. - */ - tty_insert_flip_char(tty, - cyy_readb(info, CyRDSR), - TTY_FRAME); - info->icount.rx++; - info->idle_stats.overruns++; - /* These two conditions may imply */ - /* a normal read should be done. */ - /* } else if(data & CyTIMEOUT) { */ - /* } else if(data & CySPECHAR) { */ - } else { - tty_insert_flip_char(tty, 0, - TTY_NORMAL); - info->icount.rx++; - } - } else { - tty_insert_flip_char(tty, 0, TTY_NORMAL); - info->icount.rx++; - } - } else { - /* there was a software buffer overrun and nothing - * could be done about it!!! */ - info->icount.buf_overrun++; - info->idle_stats.overruns++; - } - } else { /* normal character reception */ - /* load # chars available from the chip */ - char_count = cyy_readb(info, CyRDCR); - -#ifdef CY_ENABLE_MONITORING - ++info->mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - len = tty_buffer_request_room(tty, char_count); - while (len--) { - data = cyy_readb(info, CyRDSR); - tty_insert_flip_char(tty, data, TTY_NORMAL); - info->idle_stats.recv_bytes++; - info->icount.rx++; -#ifdef CY_16Y_HACK - udelay(10L); -#endif - } - info->idle_stats.recv_idle = jiffies; - } - tty_schedule_flip(tty); - tty_kref_put(tty); -end: - /* end of service */ - cyy_writeb(info, CyRIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_struct *tty; - int char_count, index = cinfo->bus_index; - u8 save_xir, channel, save_car, outch; - - /* Since we only get here when the transmit buffer - is empty, we know we can always stuff a dozen - characters. */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip); -#endif - - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyTIR << index)); - channel = save_xir & CyIRChannel; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); - - info = &cinfo->ports[channel + chip * 4]; - tty = tty_port_tty_get(&info->port); - if (tty == NULL) { - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); - goto end; - } - - /* load the on-chip space for outbound data */ - char_count = info->xmit_fifo_size; - - if (info->x_char) { /* send special char */ - outch = info->x_char; - cyy_writeb(info, CyTDR, outch); - char_count--; - info->icount.tx++; - info->x_char = 0; - } - - if (info->breakon || info->breakoff) { - if (info->breakon) { - cyy_writeb(info, CyTDR, 0); - cyy_writeb(info, CyTDR, 0x81); - info->breakon = 0; - char_count -= 2; - } - if (info->breakoff) { - cyy_writeb(info, CyTDR, 0); - cyy_writeb(info, CyTDR, 0x83); - info->breakoff = 0; - char_count -= 2; - } - } - - while (char_count-- > 0) { - if (!info->xmit_cnt) { - if (cyy_readb(info, CySRER) & CyTxMpty) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxMpty); - } else { - cyy_writeb(info, CySRER, CyTxMpty | - (cyy_readb(info, CySRER) & ~CyTxRdy)); - } - goto done; - } - if (info->port.xmit_buf == NULL) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - goto done; - } - if (tty->stopped || tty->hw_stopped) { - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - goto done; - } - /* Because the Embedded Transmit Commands have been enabled, - * we must check to see if the escape character, NULL, is being - * sent. If it is, we must ensure that there is room for it to - * be doubled in the output stream. Therefore we no longer - * advance the pointer when the character is fetched, but - * rather wait until after the check for a NULL output - * character. This is necessary because there may not be room - * for the two chars needed to send a NULL.) - */ - outch = info->port.xmit_buf[info->xmit_tail]; - if (outch) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - cyy_writeb(info, CyTDR, outch); - info->icount.tx++; - } else { - if (char_count > 1) { - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - cyy_writeb(info, CyTDR, outch); - cyy_writeb(info, CyTDR, 0); - info->icount.tx++; - char_count--; - } - } - } - -done: - tty_wakeup(tty); - tty_kref_put(tty); -end: - /* end of service */ - cyy_writeb(info, CyTIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, - void __iomem *base_addr) -{ - struct cyclades_port *info; - struct tty_struct *tty; - int index = cinfo->bus_index; - u8 save_xir, channel, save_car, mdm_change, mdm_status; - - /* determine the channel & change to that context */ - save_xir = readb(base_addr + (CyMIR << index)); - channel = save_xir & CyIRChannel; - info = &cinfo->ports[channel + chip * 4]; - save_car = cyy_readb(info, CyCAR); - cyy_writeb(info, CyCAR, save_xir); - - mdm_change = cyy_readb(info, CyMISR); - mdm_status = cyy_readb(info, CyMSVR1); - - tty = tty_port_tty_get(&info->port); - if (!tty) - goto end; - - if (mdm_change & CyANY_DELTA) { - /* For statistics only */ - if (mdm_change & CyDCD) - info->icount.dcd++; - if (mdm_change & CyCTS) - info->icount.cts++; - if (mdm_change & CyDSR) - info->icount.dsr++; - if (mdm_change & CyRI) - info->icount.rng++; - - wake_up_interruptible(&info->port.delta_msr_wait); - } - - if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { - if (mdm_status & CyDCD) - wake_up_interruptible(&info->port.open_wait); - else - tty_hangup(tty); - } - if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { - if (tty->hw_stopped) { - if (mdm_status & CyCTS) { - /* cy_start isn't used - because... !!! */ - tty->hw_stopped = 0; - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) | CyTxRdy); - tty_wakeup(tty); - } - } else { - if (!(mdm_status & CyCTS)) { - /* cy_stop isn't used - because ... !!! */ - tty->hw_stopped = 1; - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) & ~CyTxRdy); - } - } - } -/* if (mdm_change & CyDSR) { - } - if (mdm_change & CyRI) { - }*/ - tty_kref_put(tty); -end: - /* end of service */ - cyy_writeb(info, CyMIR, save_xir & 0x3f); - cyy_writeb(info, CyCAR, save_car); -} - -/* The real interrupt service routine is called - whenever the card wants its hand held--chars - received, out buffer empty, modem change, etc. - */ -static irqreturn_t cyy_interrupt(int irq, void *dev_id) -{ - int status; - struct cyclades_card *cinfo = dev_id; - void __iomem *base_addr, *card_base_addr; - unsigned int chip, too_many, had_work; - int index; - - if (unlikely(cinfo == NULL)) { -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n", - irq); -#endif - return IRQ_NONE; /* spurious interrupt */ - } - - card_base_addr = cinfo->base_addr; - index = cinfo->bus_index; - - /* card was not initialized yet (e.g. DEBUG_SHIRQ) */ - if (unlikely(card_base_addr == NULL)) - return IRQ_HANDLED; - - /* This loop checks all chips in the card. Make a note whenever - _any_ chip had some work to do, as this is considered an - indication that there will be more to do. Only when no chip - has any work does this outermost loop exit. - */ - do { - had_work = 0; - for (chip = 0; chip < cinfo->num_chips; chip++) { - base_addr = cinfo->base_addr + - (cy_chip_offset[chip] << index); - too_many = 0; - while ((status = readb(base_addr + - (CySVRR << index))) != 0x00) { - had_work++; - /* The purpose of the following test is to ensure that - no chip can monopolize the driver. This forces the - chips to be checked in a round-robin fashion (after - draining each of a bunch (1000) of characters). - */ - if (1000 < too_many++) - break; - spin_lock(&cinfo->card_lock); - if (status & CySRReceive) /* rx intr */ - cyy_chip_rx(cinfo, chip, base_addr); - if (status & CySRTransmit) /* tx intr */ - cyy_chip_tx(cinfo, chip, base_addr); - if (status & CySRModem) /* modem intr */ - cyy_chip_modem(cinfo, chip, base_addr); - spin_unlock(&cinfo->card_lock); - } - } - } while (had_work); - - /* clear interrupts */ - spin_lock(&cinfo->card_lock); - cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - spin_unlock(&cinfo->card_lock); - return IRQ_HANDLED; -} /* cyy_interrupt */ - -static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, - unsigned int clear) -{ - struct cyclades_card *card = info->card; - int channel = info->line - card->first_line; - u32 rts, dtr, msvrr, msvrd; - - channel &= 0x03; - - if (info->rtsdtr_inv) { - msvrr = CyMSVR2; - msvrd = CyMSVR1; - rts = CyDTR; - dtr = CyRTS; - } else { - msvrr = CyMSVR1; - msvrd = CyMSVR2; - rts = CyRTS; - dtr = CyDTR; - } - if (set & TIOCM_RTS) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrr, rts); - } - if (clear & TIOCM_RTS) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrr, ~rts); - } - if (set & TIOCM_DTR) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrd, dtr); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - cyy_readb(info, CyMSVR1), - cyy_readb(info, CyMSVR2)); -#endif - } - if (clear & TIOCM_DTR) { - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, msvrd, ~dtr); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - cyy_readb(info, CyMSVR1), - cyy_readb(info, CyMSVR2)); -#endif - } -} - -/***********************************************************/ -/********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclades-Z specific code *******/ -/***********************************************************/ - -static int -cyz_fetch_msg(struct cyclades_card *cinfo, - __u32 *channel, __u8 *cmd, __u32 *param) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - unsigned long loc_doorbell; - - loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); - if (loc_doorbell) { - *cmd = (char)(0xff & loc_doorbell); - *channel = readl(&board_ctrl->fwcmd_channel); - *param = (__u32) readl(&board_ctrl->fwcmd_param); - cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff); - return 1; - } - return 0; -} /* cyz_fetch_msg */ - -static int -cyz_issue_cmd(struct cyclades_card *cinfo, - __u32 channel, __u8 cmd, __u32 param) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - __u32 __iomem *pci_doorbell; - unsigned int index; - - if (!cyz_is_loaded(cinfo)) - return -1; - - index = 0; - pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; - while ((readl(pci_doorbell) & 0xff) != 0) { - if (index++ == 1000) - return (int)(readl(pci_doorbell) & 0xff); - udelay(50L); - } - cy_writel(&board_ctrl->hcmd_channel, channel); - cy_writel(&board_ctrl->hcmd_param, param); - cy_writel(pci_doorbell, (long)cmd); - - return 0; -} /* cyz_issue_cmd */ - -static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) -{ - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - struct cyclades_card *cinfo = info->card; - unsigned int char_count; - int len; -#ifdef BLOCKMOVE - unsigned char *buf; -#else - char data; -#endif - __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; - - rx_get = new_rx_get = readl(&buf_ctrl->rx_get); - rx_put = readl(&buf_ctrl->rx_put); - rx_bufsize = readl(&buf_ctrl->rx_bufsize); - rx_bufaddr = readl(&buf_ctrl->rx_bufaddr); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - - if (char_count) { -#ifdef CY_ENABLE_MONITORING - info->mon.int_count++; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - if (tty == NULL) { - /* flush received characters */ - new_rx_get = (new_rx_get + char_count) & - (rx_bufsize - 1); - info->rflush_count++; - } else { -#ifdef BLOCKMOVE - /* we'd like to use memcpy(t, f, n) and memset(s, c, count) - for performance, but because of buffer boundaries, there - may be several steps to the operation */ - while (1) { - len = tty_prepare_flip_string(tty, &buf, - char_count); - if (!len) - break; - - len = min_t(unsigned int, min(len, char_count), - rx_bufsize - new_rx_get); - - memcpy_fromio(buf, cinfo->base_addr + - rx_bufaddr + new_rx_get, len); - - new_rx_get = (new_rx_get + len) & - (rx_bufsize - 1); - char_count -= len; - info->icount.rx += len; - info->idle_stats.recv_bytes += len; - } -#else - len = tty_buffer_request_room(tty, char_count); - while (len--) { - data = readb(cinfo->base_addr + rx_bufaddr + - new_rx_get); - new_rx_get = (new_rx_get + 1) & - (rx_bufsize - 1); - tty_insert_flip_char(tty, data, TTY_NORMAL); - info->idle_stats.recv_bytes++; - info->icount.rx++; - } -#endif -#ifdef CONFIG_CYZ_INTR - /* Recalculate the number of chars in the RX buffer and issue - a cmd in case it's higher than the RX high water mark */ - rx_put = readl(&buf_ctrl->rx_put); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - if (char_count >= readl(&buf_ctrl->rx_threshold) && - !timer_pending(&cyz_rx_full_timer[ - info->line])) - mod_timer(&cyz_rx_full_timer[info->line], - jiffies + 1); -#endif - info->idle_stats.recv_idle = jiffies; - tty_schedule_flip(tty); - } - /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, new_rx_get); - } -} - -static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) -{ - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - struct cyclades_card *cinfo = info->card; - u8 data; - unsigned int char_count; -#ifdef BLOCKMOVE - int small_count; -#endif - __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr; - - if (info->xmit_cnt <= 0) /* Nothing to transmit */ - return; - - tx_get = readl(&buf_ctrl->tx_get); - tx_put = readl(&buf_ctrl->tx_put); - tx_bufsize = readl(&buf_ctrl->tx_bufsize); - tx_bufaddr = readl(&buf_ctrl->tx_bufaddr); - if (tx_put >= tx_get) - char_count = tx_get - tx_put - 1 + tx_bufsize; - else - char_count = tx_get - tx_put - 1; - - if (char_count) { - - if (tty == NULL) - goto ztxdone; - - if (info->x_char) { /* send special char */ - data = info->x_char; - - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - info->x_char = 0; - char_count--; - info->icount.tx++; - } -#ifdef BLOCKMOVE - while (0 < (small_count = min_t(unsigned int, - tx_bufsize - tx_put, min_t(unsigned int, - (SERIAL_XMIT_SIZE - info->xmit_tail), - min_t(unsigned int, info->xmit_cnt, - char_count))))) { - - memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + - tx_put), - &info->port.xmit_buf[info->xmit_tail], - small_count); - - tx_put = (tx_put + small_count) & (tx_bufsize - 1); - char_count -= small_count; - info->icount.tx += small_count; - info->xmit_cnt -= small_count; - info->xmit_tail = (info->xmit_tail + small_count) & - (SERIAL_XMIT_SIZE - 1); - } -#else - while (info->xmit_cnt && char_count) { - data = info->port.xmit_buf[info->xmit_tail]; - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - char_count--; - info->icount.tx++; - } -#endif - tty_wakeup(tty); -ztxdone: - /* Update tx_put */ - cy_writel(&buf_ctrl->tx_put, tx_put); - } -} - -static void cyz_handle_cmd(struct cyclades_card *cinfo) -{ - struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - struct tty_struct *tty; - struct cyclades_port *info; - __u32 channel, param, fw_ver; - __u8 cmd; - int special_count; - int delta_count; - - fw_ver = readl(&board_ctrl->fw_version); - - while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { - special_count = 0; - delta_count = 0; - info = &cinfo->ports[channel]; - tty = tty_port_tty_get(&info->port); - if (tty == NULL) - continue; - - switch (cmd) { - case C_CM_PR_ERROR: - tty_insert_flip_char(tty, 0, TTY_PARITY); - info->icount.rx++; - special_count++; - break; - case C_CM_FR_ERROR: - tty_insert_flip_char(tty, 0, TTY_FRAME); - info->icount.rx++; - special_count++; - break; - case C_CM_RXBRK: - tty_insert_flip_char(tty, 0, TTY_BREAK); - info->icount.rx++; - special_count++; - break; - case C_CM_MDCD: - info->icount.dcd++; - delta_count++; - if (info->port.flags & ASYNC_CHECK_CD) { - u32 dcd = fw_ver > 241 ? param : - readl(&info->u.cyz.ch_ctrl->rs_status); - if (dcd & C_RS_DCD) - wake_up_interruptible(&info->port.open_wait); - else - tty_hangup(tty); - } - break; - case C_CM_MCTS: - info->icount.cts++; - delta_count++; - break; - case C_CM_MRI: - info->icount.rng++; - delta_count++; - break; - case C_CM_MDSR: - info->icount.dsr++; - delta_count++; - break; -#ifdef Z_WAKE - case C_CM_IOCTLW: - complete(&info->shutdown_wait); - break; -#endif -#ifdef CONFIG_CYZ_INTR - case C_CM_RXHIWM: - case C_CM_RXNNDT: - case C_CM_INTBACK2: - /* Reception Interrupt */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " - "port %ld\n", info->card, channel); -#endif - cyz_handle_rx(info, tty); - break; - case C_CM_TXBEMPTY: - case C_CM_TXLOWWM: - case C_CM_INTBACK: - /* Transmission Interrupt */ -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " - "port %ld\n", info->card, channel); -#endif - cyz_handle_tx(info, tty); - break; -#endif /* CONFIG_CYZ_INTR */ - case C_CM_FATAL: - /* should do something with this !!! */ - break; - default: - break; - } - if (delta_count) - wake_up_interruptible(&info->port.delta_msr_wait); - if (special_count) - tty_schedule_flip(tty); - tty_kref_put(tty); - } -} - -#ifdef CONFIG_CYZ_INTR -static irqreturn_t cyz_interrupt(int irq, void *dev_id) -{ - struct cyclades_card *cinfo = dev_id; - - if (unlikely(!cyz_is_loaded(cinfo))) { -#ifdef CY_DEBUG_INTERRUPTS - printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " - "(IRQ%d).\n", irq); -#endif - return IRQ_NONE; - } - - /* Handle the interrupts */ - cyz_handle_cmd(cinfo); - - return IRQ_HANDLED; -} /* cyz_interrupt */ - -static void cyz_rx_restart(unsigned long arg) -{ - struct cyclades_port *info = (struct cyclades_port *)arg; - struct cyclades_card *card = info->card; - int retval; - __u32 channel = info->line - card->first_line; - unsigned long flags; - - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n", - info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); -} - -#else /* CONFIG_CYZ_INTR */ - -static void cyz_poll(unsigned long arg) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info; - unsigned long expires = jiffies + HZ; - unsigned int port, card; - - for (card = 0; card < NR_CARDS; card++) { - cinfo = &cy_card[card]; - - if (!cy_is_Z(cinfo)) - continue; - if (!cyz_is_loaded(cinfo)) - continue; - - /* Skip first polling cycle to avoid racing conditions with the FW */ - if (!cinfo->intr_enabled) { - cinfo->intr_enabled = 1; - continue; - } - - cyz_handle_cmd(cinfo); - - for (port = 0; port < cinfo->nports; port++) { - struct tty_struct *tty; - - info = &cinfo->ports[port]; - tty = tty_port_tty_get(&info->port); - /* OK to pass NULL to the handle functions below. - They need to drop the data in that case. */ - - if (!info->throttle) - cyz_handle_rx(info, tty); - cyz_handle_tx(info, tty); - tty_kref_put(tty); - } - /* poll every 'cyz_polling_cycle' period */ - expires = jiffies + cyz_polling_cycle; - } - mod_timer(&cyz_timerlist, expires); -} /* cyz_poll */ - -#endif /* CONFIG_CYZ_INTR */ - -/********** End of block of Cyclades-Z specific code *********/ -/***********************************************************/ - -/* This is called whenever a port becomes active; - interrupts are enabled and DTR & RTS are turned on. - */ -static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - int retval = 0; - int channel; - unsigned long page; - - card = info->card; - channel = info->line - card->first_line; - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - spin_lock_irqsave(&card->card_lock, flags); - - if (info->port.flags & ASYNC_INITIALIZED) - goto errout; - - if (!info->type) { - set_bit(TTY_IO_ERROR, &tty->flags); - goto errout; - } - - if (info->port.xmit_buf) - free_page(page); - else - info->port.xmit_buf = (unsigned char *)page; - - spin_unlock_irqrestore(&card->card_lock, flags); - - cy_set_line_char(info, tty); - - if (!cy_is_Z(card)) { - channel &= 0x03; - - spin_lock_irqsave(&card->card_lock, flags); - - cyy_writeb(info, CyCAR, channel); - - cyy_writeb(info, CyRTPR, - (info->default_timeout ? info->default_timeout : 0x02)); - /* 10ms rx timeout */ - - cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); - - cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); - - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - - if (!cyz_is_loaded(card)) - return -ENODEV; - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " - "base_addr %p\n", card, channel, card->base_addr); -#endif - spin_lock_irqsave(&card->card_lock, flags); - - cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); -#ifdef Z_WAKE -#ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl->intr_enable, - C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | - C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); -#else - cy_writel(&ch_ctrl->intr_enable, - C_IN_IOCTLW | C_IN_MDCD); -#endif /* CONFIG_CYZ_INTR */ -#else -#ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl->intr_enable, - C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | - C_IN_RXNNDT | C_IN_MDCD); -#else - cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); -#endif /* CONFIG_CYZ_INTR */ -#endif /* Z_WAKE */ - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was " - "%x\n", info->line, retval); - } - - /* Flush RX buffers before raising DTR and RTS */ - retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was " - "%x\n", info->line, retval); - } - - /* set timeout !!! */ - /* set RTS and DTR !!! */ - tty_port_raise_dtr_rts(&info->port); - - /* enable send, recv, modem !!! */ - } - - info->port.flags |= ASYNC_INITIALIZED; - - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; - - spin_unlock_irqrestore(&card->card_lock, flags); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup done\n"); -#endif - return 0; - -errout: - spin_unlock_irqrestore(&card->card_lock, flags); - free_page(page); - return retval; -} /* startup */ - -static void start_xmit(struct cyclades_port *info) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - int channel = info->line - card->first_line; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { -#ifdef CONFIG_CYZ_INTR - int retval; - - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " - "%x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); -#else /* CONFIG_CYZ_INTR */ - /* Don't have to do anything at this time */ -#endif /* CONFIG_CYZ_INTR */ - } -} /* start_xmit */ - -/* - * This routine shuts down a serial port; interrupts are disabled, - * and DTR is dropped if the hangup on close termio flag is on. - */ -static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - int channel; - - if (!(info->port.flags & ASYNC_INITIALIZED)) - return; - - card = info->card; - channel = info->line - card->first_line; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - - /* Clear delta_msr_wait queue to avoid mem leaks. */ - wake_up_interruptible(&info->port.delta_msr_wait); - - if (info->port.xmit_buf) { - unsigned char *temp; - temp = info->port.xmit_buf; - info->port.xmit_buf = NULL; - free_page((unsigned long)temp); - } - if (tty->termios->c_cflag & HUPCL) - cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); - - cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); - /* it may be appropriate to clear _XMIT at - some later date (after testing)!!! */ - - set_bit(TTY_IO_ERROR, &tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; - spin_unlock_irqrestore(&card->card_lock, flags); - } else { -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " - "base_addr %p\n", card, channel, card->base_addr); -#endif - - if (!cyz_is_loaded(card)) - return; - - spin_lock_irqsave(&card->card_lock, flags); - - if (info->port.xmit_buf) { - unsigned char *temp; - temp = info->port.xmit_buf; - info->port.xmit_buf = NULL; - free_page((unsigned long)temp); - } - - if (tty->termios->c_cflag & HUPCL) - tty_port_lower_dtr_rts(&info->port); - - set_bit(TTY_IO_ERROR, &tty->flags); - info->port.flags &= ~ASYNC_INITIALIZED; - - spin_unlock_irqrestore(&card->card_lock, flags); - } - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc shutdown done\n"); -#endif -} /* shutdown */ - -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ - -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -static int cy_open(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info; - unsigned int i, line; - int retval; - - line = tty->index; - if (tty->index < 0 || NR_PORTS <= line) - return -ENODEV; - - for (i = 0; i < NR_CARDS; i++) - if (line < cy_card[i].first_line + cy_card[i].nports && - line >= cy_card[i].first_line) - break; - if (i >= NR_CARDS) - return -ENODEV; - info = &cy_card[i].ports[line - cy_card[i].first_line]; - if (info->line < 0) - return -ENODEV; - - /* If the card's firmware hasn't been loaded, - treat it as absent from the system. This - will make the user pay attention. - */ - if (cy_is_Z(info->card)) { - struct cyclades_card *cinfo = info->card; - struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS; - - if (!cyz_is_loaded(cinfo)) { - if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) && - readl(&firm_id->signature) == - ZFIRM_HLT) { - printk(KERN_ERR "cyc:Cyclades-Z Error: you " - "need an external power supply for " - "this number of ports.\nFirmware " - "halted.\n"); - } else { - printk(KERN_ERR "cyc:Cyclades-Z firmware not " - "yet loaded\n"); - } - return -ENODEV; - } -#ifdef CONFIG_CYZ_INTR - else { - /* In case this Z board is operating in interrupt mode, its - interrupts should be enabled as soon as the first open - happens to one of its ports. */ - if (!cinfo->intr_enabled) { - u16 intr; - - /* Enable interrupts on the PLX chip */ - intr = readw(&cinfo->ctl_addr.p9060-> - intr_ctrl_stat) | 0x0900; - cy_writew(&cinfo->ctl_addr.p9060-> - intr_ctrl_stat, intr); - /* Enable interrupts on the FW */ - retval = cyz_issue_cmd(cinfo, 0, - C_CM_IRQ_ENBL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:IRQ enable retval " - "was %x\n", retval); - } - cinfo->intr_enabled = 1; - } - } -#endif /* CONFIG_CYZ_INTR */ - /* Make sure this Z port really exists in hardware */ - if (info->line > (cinfo->first_line + cinfo->nports - 1)) - return -ENODEV; - } -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); -#endif - tty->driver_data = info; - if (serial_paranoia_check(info, tty->name, "cy_open")) - return -ENODEV; - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, - info->port.count); -#endif - info->port.count++; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", - current->pid, info->port.count); -#endif - - /* - * If the port is the middle of closing, bail out now - */ - if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; - } - - /* - * Start up serial port - */ - retval = cy_startup(info, tty); - if (retval) - return retval; - - retval = tty_port_block_til_ready(&info->port, tty, filp); - if (retval) { -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " - "with %d\n", retval); -#endif - return retval; - } - - info->throttle = 0; - tty_port_tty_set(&info->port, tty); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_open done\n"); -#endif - return 0; -} /* cy_open */ - -/* - * cy_wait_until_sent() --- wait until the transmitter is empty - */ -static void cy_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct cyclades_card *card; - struct cyclades_port *info = tty->driver_data; - unsigned long orig_jiffies; - int char_time; - - if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent")) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - 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. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; - char_time = char_time / 5; - if (char_time <= 0) - char_time = 1; - if (timeout < 0) - timeout = 0; - if (timeout) - char_time = min(char_time, timeout); - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout = 2 * info->timeout; -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "In cy_wait_until_sent(%d) check=%d, jiff=%lu...", - timeout, char_time, jiffies); -#endif - card = info->card; - if (!cy_is_Z(card)) { - while (cyy_readb(info, CySRER) & CyTxRdy) { -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies); -#endif - if (msleep_interruptible(jiffies_to_msecs(char_time))) - break; - if (timeout && time_after(jiffies, orig_jiffies + - timeout)) - break; - } - } - /* Run one more char cycle */ - msleep_interruptible(jiffies_to_msecs(char_time * 5)); -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies); -#endif -} - -static void cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int channel, retval; - unsigned long flags; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) - return; - - card = info->card; - channel = info->line - card->first_line; - - spin_lock_irqsave(&card->card_lock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board - buffers as well */ - spin_lock_irqsave(&card->card_lock, flags); - retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " - "was %x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - tty_wakeup(tty); -} /* cy_flush_buffer */ - - -static void cy_do_close(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *card; - unsigned long flags; - int channel; - - card = info->card; - channel = info->line - card->first_line; - spin_lock_irqsave(&card->card_lock, flags); - - if (!cy_is_Z(card)) { - /* Stop accepting input */ - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); - if (info->port.flags & ASYNC_INITIALIZED) { - /* Waiting for on-board buffers to be empty before - closing the port */ - spin_unlock_irqrestore(&card->card_lock, flags); - cy_wait_until_sent(port->tty, info->timeout); - spin_lock_irqsave(&card->card_lock, flags); - } - } else { -#ifdef Z_WAKE - /* Waiting for on-board buffers to be empty before closing - the port */ - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int retval; - - if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); - if (retval != 0) { - printk(KERN_DEBUG "cyc:cy_close retval on " - "ttyC%d was %x\n", info->line, retval); - } - spin_unlock_irqrestore(&card->card_lock, flags); - wait_for_completion_interruptible(&info->shutdown_wait); - spin_lock_irqsave(&card->card_lock, flags); - } -#endif - } - spin_unlock_irqrestore(&card->card_lock, flags); - cy_shutdown(info, port->tty); -} - -/* - * This routine is called when a particular tty device is closed. - */ -static void cy_close(struct tty_struct *tty, struct file *filp) -{ - struct cyclades_port *info = tty->driver_data; - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) - return; - tty_port_close(&info->port, tty, filp); -} /* cy_close */ - -/* This routine gets called when tty_write has put something into - * the write_queue. The characters may come from user space or - * kernel space. - * - * This routine will return the number of characters actually - * accepted for writing. - * - * If the port is not already transmitting stuff, start it off by - * enabling interrupts. The interrupt service routine will then - * ensure that the characters are sent. - * If the port is already active, there is no need to kick it. - * - */ -static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - int c, ret = 0; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write")) - return 0; - - if (!info->port.xmit_buf) - return 0; - - spin_lock_irqsave(&info->card->card_lock, flags); - while (1) { - c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); - c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); - - if (c <= 0) - break; - - memcpy(info->port.xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & - (SERIAL_XMIT_SIZE - 1); - info->xmit_cnt += c; - buf += c; - count -= c; - ret += c; - } - spin_unlock_irqrestore(&info->card->card_lock, flags); - - info->idle_stats.xmit_bytes += ret; - info->idle_stats.xmit_idle = jiffies; - - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) - start_xmit(info); - - return ret; -} /* cy_write */ - -/* - * This routine is called by the kernel to write a single - * character to the tty device. If the kernel uses this routine, - * it must call the flush_chars() routine (if defined) when it is - * done stuffing characters into the driver. If there is no room - * in the queue, the character is ignored. - */ -static int cy_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct cyclades_port *info = tty->driver_data; - unsigned long flags; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_put_char")) - return 0; - - if (!info->port.xmit_buf) - return 0; - - spin_lock_irqsave(&info->card->card_lock, flags); - if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { - spin_unlock_irqrestore(&info->card->card_lock, flags); - return 0; - } - - info->port.xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE - 1; - info->xmit_cnt++; - info->idle_stats.xmit_bytes++; - info->idle_stats.xmit_idle = jiffies; - spin_unlock_irqrestore(&info->card->card_lock, flags); - return 1; -} /* cy_put_char */ - -/* - * This routine is called by the kernel after it has written a - * series of characters to the tty device using put_char(). - */ -static void cy_flush_chars(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) - return; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->port.xmit_buf) - return; - - start_xmit(info); -} /* cy_flush_chars */ - -/* - * This routine returns the numbers of characters the tty driver - * will accept for queuing to be written. This number is subject - * to change as output buffers get emptied, or if the output flow - * control is activated. - */ -static int cy_write_room(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - int ret; - -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} /* cy_write_room */ - -static int cy_chars_in_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) - return 0; - -#ifdef Z_EXT_CHARS_IN_BUFFER - if (!cy_is_Z(info->card)) { -#endif /* Z_EXT_CHARS_IN_BUFFER */ -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", - info->line, info->xmit_cnt); -#endif - return info->xmit_cnt; -#ifdef Z_EXT_CHARS_IN_BUFFER - } else { - struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; - int char_count; - __u32 tx_put, tx_get, tx_bufsize; - - tx_get = readl(&buf_ctrl->tx_get); - tx_put = readl(&buf_ctrl->tx_put); - tx_bufsize = readl(&buf_ctrl->tx_bufsize); - if (tx_put >= tx_get) - char_count = tx_put - tx_get; - else - char_count = tx_put - tx_get + tx_bufsize; -#ifdef CY_DEBUG_IO - printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", - info->line, info->xmit_cnt + char_count); -#endif - return info->xmit_cnt + char_count; - } -#endif /* Z_EXT_CHARS_IN_BUFFER */ -} /* cy_chars_in_buffer */ - -/* - * ------------------------------------------------------------ - * cy_ioctl() and friends - * ------------------------------------------------------------ - */ - -static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) -{ - int co, co_val, bpr; - __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : - 25000000); - - if (baud == 0) { - info->tbpr = info->tco = info->rbpr = info->rco = 0; - return; - } - - /* determine which prescaler to use */ - for (co = 4, co_val = 2048; co; co--, co_val >>= 2) { - if (cy_clock / co_val / baud > 63) - break; - } - - bpr = (cy_clock / co_val * 2 / baud + 1) / 2; - if (bpr > 255) - bpr = 255; - - info->tbpr = info->rbpr = bpr; - info->tco = info->rco = co; -} - -/* - * This routine finds or computes the various line characteristics. - * It used to be called config_setup - */ -static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) -{ - struct cyclades_card *card; - unsigned long flags; - int channel; - unsigned cflag, iflag; - int baud, baud_rate = 0; - int i; - - if (!tty->termios) /* XXX can this happen at all? */ - return; - - if (info->line == -1) - return; - - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; - - /* - * Set up the tty->alt_speed kludge - */ - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - tty->alt_speed = 57600; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - tty->alt_speed = 115200; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - tty->alt_speed = 230400; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - tty->alt_speed = 460800; - - card = info->card; - channel = info->line - card->first_line; - - if (!cy_is_Z(card)) { - u32 cflags; - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - if (info->custom_divisor) - baud_rate = info->baud / info->custom_divisor; - else - baud_rate = info->baud; - } else if (baud > CD1400_MAX_SPEED) { - baud = CD1400_MAX_SPEED; - } - /* find the baud index */ - for (i = 0; i < 20; i++) { - if (baud == baud_table[i]) - break; - } - if (i == 20) - i = 19; /* CD1400_MAX_SPEED */ - - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - cyy_baud_calc(info, baud_rate); - } else { - if (info->chip_rev >= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - info->tbpr = baud_bpr_60[i]; /* Tx BPR */ - info->tco = baud_co_60[i]; /* Tx CO */ - info->rbpr = baud_bpr_60[i]; /* Rx BPR */ - info->rco = baud_co_60[i]; /* Rx CO */ - } else { - info->tbpr = baud_bpr_25[i]; /* Tx BPR */ - info->tco = baud_co_25[i]; /* Tx CO */ - info->rbpr = baud_bpr_25[i]; /* Rx BPR */ - info->rco = baud_co_25[i]; /* Rx CO */ - } - } - if (baud_table[i] == 134) { - /* get it right for 134.5 baud */ - info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + - 2; - } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_rate) + 2; - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - /* receive threshold */ - info->cor3 = (info->default_threshold ? - info->default_threshold : baud_cor3[i]); - info->cor2 = CyETC; - switch (cflag & CSIZE) { - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if (cflag & CSTOPB) - info->cor1 |= Cy_2_STOP; - - if (cflag & PARENB) { - if (cflag & PARODD) - info->cor1 |= CyPARITY_O; - else - info->cor1 |= CyPARITY_E; - } else - info->cor1 |= CyPARITY_NONE; - - /* CTS flow control flag */ - if (cflag & CRTSCTS) { - info->port.flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - } else { - info->port.flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - - channel &= 0x03; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel); - - /* tx and rx baud rate */ - - cyy_writeb(info, CyTCOR, info->tco); - cyy_writeb(info, CyTBPR, info->tbpr); - cyy_writeb(info, CyRCOR, info->rco); - cyy_writeb(info, CyRBPR, info->rbpr); - - /* set line characteristics according configuration */ - - cyy_writeb(info, CySCHR1, START_CHAR(tty)); - cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); - cyy_writeb(info, CyCOR1, info->cor1); - cyy_writeb(info, CyCOR2, info->cor2); - cyy_writeb(info, CyCOR3, info->cor3); - cyy_writeb(info, CyCOR4, info->cor4); - cyy_writeb(info, CyCOR5, info->cor5); - - cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | - CyCOR3ch); - - /* !!! Is this needed? */ - cyy_writeb(info, CyCAR, channel); - cyy_writeb(info, CyRTPR, - (info->default_timeout ? info->default_timeout : 0x02)); - /* 10ms rx timeout */ - - cflags = CyCTS; - if (!C_CLOCAL(tty)) - cflags |= CyDSR | CyRI | CyDCD; - /* without modem intr */ - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) - cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); - else - cyy_writeb(info, CyMCOR1, cflags); - /* act on 0->1 modem transitions */ - cyy_writeb(info, CyMCOR2, cflags); - - if (i == 0) /* baud rate is zero, turn off line */ - cyy_change_rts_dtr(info, 0, TIOCM_DTR); - else - cyy_change_rts_dtr(info, TIOCM_DTR, 0); - - clear_bit(TTY_IO_ERROR, &tty->flags); - spin_unlock_irqrestore(&card->card_lock, flags); - - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - __u32 sw_flow; - int retval; - - if (!cyz_is_loaded(card)) - return; - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - if (info->custom_divisor) - baud_rate = info->baud / info->custom_divisor; - else - baud_rate = info->baud; - } else if (baud > CYZ_MAX_SPEED) { - baud = CYZ_MAX_SPEED; - } - cy_writel(&ch_ctrl->comm_baud, baud); - - if (baud == 134) { - /* get it right for 134.5 baud */ - info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + - 2; - } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == - ASYNC_SPD_CUST) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud_rate) + 2; - } else if (baud) { - info->timeout = (info->xmit_fifo_size * HZ * 15 / - baud) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5); - break; - case CS6: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6); - break; - case CS7: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7); - break; - case CS8: - cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8); - break; - } - if (cflag & CSTOPB) { - cy_writel(&ch_ctrl->comm_data_l, - readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); - } else { - cy_writel(&ch_ctrl->comm_data_l, - readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); - } - if (cflag & PARENB) { - if (cflag & PARODD) - cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); - else - cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); - } else - cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); - - /* CTS flow control flag */ - if (cflag & CRTSCTS) { - cy_writel(&ch_ctrl->hw_flow, - readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); - } else { - cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) & - ~(C_RS_CTS | C_RS_RTS)); - } - /* As the HW flow control is done in firmware, the driver - doesn't need to care about it */ - info->port.flags &= ~ASYNC_CTS_FLOW; - - /* XON/XOFF/XANY flow control flags */ - sw_flow = 0; - if (iflag & IXON) { - sw_flow |= C_FL_OXX; - if (iflag & IXANY) - sw_flow |= C_FL_OIXANY; - } - cy_writel(&ch_ctrl->sw_flow, sw_flow); - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:set_line_char retval on ttyC%d " - "was %x\n", info->line, retval); - } - - /* CD sensitivity */ - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; - - if (baud == 0) { /* baud rate is zero, turn off line */ - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) & ~C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n"); -#endif - } else { - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) | C_RS_DTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n"); -#endif - } - - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " - "was %x\n", info->line, retval); - } - - clear_bit(TTY_IO_ERROR, &tty->flags); - } -} /* set_line_char */ - -static int cy_get_serial_info(struct cyclades_port *info, - struct serial_struct __user *retinfo) -{ - struct cyclades_card *cinfo = info->card; - struct serial_struct tmp = { - .type = info->type, - .line = info->line, - .port = (info->card - cy_card) * 0x100 + info->line - - cinfo->first_line, - .irq = cinfo->irq, - .flags = info->port.flags, - .close_delay = info->port.close_delay, - .closing_wait = info->port.closing_wait, - .baud_base = info->baud, - .custom_divisor = info->custom_divisor, - .hub6 = 0, /*!!! */ - }; - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} - -static int -cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, - struct serial_struct __user *new_info) -{ - struct serial_struct new_serial; - int ret; - - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - mutex_lock(&info->port.mutex); - if (!capable(CAP_SYS_ADMIN)) { - if (new_serial.close_delay != info->port.close_delay || - new_serial.baud_base != info->baud || - (new_serial.flags & ASYNC_FLAGS & - ~ASYNC_USR_MASK) != - (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) - { - mutex_unlock(&info->port.mutex); - return -EPERM; - } - info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK); - info->baud = new_serial.baud_base; - info->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud = new_serial.baud_base; - info->custom_divisor = new_serial.custom_divisor; - info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS); - info->port.close_delay = new_serial.close_delay * HZ / 100; - info->port.closing_wait = new_serial.closing_wait * HZ / 100; - -check_and_exit: - if (info->port.flags & ASYNC_INITIALIZED) { - cy_set_line_char(info, tty); - ret = 0; - } else { - ret = cy_startup(info, tty); - } - mutex_unlock(&info->port.mutex); - return ret; -} /* set_serial_info */ - -/* - * 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 cyclades_port *info, unsigned int __user *value) -{ - struct cyclades_card *card = info->card; - unsigned int result; - unsigned long flags; - u8 status; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); - spin_unlock_irqrestore(&card->card_lock, flags); - result = (status ? 0 : TIOCSER_TEMT); - } else { - /* Not supported yet */ - return -EINVAL; - } - return put_user(result, (unsigned long __user *)value); -} - -static int cy_tiocmget(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int result; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - card = info->card; - - if (!cy_is_Z(card)) { - unsigned long flags; - int channel = info->line - card->first_line; - u8 status; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - status = cyy_readb(info, CyMSVR1); - status |= cyy_readb(info, CyMSVR2); - spin_unlock_irqrestore(&card->card_lock, flags); - - if (info->rtsdtr_inv) { - result = ((status & CyRTS) ? TIOCM_DTR : 0) | - ((status & CyDTR) ? TIOCM_RTS : 0); - } else { - result = ((status & CyRTS) ? TIOCM_RTS : 0) | - ((status & CyDTR) ? TIOCM_DTR : 0); - } - result |= ((status & CyDCD) ? TIOCM_CAR : 0) | - ((status & CyRI) ? TIOCM_RNG : 0) | - ((status & CyDSR) ? TIOCM_DSR : 0) | - ((status & CyCTS) ? TIOCM_CTS : 0); - } else { - u32 lstatus; - - if (!cyz_is_loaded(card)) { - result = -ENODEV; - goto end; - } - - lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); - result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | - ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | - ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | - ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | - ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | - ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); - } -end: - return result; -} /* cy_tiomget */ - -static int -cy_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - card = info->card; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, set, clear); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int retval, channel = info->line - card->first_line; - u32 rs; - - if (!cyz_is_loaded(card)) - return -ENODEV; - - spin_lock_irqsave(&card->card_lock, flags); - rs = readl(&ch_ctrl->rs_control); - if (set & TIOCM_RTS) - rs |= C_RS_RTS; - if (clear & TIOCM_RTS) - rs &= ~C_RS_RTS; - if (set & TIOCM_DTR) { - rs |= C_RS_DTR; -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); -#endif - } - if (clear & TIOCM_DTR) { - rs &= ~C_RS_DTR; -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info clearing " - "Z DTR\n"); -#endif - } - cy_writel(&ch_ctrl->rs_control, rs); - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - spin_unlock_irqrestore(&card->card_lock, flags); - if (retval != 0) { - printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " - "was %x\n", info->line, retval); - } - } - return 0; -} - -/* - * cy_break() --- routine which turns the break handling on or off - */ -static int cy_break(struct tty_struct *tty, int break_state) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - int retval = 0; - - if (serial_paranoia_check(info, tty->name, "cy_break")) - return -EINVAL; - - card = info->card; - - spin_lock_irqsave(&card->card_lock, flags); - if (!cy_is_Z(card)) { - /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - if (break_state == -1) { - if (!info->breakon) { - info->breakon = 1; - if (!info->xmit_cnt) { - spin_unlock_irqrestore(&card->card_lock, flags); - start_xmit(info); - spin_lock_irqsave(&card->card_lock, flags); - } - } - } else { - if (!info->breakoff) { - info->breakoff = 1; - if (!info->xmit_cnt) { - spin_unlock_irqrestore(&card->card_lock, flags); - start_xmit(info); - spin_lock_irqsave(&card->card_lock, flags); - } - } - } - } else { - if (break_state == -1) { - retval = cyz_issue_cmd(card, - info->line - card->first_line, - C_CM_SET_BREAK, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:cy_break (set) retval on " - "ttyC%d was %x\n", info->line, retval); - } - } else { - retval = cyz_issue_cmd(card, - info->line - card->first_line, - C_CM_CLR_BREAK, 0L); - if (retval != 0) { - printk(KERN_DEBUG "cyc:cy_break (clr) retval " - "on ttyC%d was %x\n", info->line, - retval); - } - } - } - spin_unlock_irqrestore(&card->card_lock, flags); - return retval; -} /* cy_break */ - -static int set_threshold(struct cyclades_port *info, unsigned long value) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - - if (!cy_is_Z(card)) { - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyCOR3, info->cor3); - cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); - spin_unlock_irqrestore(&card->card_lock, flags); - } - return 0; -} /* set_threshold */ - -static int get_threshold(struct cyclades_port *info, - unsigned long __user *value) -{ - struct cyclades_card *card = info->card; - - if (!cy_is_Z(card)) { - u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; - return put_user(tmp, value); - } - return 0; -} /* get_threshold */ - -static int set_timeout(struct cyclades_port *info, unsigned long value) -{ - struct cyclades_card *card = info->card; - unsigned long flags; - - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_writeb(info, CyRTPR, value & 0xff); - spin_unlock_irqrestore(&card->card_lock, flags); - } - return 0; -} /* set_timeout */ - -static int get_timeout(struct cyclades_port *info, - unsigned long __user *value) -{ - struct cyclades_card *card = info->card; - - if (!cy_is_Z(card)) { - u8 tmp = cyy_readb(info, CyRTPR); - return put_user(tmp, value); - } - return 0; -} /* get_timeout */ - -static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, - struct cyclades_icount *cprev) -{ - struct cyclades_icount cnow; - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->card->card_lock, flags); - - ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); - - *cprev = cnow; - - return ret; -} - -/* - * This routine allows the tty driver to implement device- - * specific ioctl's. If the ioctl number passed in cmd is - * not recognized by the driver, it should return ENOIOCTLCMD. - */ -static int -cy_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cnow; /* kernel counter temps */ - int ret_val = 0; - unsigned long flags; - void __user *argp = (void __user *)arg; - - if (serial_paranoia_check(info, tty->name, "cy_ioctl")) - return -ENODEV; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", - info->line, cmd, arg); -#endif - - switch (cmd) { - case CYGETMON: - if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { - ret_val = -EFAULT; - break; - } - memset(&info->mon, 0, sizeof(info->mon)); - break; - case CYGETTHRESH: - ret_val = get_threshold(info, argp); - break; - case CYSETTHRESH: - ret_val = set_threshold(info, arg); - break; - case CYGETDEFTHRESH: - ret_val = put_user(info->default_threshold, - (unsigned long __user *)argp); - break; - case CYSETDEFTHRESH: - info->default_threshold = arg & 0x0f; - break; - case CYGETTIMEOUT: - ret_val = get_timeout(info, argp); - break; - case CYSETTIMEOUT: - ret_val = set_timeout(info, arg); - break; - case CYGETDEFTIMEOUT: - ret_val = put_user(info->default_timeout, - (unsigned long __user *)argp); - break; - case CYSETDEFTIMEOUT: - info->default_timeout = arg & 0xff; - break; - case CYSETRFLOW: - info->rflow = (int)arg; - break; - case CYGETRFLOW: - ret_val = info->rflow; - break; - case CYSETRTSDTR_INV: - info->rtsdtr_inv = (int)arg; - break; - case CYGETRTSDTR_INV: - ret_val = info->rtsdtr_inv; - break; - case CYGETCD1400VER: - ret_val = info->chip_rev; - break; -#ifndef CONFIG_CYZ_INTR - case CYZSETPOLLCYCLE: - cyz_polling_cycle = (arg * HZ) / 1000; - break; - case CYZGETPOLLCYCLE: - ret_val = (cyz_polling_cycle * 1000) / HZ; - break; -#endif /* CONFIG_CYZ_INTR */ - case CYSETWAIT: - info->port.closing_wait = (unsigned short)arg * HZ / 100; - break; - case CYGETWAIT: - ret_val = info->port.closing_wait / (HZ / 100); - break; - case TIOCGSERIAL: - ret_val = cy_get_serial_info(info, argp); - break; - case TIOCSSERIAL: - ret_val = cy_set_serial_info(info, tty, argp); - break; - case TIOCSERGETLSR: /* Get line status register */ - ret_val = get_lsr_info(info, argp); - break; - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - spin_lock_irqsave(&info->card->card_lock, flags); - /* note the counters on entry */ - cnow = info->icount; - spin_unlock_irqrestore(&info->card->card_lock, flags); - ret_val = wait_event_interruptible(info->port.delta_msr_wait, - cy_cflags_changed(info, arg, &cnow)); - break; - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - default: - ret_val = -ENOIOCTLCMD; - } - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_ioctl done\n"); -#endif - return ret_val; -} /* cy_ioctl */ - -static int cy_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *sic) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cnow; /* Used to snapshot */ - unsigned long flags; - - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->card->card_lock, flags); - - sic->cts = cnow.cts; - sic->dsr = cnow.dsr; - sic->rng = cnow.rng; - sic->dcd = cnow.dcd; - sic->rx = cnow.rx; - sic->tx = cnow.tx; - sic->frame = cnow.frame; - sic->overrun = cnow.overrun; - sic->parity = cnow.parity; - sic->brk = cnow.brk; - sic->buf_overrun = cnow.buf_overrun; - return 0; -} - -/* - * This routine allows the tty driver to be notified when - * device's termios settings have changed. Note that a - * well-designed tty driver should be prepared to accept the case - * where old == NULL, and try to do something rational. - */ -static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); -#endif - - cy_set_line_char(info, tty); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - cy_start(tty); - } -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->port.open_wait); -#endif -} /* cy_set_termios */ - -/* This function is used to send a high-priority XON/XOFF character to - the device. -*/ -static void cy_send_xchar(struct tty_struct *tty, char ch) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - int channel; - - if (serial_paranoia_check(info, tty->name, "cy_send_xchar")) - return; - - info->x_char = ch; - - if (ch) - cy_start(tty); - - card = info->card; - channel = info->line - card->first_line; - - if (cy_is_Z(card)) { - if (ch == STOP_CHAR(tty)) - cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L); - else if (ch == START_CHAR(tty)) - cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L); - } -} - -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled because the input - buffers are close to full. - */ -static void cy_throttle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - -#ifdef CY_DEBUG_THROTTLE - char buf[64]; - - printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty), info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_throttle")) - return; - - card = info->card; - - if (I_IXOFF(tty)) { - if (!cy_is_Z(card)) - cy_send_xchar(tty, STOP_CHAR(tty)); - else - info->throttle = 1; - } - - if (tty->termios->c_cflag & CRTSCTS) { - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, 0, TIOCM_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - info->throttle = 1; - } - } -} /* cy_throttle */ - -/* - * This routine notifies the tty driver that it should signal - * that characters can now be sent to the tty without fear of - * overrunning the input buffers of the line disciplines. - */ -static void cy_unthrottle(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - struct cyclades_card *card; - unsigned long flags; - -#ifdef CY_DEBUG_THROTTLE - char buf[64]; - - printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", - tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - cy_send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) { - card = info->card; - if (!cy_is_Z(card)) { - spin_lock_irqsave(&card->card_lock, flags); - cyy_change_rts_dtr(info, TIOCM_RTS, 0); - spin_unlock_irqrestore(&card->card_lock, flags); - } else { - info->throttle = 0; - } - } -} /* cy_unthrottle */ - -/* cy_start and cy_stop provide software output flow control as a - function of XON/XOFF, software CTS, and other such stuff. -*/ -static void cy_stop(struct tty_struct *tty) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info = tty->driver_data; - int channel; - unsigned long flags; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_stop")) - return; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - if (!cy_is_Z(cinfo)) { - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); - spin_unlock_irqrestore(&cinfo->card_lock, flags); - } -} /* cy_stop */ - -static void cy_start(struct tty_struct *tty) -{ - struct cyclades_card *cinfo; - struct cyclades_port *info = tty->driver_data; - int channel; - unsigned long flags; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_start")) - return; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - if (!cy_is_Z(cinfo)) { - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); - spin_unlock_irqrestore(&cinfo->card_lock, flags); - } -} /* cy_start */ - -/* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void cy_hangup(struct tty_struct *tty) -{ - struct cyclades_port *info = tty->driver_data; - -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->name, "cy_hangup")) - return; - - cy_flush_buffer(tty); - cy_shutdown(info, tty); - tty_port_hangup(&info->port); -} /* cy_hangup */ - -static int cyy_carrier_raised(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - unsigned long flags; - int channel = info->line - cinfo->first_line; - u32 cd; - - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_writeb(info, CyCAR, channel & 0x03); - cd = cyy_readb(info, CyMSVR1) & CyDCD; - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - return cd; -} - -static void cyy_dtr_rts(struct tty_port *port, int raise) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - unsigned long flags; - - spin_lock_irqsave(&cinfo->card_lock, flags); - cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, - raise ? 0 : TIOCM_RTS | TIOCM_DTR); - spin_unlock_irqrestore(&cinfo->card_lock, flags); -} - -static int cyz_carrier_raised(struct tty_port *port) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - - return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; -} - -static void cyz_dtr_rts(struct tty_port *port, int raise) -{ - struct cyclades_port *info = container_of(port, struct cyclades_port, - port); - struct cyclades_card *cinfo = info->card; - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int ret, channel = info->line - cinfo->first_line; - u32 rs; - - rs = readl(&ch_ctrl->rs_control); - if (raise) - rs |= C_RS_RTS | C_RS_DTR; - else - rs &= ~(C_RS_RTS | C_RS_DTR); - cy_writel(&ch_ctrl->rs_control, rs); - ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); - if (ret != 0) - printk(KERN_ERR "%s: retval on ttyC%d was %x\n", - __func__, info->line, ret); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); -#endif -} - -static const struct tty_port_operations cyy_port_ops = { - .carrier_raised = cyy_carrier_raised, - .dtr_rts = cyy_dtr_rts, - .shutdown = cy_do_close, -}; - -static const struct tty_port_operations cyz_port_ops = { - .carrier_raised = cyz_carrier_raised, - .dtr_rts = cyz_dtr_rts, - .shutdown = cy_do_close, -}; - -/* - * --------------------------------------------------------------------- - * cy_init() and friends - * - * cy_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -static int __devinit cy_init_card(struct cyclades_card *cinfo) -{ - struct cyclades_port *info; - unsigned int channel, port; - - spin_lock_init(&cinfo->card_lock); - cinfo->intr_enabled = 0; - - cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports), - GFP_KERNEL); - if (cinfo->ports == NULL) { - printk(KERN_ERR "Cyclades: cannot allocate ports\n"); - return -ENOMEM; - } - - for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; - channel++, port++) { - info = &cinfo->ports[channel]; - tty_port_init(&info->port); - info->magic = CYCLADES_MAGIC; - info->card = cinfo; - info->line = port; - - info->port.closing_wait = CLOSING_WAIT_DELAY; - info->port.close_delay = 5 * HZ / 10; - info->port.flags = STD_COM_FLAGS; - init_completion(&info->shutdown_wait); - - if (cy_is_Z(cinfo)) { - struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; - struct ZFW_CTRL *zfw_ctrl; - - info->port.ops = &cyz_port_ops; - info->type = PORT_STARTECH; - - zfw_ctrl = cinfo->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; - info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; - - if (cinfo->hw_ver == ZO_V1) - info->xmit_fifo_size = CYZ_FIFO_SIZE; - else - info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; -#ifdef CONFIG_CYZ_INTR - setup_timer(&cyz_rx_full_timer[port], - cyz_rx_restart, (unsigned long)info); -#endif - } else { - unsigned short chip_number; - int index = cinfo->bus_index; - - info->port.ops = &cyy_port_ops; - info->type = PORT_CIRRUS; - info->xmit_fifo_size = CyMAX_CHAR_FIFO; - info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small rcv threshold */ - - chip_number = channel / CyPORTS_PER_CHIP; - info->u.cyy.base_addr = cinfo->base_addr + - (cy_chip_offset[chip_number] << index); - info->chip_rev = cyy_readb(info, CyGFRCR); - - if (info->chip_rev >= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - info->tbpr = baud_bpr_60[13]; /* Tx BPR */ - info->tco = baud_co_60[13]; /* Tx CO */ - info->rbpr = baud_bpr_60[13]; /* Rx BPR */ - info->rco = baud_co_60[13]; /* Rx CO */ - info->rtsdtr_inv = 1; - } else { - info->tbpr = baud_bpr_25[13]; /* Tx BPR */ - info->tco = baud_co_25[13]; /* Tx CO */ - info->rbpr = baud_bpr_25[13]; /* Rx BPR */ - info->rco = baud_co_25[13]; /* Rx CO */ - info->rtsdtr_inv = 0; - } - info->read_status_mask = CyTIMEOUT | CySPECHAR | - CyBREAK | CyPARITY | CyFRAME | CyOVERRUN; - } - - } - -#ifndef CONFIG_CYZ_INTR - if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) { - mod_timer(&cyz_timerlist, jiffies + 1); -#ifdef CY_PCI_DEBUG - printk(KERN_DEBUG "Cyclades-Z polling initialized\n"); -#endif - } -#endif - return 0; -} - -/* initialize chips on Cyclom-Y card -- return number of valid - chips (which is number of ports/4) */ -static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, - int index) -{ - unsigned int chip_number; - void __iomem *base_addr; - - cy_writeb(true_base_addr + (Cy_HwReset << index), 0); - /* Cy_HwReset is 0x1400 */ - cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0); - /* Cy_ClrIntr is 0x1800 */ - udelay(500L); - - for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; - chip_number++) { - base_addr = - true_base_addr + (cy_chip_offset[chip_number] << index); - mdelay(1); - if (readb(base_addr + (CyCCR << index)) != 0x00) { - /************* - printk(" chip #%d at %#6lx is never idle (CCR != 0)\n", - chip_number, (unsigned long)base_addr); - *************/ - return chip_number; - } - - cy_writeb(base_addr + (CyGFRCR << index), 0); - udelay(10L); - - /* The Cyclom-16Y does not decode address bit 9 and therefore - cannot distinguish between references to chip 0 and a non- - existent chip 4. If the preceding clearing of the supposed - chip 4 GFRCR register appears at chip 0, there is no chip 4 - and this must be a Cyclom-16Y, not a Cyclom-32Ye. - */ - if (chip_number == 4 && readb(true_base_addr + - (cy_chip_offset[0] << index) + - (CyGFRCR << index)) == 0) { - return chip_number; - } - - cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET); - mdelay(1); - - if (readb(base_addr + (CyGFRCR << index)) == 0x00) { - /* - printk(" chip #%d at %#6lx is not responding ", - chip_number, (unsigned long)base_addr); - printk("(GFRCR stayed 0)\n", - */ - return chip_number; - } - if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) != - 0x40) { - /* - printk(" chip #%d at %#6lx is not valid (GFRCR == " - "%#2x)\n", - chip_number, (unsigned long)base_addr, - base_addr[CyGFRCR<= CD1400_REV_J) { - /* It is a CD1400 rev. J or later */ - /* Impossible to reach 5ms with this chip. - Changed to 2ms instead (f = 500 Hz). */ - cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS); - } else { - /* f = 200 Hz */ - cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS); - } - - /* - printk(" chip #%d at %#6lx is rev 0x%2x\n", - chip_number, (unsigned long)base_addr, - readb(base_addr+(CyGFRCR< NR_PORTS) { - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " - "more channels are available. Change NR_PORTS " - "in cyclades.c and recompile kernel.\n", - (unsigned long)cy_isa_address); - iounmap(cy_isa_address); - return nboard; - } - /* fill the next cy_card structure available */ - for (j = 0; j < NR_CARDS; j++) { - if (cy_card[j].base_addr == NULL) - break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " - "more cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n", - (unsigned long)cy_isa_address); - iounmap(cy_isa_address); - return nboard; - } - - /* allocate IRQ */ - if (request_irq(cy_isa_irq, cyy_interrupt, - IRQF_DISABLED, "Cyclom-Y", &cy_card[j])) { - printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " - "could not allocate IRQ#%d.\n", - (unsigned long)cy_isa_address, cy_isa_irq); - iounmap(cy_isa_address); - return nboard; - } - - /* set cy_card */ - cy_card[j].base_addr = cy_isa_address; - cy_card[j].ctl_addr.p9050 = NULL; - cy_card[j].irq = (int)cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; - cy_card[j].nports = cy_isa_nchan; - if (cy_init_card(&cy_card[j])) { - cy_card[j].base_addr = NULL; - free_irq(cy_isa_irq, &cy_card[j]); - iounmap(cy_isa_address); - continue; - } - nboard++; - - printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " - "%d channels starting from port %d\n", - j + 1, (unsigned long)cy_isa_address, - (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), - cy_isa_irq, cy_isa_nchan, cy_next_channel); - - for (j = cy_next_channel; - j < cy_next_channel + cy_isa_nchan; j++) - tty_register_device(cy_serial_driver, j, NULL); - cy_next_channel += cy_isa_nchan; - } - return nboard; -#else - return 0; -#endif /* CONFIG_ISA */ -} /* cy_detect_isa */ - -#ifdef CONFIG_PCI -static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) -{ - unsigned int a; - - for (a = 0; a < size && *str; a++, str++) - if (*str & 0x80) - return -EINVAL; - - for (; a < size; a++, str++) - if (*str) - return -EINVAL; - - return 0; -} - -static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data, - unsigned int size) -{ - for (; size > 0; size--) { - cy_writel(fpga, *data++); - udelay(10); - } -} - -static void __devinit plx_init(struct pci_dev *pdev, int irq, - struct RUNTIME_9060 __iomem *addr) -{ - /* Reset PLX */ - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); - udelay(100L); - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); - - /* Reload Config. Registers from EEPROM */ - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); - udelay(100L); - cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); - - /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, - * the IRQ is lost and, thus, we have to re-write it to the PCI config. - * registers. This will remain here until we find a permanent fix. - */ - pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); -} - -static int __devinit __cyz_load_fw(const struct firmware *fw, - const char *name, const u32 mailbox, void __iomem *base, - void __iomem *fpga) -{ - const void *ptr = fw->data; - const struct zfile_header *h = ptr; - const struct zfile_config *c, *cs; - const struct zfile_block *b, *bs; - unsigned int a, tmp, len = fw->size; -#define BAD_FW KERN_ERR "Bad firmware: " - if (len < sizeof(*h)) { - printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); - return -EINVAL; - } - - cs = ptr + h->config_offset; - bs = ptr + h->block_offset; - - if ((void *)(cs + h->n_config) > ptr + len || - (void *)(bs + h->n_blocks) > ptr + len) { - printk(BAD_FW "too short"); - return -EINVAL; - } - - if (cyc_isfwstr(h->name, sizeof(h->name)) || - cyc_isfwstr(h->date, sizeof(h->date))) { - printk(BAD_FW "bad formatted header string\n"); - return -EINVAL; - } - - if (strncmp(name, h->name, sizeof(h->name))) { - printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); - return -EINVAL; - } - - tmp = 0; - for (c = cs; c < cs + h->n_config; c++) { - for (a = 0; a < c->n_blocks; a++) - if (c->block_list[a] > h->n_blocks) { - printk(BAD_FW "bad block ref number in cfgs\n"); - return -EINVAL; - } - if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ - tmp++; - } - if (!tmp) { - printk(BAD_FW "nothing appropriate\n"); - return -EINVAL; - } - - for (b = bs; b < bs + h->n_blocks; b++) - if (b->file_offset + b->size > len) { - printk(BAD_FW "bad block data offset\n"); - return -EINVAL; - } - - /* everything is OK, let's seek'n'load it */ - for (c = cs; c < cs + h->n_config; c++) - if (c->mailbox == mailbox && c->function == 0) - break; - - for (a = 0; a < c->n_blocks; a++) { - b = &bs[c->block_list[a]]; - if (b->type == ZBLOCK_FPGA) { - if (fpga != NULL) - cyz_fpga_copy(fpga, ptr + b->file_offset, - b->size); - } else { - if (base != NULL) - memcpy_toio(base + b->ram_offset, - ptr + b->file_offset, b->size); - } - } -#undef BAD_FW - return 0; -} - -static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, - struct RUNTIME_9060 __iomem *ctl_addr, int irq) -{ - const struct firmware *fw; - struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; - struct CUSTOM_REG __iomem *cust = base_addr; - struct ZFW_CTRL __iomem *pt_zfwctrl; - void __iomem *tmp; - u32 mailbox, status, nchan; - unsigned int i; - int retval; - - retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); - if (retval) { - dev_err(&pdev->dev, "can't get firmware\n"); - goto err; - } - - /* Check whether the firmware is already loaded and running. If - positive, skip this board */ - if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { - u32 cntval = readl(base_addr + 0x190); - - udelay(100); - if (cntval != readl(base_addr + 0x190)) { - /* FW counter is working, FW is running */ - dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " - "Skipping board.\n"); - retval = 0; - goto err_rel; - } - } - - /* start boot */ - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & - ~0x00030800UL); - - mailbox = readl(&ctl_addr->mail_box_0); - - if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) { - /* stops CPU and set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_stop, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - udelay(100); - } - - plx_init(pdev, irq, ctl_addr); - - if (mailbox != 0) { - /* load FPGA */ - retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, - base_addr); - if (retval) - goto err_rel; - if (!__cyz_fpga_loaded(ctl_addr)) { - dev_err(&pdev->dev, "fw upload successful, but fw is " - "not loaded\n"); - goto err_rel; - } - } - - /* stops CPU and set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_stop, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - udelay(100); - - /* clear memory */ - for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) - cy_writeb(tmp, 255); - if (mailbox != 0) { - /* set window to last 512K of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); - for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) - cy_writeb(tmp, 255); - /* set window to beginning of RAM */ - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - } - - retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); - release_firmware(fw); - if (retval) - goto err; - - /* finish boot and start boards */ - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - cy_writel(&cust->cpu_start, 0); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - i = 0; - while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) - msleep(100); - if (status != ZFIRM_ID) { - if (status == ZFIRM_HLT) { - dev_err(&pdev->dev, "you need an external power supply " - "for this number of ports. Firmware halted and " - "board reset.\n"); - retval = -EIO; - goto err; - } - dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " - "some more time\n", status); - while ((status = readl(&fid->signature)) != ZFIRM_ID && - i++ < 200) - msleep(100); - if (status != ZFIRM_ID) { - dev_err(&pdev->dev, "Board not started in 20 seconds! " - "Giving up. (fid->signature = 0x%x)\n", - status); - dev_info(&pdev->dev, "*** Warning ***: if you are " - "upgrading the FW, please power cycle the " - "system before loading the new FW to the " - "Cyclades-Z.\n"); - - if (__cyz_fpga_loaded(ctl_addr)) - plx_init(pdev, irq, ctl_addr); - - retval = -EIO; - goto err; - } - dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", - i / 10); - } - pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); - - dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", - base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), - base_addr + readl(&fid->zfwctrl_addr)); - - nchan = readl(&pt_zfwctrl->board_ctrl.n_channel); - dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", - readl(&pt_zfwctrl->board_ctrl.fw_version), nchan); - - if (nchan == 0) { - dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " - "check the connection between the Z host card and the " - "serial expanders.\n"); - - if (__cyz_fpga_loaded(ctl_addr)) - plx_init(pdev, irq, ctl_addr); - - dev_info(&pdev->dev, "Null number of ports detected. Board " - "reset.\n"); - retval = 0; - goto err; - } - - cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); - cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); - - /* - Early firmware failed to start looking for commands. - This enables firmware interrupts for those commands. - */ - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | - (1 << 17)); - cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | - 0x00030800UL); - - return nchan; -err_rel: - release_firmware(fw); -err: - return retval; -} - -static int __devinit cy_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - void __iomem *addr0 = NULL, *addr2 = NULL; - char *card_name = NULL; - u32 uninitialized_var(mailbox); - unsigned int device_id, nchan = 0, card_no, i; - unsigned char plx_ver; - int retval, irq; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "cannot enable device\n"); - goto err; - } - - /* read PCI configuration area */ - irq = pdev->irq; - device_id = pdev->device & ~PCI_DEVICE_ID_MASK; - -#if defined(__alpha__) - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ - dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " - "addresses on Alpha systems.\n"); - retval = -EIO; - goto err_dis; - } -#endif - if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { - dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " - "addresses\n"); - retval = -EIO; - goto err_dis; - } - - if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { - dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " - "it...\n"); - pdev->resource[2].flags &= ~IORESOURCE_IO; - } - - retval = pci_request_regions(pdev, "cyclades"); - if (retval) { - dev_err(&pdev->dev, "failed to reserve resources\n"); - goto err_dis; - } - - retval = -EIO; - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - card_name = "Cyclom-Y"; - - addr0 = ioremap_nocache(pci_resource_start(pdev, 0), - CyPCI_Yctl); - if (addr0 == NULL) { - dev_err(&pdev->dev, "can't remap ctl region\n"); - goto err_reg; - } - addr2 = ioremap_nocache(pci_resource_start(pdev, 2), - CyPCI_Ywin); - if (addr2 == NULL) { - dev_err(&pdev->dev, "can't remap base region\n"); - goto err_unmap; - } - - nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); - if (nchan == 0) { - dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " - "Serial-Modules\n"); - goto err_unmap; - } - } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { - struct RUNTIME_9060 __iomem *ctl_addr; - - ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0), - CyPCI_Zctl); - if (addr0 == NULL) { - dev_err(&pdev->dev, "can't remap ctl region\n"); - goto err_reg; - } - - /* Disable interrupts on the PLX before resetting it */ - cy_writew(&ctl_addr->intr_ctrl_stat, - readw(&ctl_addr->intr_ctrl_stat) & ~0x0900); - - plx_init(pdev, irq, addr0); - - mailbox = readl(&ctl_addr->mail_box_0); - - addr2 = ioremap_nocache(pci_resource_start(pdev, 2), - mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); - if (addr2 == NULL) { - dev_err(&pdev->dev, "can't remap base region\n"); - goto err_unmap; - } - - if (mailbox == ZE_V1) { - card_name = "Cyclades-Ze"; - } else { - card_name = "Cyclades-8Zo"; -#ifdef CY_PCI_DEBUG - if (mailbox == ZO_V1) { - cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); - dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " - "id %lx, ver %lx\n", (ulong)(0xff & - readl(&((struct CUSTOM_REG *)addr2)-> - fpga_id)), (ulong)(0xff & - readl(&((struct CUSTOM_REG *)addr2)-> - fpga_version))); - cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); - } else { - dev_info(&pdev->dev, "Cyclades-Z/PCI: New " - "Cyclades-Z board. FPGA not loaded\n"); - } -#endif - /* The following clears the firmware id word. This - ensures that the driver will not attempt to talk to - the board until it has been properly initialized. - */ - if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) - cy_writel(addr2 + ID_ADDRESS, 0L); - } - - retval = cyz_load_fw(pdev, addr2, addr0, irq); - if (retval <= 0) - goto err_unmap; - nchan = retval; - } - - if ((cy_next_channel + nchan) > NR_PORTS) { - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "channels are available. Change NR_PORTS in " - "cyclades.c and recompile kernel.\n"); - goto err_unmap; - } - /* fill the next cy_card structure available */ - for (card_no = 0; card_no < NR_CARDS; card_no++) { - if (cy_card[card_no].base_addr == NULL) - break; - } - if (card_no == NR_CARDS) { /* no more cy_cards available */ - dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " - "more cards can be used. Change NR_CARDS in " - "cyclades.c and recompile kernel.\n"); - goto err_unmap; - } - - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - /* allocate IRQ */ - retval = request_irq(irq, cyy_interrupt, - IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); - if (retval) { - dev_err(&pdev->dev, "could not allocate IRQ\n"); - goto err_unmap; - } - cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; - } else { - struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl; - - zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - - cy_card[card_no].hw_ver = mailbox; - cy_card[card_no].num_chips = (unsigned int)-1; - cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; -#ifdef CONFIG_CYZ_INTR - /* allocate IRQ only if board has an IRQ */ - if (irq != 0 && irq != 255) { - retval = request_irq(irq, cyz_interrupt, - IRQF_SHARED, "Cyclades-Z", - &cy_card[card_no]); - if (retval) { - dev_err(&pdev->dev, "could not allocate IRQ\n"); - goto err_unmap; - } - } -#endif /* CONFIG_CYZ_INTR */ - } - - /* set cy_card */ - cy_card[card_no].base_addr = addr2; - cy_card[card_no].ctl_addr.p9050 = addr0; - cy_card[card_no].irq = irq; - cy_card[card_no].bus_index = 1; - cy_card[card_no].first_line = cy_next_channel; - cy_card[card_no].nports = nchan; - retval = cy_init_card(&cy_card[card_no]); - if (retval) - goto err_null; - - pci_set_drvdata(pdev, &cy_card[card_no]); - - if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || - device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { - /* enable interrupts in the PCI interface */ - plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; - switch (plx_ver) { - case PLX_9050: - cy_writeb(addr0 + 0x4c, 0x43); - break; - - case PLX_9060: - case PLX_9080: - default: /* Old boards, use PLX_9060 */ - { - struct RUNTIME_9060 __iomem *ctl_addr = addr0; - plx_init(pdev, irq, ctl_addr); - cy_writew(&ctl_addr->intr_ctrl_stat, - readw(&ctl_addr->intr_ctrl_stat) | 0x0900); - break; - } - } - } - - dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " - "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); - for (i = cy_next_channel; i < cy_next_channel + nchan; i++) - tty_register_device(cy_serial_driver, i, &pdev->dev); - cy_next_channel += nchan; - - return 0; -err_null: - cy_card[card_no].base_addr = NULL; - free_irq(irq, &cy_card[card_no]); -err_unmap: - iounmap(addr0); - if (addr2) - iounmap(addr2); -err_reg: - pci_release_regions(pdev); -err_dis: - pci_disable_device(pdev); -err: - return retval; -} - -static void __devexit cy_pci_remove(struct pci_dev *pdev) -{ - struct cyclades_card *cinfo = pci_get_drvdata(pdev); - unsigned int i; - - /* non-Z with old PLX */ - if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == - PLX_9050) - cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0); - else -#ifndef CONFIG_CYZ_INTR - if (!cy_is_Z(cinfo)) -#endif - cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat, - readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) & - ~0x0900); - - iounmap(cinfo->base_addr); - if (cinfo->ctl_addr.p9050) - iounmap(cinfo->ctl_addr.p9050); - if (cinfo->irq -#ifndef CONFIG_CYZ_INTR - && !cy_is_Z(cinfo) -#endif /* CONFIG_CYZ_INTR */ - ) - free_irq(cinfo->irq, cinfo); - pci_release_regions(pdev); - - cinfo->base_addr = NULL; - for (i = cinfo->first_line; i < cinfo->first_line + - cinfo->nports; i++) - tty_unregister_device(cy_serial_driver, i); - cinfo->nports = 0; - kfree(cinfo->ports); -} - -static struct pci_driver cy_pci_driver = { - .name = "cyclades", - .id_table = cy_pci_dev_id, - .probe = cy_pci_probe, - .remove = __devexit_p(cy_pci_remove) -}; -#endif - -static int cyclades_proc_show(struct seq_file *m, void *v) -{ - struct cyclades_port *info; - unsigned int i, j; - __u32 cur_jifs = jiffies; - - seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " - "IdleIn Overruns Ldisc\n"); - - /* Output one line for each known port */ - for (i = 0; i < NR_CARDS; i++) - for (j = 0; j < cy_card[i].nports; j++) { - info = &cy_card[i].ports[j]; - - if (info->port.count) { - /* XXX is the ldisc num worth this? */ - struct tty_struct *tty; - struct tty_ldisc *ld; - int num = 0; - tty = tty_port_tty_get(&info->port); - if (tty) { - ld = tty_ldisc_ref(tty); - if (ld) { - num = ld->ops->num; - tty_ldisc_deref(ld); - } - tty_kref_put(tty); - } - seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6d\n", info->line, - (cur_jifs - info->idle_stats.in_use) / - HZ, info->idle_stats.xmit_bytes, - (cur_jifs - info->idle_stats.xmit_idle)/ - HZ, info->idle_stats.recv_bytes, - (cur_jifs - info->idle_stats.recv_idle)/ - HZ, info->idle_stats.overruns, - num); - } else - seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6ld\n", - info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); - } - return 0; -} - -static int cyclades_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cyclades_proc_show, NULL); -} - -static const struct file_operations cyclades_proc_fops = { - .owner = THIS_MODULE, - .open = cyclades_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* The serial driver boot-time initialization code! - Hardware I/O ports are mapped to character special devices on a - first found, first allocated manner. That is, this code searches - for Cyclom cards in the system. As each is found, it is probed - to discover how many chips (and thus how many ports) are present. - These ports are mapped to the tty ports 32 and upward in monotonic - fashion. If an 8-port card is replaced with a 16-port card, the - port mapping on a following card will shift. - - This approach is different from what is used in the other serial - device driver because the Cyclom is more properly a multiplexer, - not just an aggregation of serial ports on one card. - - If there are more cards with more ports than have been - statically allocated above, a warning is printed and the - extra ports are ignored. - */ - -static const struct tty_operations cy_ops = { - .open = cy_open, - .close = cy_close, - .write = cy_write, - .put_char = cy_put_char, - .flush_chars = cy_flush_chars, - .write_room = cy_write_room, - .chars_in_buffer = cy_chars_in_buffer, - .flush_buffer = cy_flush_buffer, - .ioctl = cy_ioctl, - .throttle = cy_throttle, - .unthrottle = cy_unthrottle, - .set_termios = cy_set_termios, - .stop = cy_stop, - .start = cy_start, - .hangup = cy_hangup, - .break_ctl = cy_break, - .wait_until_sent = cy_wait_until_sent, - .tiocmget = cy_tiocmget, - .tiocmset = cy_tiocmset, - .get_icount = cy_get_icount, - .proc_fops = &cyclades_proc_fops, -}; - -static int __init cy_init(void) -{ - unsigned int nboards; - int retval = -ENOMEM; - - cy_serial_driver = alloc_tty_driver(NR_PORTS); - if (!cy_serial_driver) - goto err; - - printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n", - __DATE__, __TIME__); - - /* Initialize the tty_driver structure */ - - cy_serial_driver->owner = THIS_MODULE; - cy_serial_driver->driver_name = "cyclades"; - cy_serial_driver->name = "ttyC"; - cy_serial_driver->major = CYCLADES_MAJOR; - cy_serial_driver->minor_start = 0; - cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; - cy_serial_driver->init_termios = tty_std_termios; - cy_serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(cy_serial_driver, &cy_ops); - - retval = tty_register_driver(cy_serial_driver); - if (retval) { - printk(KERN_ERR "Couldn't register Cyclades serial driver\n"); - goto err_frtty; - } - - /* the code below is responsible to find the boards. Each different - type of board has its own detection routine. If a board is found, - the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the - availability of cy_card and cy_port data structures and updating - the cy_next_channel. */ - - /* look for isa boards */ - nboards = cy_detect_isa(); - -#ifdef CONFIG_PCI - /* look for pci boards */ - retval = pci_register_driver(&cy_pci_driver); - if (retval && !nboards) { - tty_unregister_driver(cy_serial_driver); - goto err_frtty; - } -#endif - - return 0; -err_frtty: - put_tty_driver(cy_serial_driver); -err: - return retval; -} /* cy_init */ - -static void __exit cy_cleanup_module(void) -{ - struct cyclades_card *card; - unsigned int i, e1; - -#ifndef CONFIG_CYZ_INTR - del_timer_sync(&cyz_timerlist); -#endif /* CONFIG_CYZ_INTR */ - - e1 = tty_unregister_driver(cy_serial_driver); - if (e1) - printk(KERN_ERR "failed to unregister Cyclades serial " - "driver(%d)\n", e1); - -#ifdef CONFIG_PCI - pci_unregister_driver(&cy_pci_driver); -#endif - - for (i = 0; i < NR_CARDS; i++) { - card = &cy_card[i]; - if (card->base_addr) { - /* clear interrupt */ - cy_writeb(card->base_addr + Cy_ClrIntr, 0); - iounmap(card->base_addr); - if (card->ctl_addr.p9050) - iounmap(card->ctl_addr.p9050); - if (card->irq -#ifndef CONFIG_CYZ_INTR - && !cy_is_Z(card) -#endif /* CONFIG_CYZ_INTR */ - ) - free_irq(card->irq, card); - for (e1 = card->first_line; e1 < card->first_line + - card->nports; e1++) - tty_unregister_device(cy_serial_driver, e1); - kfree(card->ports); - } - } - - put_tty_driver(cy_serial_driver); -} /* cy_cleanup_module */ - -module_init(cy_init); -module_exit(cy_cleanup_module); - -MODULE_LICENSE("GPL"); -MODULE_VERSION(CY_VERSION); -MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); -MODULE_FIRMWARE("cyzfirm.bin"); diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c deleted file mode 100644 index db1cf9c328d8..000000000000 --- a/drivers/char/isicom.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Original driver code supplied by Multi-Tech - * - * Changes - * 1/9/98 alan@lxorguk.ukuu.org.uk - * Merge to 2.0.x kernel tree - * Obtain and use official major/minors - * Loader switched to a misc device - * (fixed range check bug as a side effect) - * Printk clean up - * 9/12/98 alan@lxorguk.ukuu.org.uk - * Rough port to 2.1.x - * - * 10/6/99 sameer Merged the ISA and PCI drivers to - * a new unified driver. - * - * 3/9/99 sameer Added support for ISI4616 cards. - * - * 16/9/99 sameer We do not force RTS low anymore. - * This is to prevent the firmware - * from getting confused. - * - * 26/10/99 sameer Cosmetic changes:The driver now - * dumps the Port Count information - * along with I/O address and IRQ. - * - * 13/12/99 sameer Fixed the problem with IRQ sharing. - * - * 10/5/00 sameer Fixed isicom_shutdown_board() - * to not lower DTR on all the ports - * when the last port on the card is - * closed. - * - * 10/5/00 sameer Signal mask setup command added - * to isicom_setup_port and - * isicom_shutdown_port. - * - * 24/5/00 sameer The driver is now SMP aware. - * - * - * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem - * - * - * 03/01/01 anil .s Added support for resetting the - * internal modems on ISI cards. - * - * 08/02/01 anil .s Upgraded the driver for kernel - * 2.4.x - * - * 11/04/01 Kevin Fixed firmware load problem with - * ISIHP-4X card - * - * 30/04/01 anil .s Fixed the remote login through - * ISI port problem. Now the link - * does not go down before password - * prompt. - * - * 03/05/01 anil .s Fixed the problem with IRQ sharing - * among ISI-PCI cards. - * - * 03/05/01 anil .s Added support to display the version - * info during insmod as well as module - * listing by lsmod. - * - * 10/05/01 anil .s Done the modifications to the source - * file and Install script so that the - * same installation can be used for - * 2.2.x and 2.4.x kernel. - * - * 06/06/01 anil .s Now we drop both dtr and rts during - * shutdown_port as well as raise them - * during isicom_config_port. - * - * 09/06/01 acme@conectiva.com.br use capable, not suser, do - * restore_flags on failure in - * isicom_send_break, verify put_user - * result - * - * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps - * Baud index extended to 21 - * - * 20/03/03 ranjeeth Made to work for Linux Advanced server. - * Taken care of license warning. - * - * 10/12/03 Ravindra Made to work for Fedora Core 1 of - * Red Hat Distribution - * - * 06/01/05 Alan Cox Merged the ISI and base kernel strands - * into a single 2.6 driver - * - * *********************************************************** - * - * To use this driver you also need the support package. You - * can find this in RPM format on - * ftp://ftp.linux.org.uk/pub/linux/alan - * - * You can find the original tools for this direct from Multitech - * ftp://ftp.multitech.com/ISI-Cards/ - * - * Having installed the cards the module options (/etc/modprobe.conf) - * - * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 - * - * Omit those entries for boards you don't have installed. - * - * TODO - * Merge testing - * 64-bit verification - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#define InterruptTheCard(base) outw(0, (base) + 0xc) -#define ClearInterrupt(base) inw((base) + 0x0a) - -#ifdef DEBUG -#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c)) -#else -#define isicom_paranoia_check(a, b, c) 0 -#endif - -static int isicom_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit isicom_remove(struct pci_dev *); - -static struct pci_device_id isicom_pci_tbl[] = { - { PCI_DEVICE(VENDOR_ID, 0x2028) }, - { PCI_DEVICE(VENDOR_ID, 0x2051) }, - { PCI_DEVICE(VENDOR_ID, 0x2052) }, - { PCI_DEVICE(VENDOR_ID, 0x2053) }, - { PCI_DEVICE(VENDOR_ID, 0x2054) }, - { PCI_DEVICE(VENDOR_ID, 0x2055) }, - { PCI_DEVICE(VENDOR_ID, 0x2056) }, - { PCI_DEVICE(VENDOR_ID, 0x2057) }, - { PCI_DEVICE(VENDOR_ID, 0x2058) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, isicom_pci_tbl); - -static struct pci_driver isicom_driver = { - .name = "isicom", - .id_table = isicom_pci_tbl, - .probe = isicom_probe, - .remove = __devexit_p(isicom_remove) -}; - -static int prev_card = 3; /* start servicing isi_card[0] */ -static struct tty_driver *isicom_normal; - -static void isicom_tx(unsigned long _data); -static void isicom_start(struct tty_struct *tty); - -static DEFINE_TIMER(tx, isicom_tx, 0, 0); - -/* baud index mappings from linux defns to isi */ - -static signed char linuxb_to_isib[] = { - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21 -}; - -struct isi_board { - unsigned long base; - int irq; - unsigned char port_count; - unsigned short status; - unsigned short port_status; /* each bit for each port */ - unsigned short shift_count; - struct isi_port *ports; - signed char count; - spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ - unsigned long flags; - unsigned int index; -}; - -struct isi_port { - unsigned short magic; - struct tty_port port; - u16 channel; - u16 status; - struct isi_board *card; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; -}; - -static struct isi_board isi_card[BOARD_COUNT]; -static struct isi_port isi_ports[PORT_COUNT]; - -/* - * Locking functions for card level locking. We need to own both - * the kernel lock for the card and have the card in a position that - * it wants to talk. - */ - -static inline int WaitTillCardIsFree(unsigned long base) -{ - unsigned int count = 0; - unsigned int a = in_atomic(); /* do we run under spinlock? */ - - while (!(inw(base + 0xe) & 0x1) && count++ < 100) - if (a) - mdelay(1); - else - msleep(1); - - return !(inw(base + 0xe) & 0x1); -} - -static int lock_card(struct isi_board *card) -{ - unsigned long base = card->base; - unsigned int retries, a; - - for (retries = 0; retries < 10; retries++) { - spin_lock_irqsave(&card->card_lock, card->flags); - for (a = 0; a < 10; a++) { - if (inw(base + 0xe) & 0x1) - return 1; - udelay(10); - } - spin_unlock_irqrestore(&card->card_lock, card->flags); - msleep(10); - } - pr_warning("Failed to lock Card (0x%lx)\n", card->base); - - return 0; /* Failed to acquire the card! */ -} - -static void unlock_card(struct isi_board *card) -{ - spin_unlock_irqrestore(&card->card_lock, card->flags); -} - -/* - * ISI Card specific ops ... - */ - -/* card->lock HAS to be held */ -static void raise_dtr(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0504, base); - InterruptTheCard(base); - port->status |= ISI_DTR; -} - -/* card->lock HAS to be held */ -static inline void drop_dtr(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0404, base); - InterruptTheCard(base); - port->status &= ~ISI_DTR; -} - -/* card->lock HAS to be held */ -static inline void raise_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0a04, base); - InterruptTheCard(base); - port->status |= ISI_RTS; -} - -/* card->lock HAS to be held */ -static inline void drop_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0804, base); - InterruptTheCard(base); - port->status &= ~ISI_RTS; -} - -/* card->lock MUST NOT be held */ - -static void isicom_dtr_rts(struct tty_port *port, int on) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - struct isi_board *card = ip->card; - unsigned long base = card->base; - u16 channel = ip->channel; - - if (!lock_card(card)) - return; - - if (on) { - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0f04, base); - InterruptTheCard(base); - ip->status |= (ISI_DTR | ISI_RTS); - } else { - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0C04, base); - InterruptTheCard(base); - ip->status &= ~(ISI_DTR | ISI_RTS); - } - unlock_card(card); -} - -/* card->lock HAS to be held */ -static void drop_dtr_rts(struct isi_port *port) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (WaitTillCardIsFree(base)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw(0x0c04, base); - InterruptTheCard(base); - port->status &= ~(ISI_RTS | ISI_DTR); -} - -/* - * ISICOM Driver specific routines ... - * - */ - -static inline int __isicom_paranoia_check(struct isi_port const *port, - char *name, const char *routine) -{ - if (!port) { - pr_warning("Warning: bad isicom magic for dev %s in %s.\n", - name, routine); - return 1; - } - if (port->magic != ISICOM_MAGIC) { - pr_warning("Warning: NULL isicom port for dev %s in %s.\n", - name, routine); - return 1; - } - - return 0; -} - -/* - * Transmitter. - * - * We shovel data into the card buffers on a regular basis. The card - * will do the rest of the work for us. - */ - -static void isicom_tx(unsigned long _data) -{ - unsigned long flags, base; - unsigned int retries; - short count = (BOARD_COUNT-1), card; - short txcount, wrd, residue, word_count, cnt; - struct isi_port *port; - struct tty_struct *tty; - - /* find next active board */ - card = (prev_card + 1) & 0x0003; - while (count-- > 0) { - if (isi_card[card].status & BOARD_ACTIVE) - break; - card = (card + 1) & 0x0003; - } - if (!(isi_card[card].status & BOARD_ACTIVE)) - goto sched_again; - - prev_card = card; - - count = isi_card[card].port_count; - port = isi_card[card].ports; - base = isi_card[card].base; - - spin_lock_irqsave(&isi_card[card].card_lock, flags); - for (retries = 0; retries < 100; retries++) { - if (inw(base + 0xe) & 0x1) - break; - udelay(2); - } - if (retries >= 100) - goto unlock; - - tty = tty_port_tty_get(&port->port); - if (tty == NULL) - goto put_unlock; - - for (; count > 0; count--, port++) { - /* port not active or tx disabled to force flow control */ - if (!(port->port.flags & ASYNC_INITIALIZED) || - !(port->status & ISI_TXOK)) - continue; - - txcount = min_t(short, TX_SIZE, port->xmit_cnt); - if (txcount <= 0 || tty->stopped || tty->hw_stopped) - continue; - - if (!(inw(base + 0x02) & (1 << port->channel))) - continue; - - pr_debug("txing %d bytes, port%d.\n", - txcount, port->channel + 1); - outw((port->channel << isi_card[card].shift_count) | txcount, - base); - residue = NO; - wrd = 0; - while (1) { - cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - - port->xmit_tail)); - if (residue == YES) { - residue = NO; - if (cnt > 0) { - wrd |= (port->port.xmit_buf[port->xmit_tail] - << 8); - port->xmit_tail = (port->xmit_tail + 1) - & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - txcount--; - cnt--; - outw(wrd, base); - } else { - outw(wrd, base); - break; - } - } - if (cnt <= 0) - break; - word_count = cnt >> 1; - outsw(base, port->port.xmit_buf+port->xmit_tail, word_count); - port->xmit_tail = (port->xmit_tail - + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); - txcount -= (word_count << 1); - port->xmit_cnt -= (word_count << 1); - if (cnt & 0x0001) { - residue = YES; - wrd = port->port.xmit_buf[port->xmit_tail]; - port->xmit_tail = (port->xmit_tail + 1) - & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - txcount--; - } - } - - InterruptTheCard(base); - if (port->xmit_cnt <= 0) - port->status &= ~ISI_TXOK; - if (port->xmit_cnt <= WAKEUP_CHARS) - tty_wakeup(tty); - } - -put_unlock: - tty_kref_put(tty); -unlock: - spin_unlock_irqrestore(&isi_card[card].card_lock, flags); - /* schedule another tx for hopefully in about 10ms */ -sched_again: - mod_timer(&tx, jiffies + msecs_to_jiffies(10)); -} - -/* - * Main interrupt handler routine - */ - -static irqreturn_t isicom_interrupt(int irq, void *dev_id) -{ - struct isi_board *card = dev_id; - struct isi_port *port; - struct tty_struct *tty; - unsigned long base; - u16 header, word_count, count, channel; - short byte_count; - unsigned char *rp; - - if (!card || !(card->status & FIRMWARE_LOADED)) - return IRQ_NONE; - - base = card->base; - - /* did the card interrupt us? */ - if (!(inw(base + 0x0e) & 0x02)) - return IRQ_NONE; - - spin_lock(&card->card_lock); - - /* - * disable any interrupts from the PCI card and lower the - * interrupt line - */ - outw(0x8000, base+0x04); - ClearInterrupt(base); - - inw(base); /* get the dummy word out */ - header = inw(base); - channel = (header & 0x7800) >> card->shift_count; - byte_count = header & 0xff; - - if (channel + 1 > card->port_count) { - pr_warning("%s(0x%lx): %d(channel) > port_count.\n", - __func__, base, channel+1); - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - port = card->ports + channel; - if (!(port->port.flags & ASYNC_INITIALIZED)) { - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - - tty = tty_port_tty_get(&port->port); - if (tty == NULL) { - word_count = byte_count >> 1; - while (byte_count > 1) { - inw(base); - byte_count -= 2; - } - if (byte_count & 0x01) - inw(base); - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - return IRQ_HANDLED; - } - - if (header & 0x8000) { /* Status Packet */ - header = inw(base); - switch (header & 0xff) { - case 0: /* Change in EIA signals */ - if (port->port.flags & ASYNC_CHECK_CD) { - if (port->status & ISI_DCD) { - if (!(header & ISI_DCD)) { - /* Carrier has been lost */ - pr_debug("%s: DCD->low.\n", - __func__); - port->status &= ~ISI_DCD; - tty_hangup(tty); - } - } else if (header & ISI_DCD) { - /* Carrier has been detected */ - pr_debug("%s: DCD->high.\n", - __func__); - port->status |= ISI_DCD; - wake_up_interruptible(&port->port.open_wait); - } - } else { - if (header & ISI_DCD) - port->status |= ISI_DCD; - else - port->status &= ~ISI_DCD; - } - - if (port->port.flags & ASYNC_CTS_FLOW) { - if (tty->hw_stopped) { - if (header & ISI_CTS) { - port->port.tty->hw_stopped = 0; - /* start tx ing */ - port->status |= (ISI_TXOK - | ISI_CTS); - tty_wakeup(tty); - } - } else if (!(header & ISI_CTS)) { - tty->hw_stopped = 1; - /* stop tx ing */ - port->status &= ~(ISI_TXOK | ISI_CTS); - } - } else { - if (header & ISI_CTS) - port->status |= ISI_CTS; - else - port->status &= ~ISI_CTS; - } - - if (header & ISI_DSR) - port->status |= ISI_DSR; - else - port->status &= ~ISI_DSR; - - if (header & ISI_RI) - port->status |= ISI_RI; - else - port->status &= ~ISI_RI; - - break; - - case 1: /* Received Break !!! */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - if (port->port.flags & ASYNC_SAK) - do_SAK(tty); - tty_flip_buffer_push(tty); - break; - - case 2: /* Statistics */ - pr_debug("%s: stats!!!\n", __func__); - break; - - default: - pr_debug("%s: Unknown code in status packet.\n", - __func__); - break; - } - } else { /* Data Packet */ - - count = tty_prepare_flip_string(tty, &rp, byte_count & ~1); - pr_debug("%s: Can rx %d of %d bytes.\n", - __func__, count, byte_count); - word_count = count >> 1; - insw(base, rp, word_count); - byte_count -= (word_count << 1); - if (count & 0x0001) { - tty_insert_flip_char(tty, inw(base) & 0xff, - TTY_NORMAL); - byte_count -= 2; - } - if (byte_count > 0) { - pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n", - __func__, base, channel + 1); - /* drain out unread xtra data */ - while (byte_count > 0) { - inw(base); - byte_count -= 2; - } - } - tty_flip_buffer_push(tty); - } - outw(0x0000, base+0x04); /* enable interrupts */ - spin_unlock(&card->card_lock); - tty_kref_put(tty); - - return IRQ_HANDLED; -} - -static void isicom_config_port(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long baud; - unsigned long base = card->base; - u16 channel_setup, channel = port->channel, - shift_count = card->shift_count; - unsigned char flow_ctrl; - - /* FIXME: Switch to new tty baud API */ - baud = C_BAUD(tty); - if (baud & CBAUDEX) { - baud &= ~CBAUDEX; - - /* if CBAUDEX bit is on and the baud is set to either 50 or 75 - * then the card is programmed for 57.6Kbps or 115Kbps - * respectively. - */ - - /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ - if (baud < 1 || baud > 4) - tty->termios->c_cflag &= ~CBAUDEX; - else - baud += 15; - } - if (baud == 15) { - - /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set - * by the set_serial_info ioctl ... this is done by - * the 'setserial' utility. - */ - - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - baud++; /* 57.6 Kbps */ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - baud += 2; /* 115 Kbps */ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - baud += 3; /* 230 kbps*/ - if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - baud += 4; /* 460 kbps*/ - } - if (linuxb_to_isib[baud] == -1) { - /* hang up */ - drop_dtr(port); - return; - } else - raise_dtr(port); - - if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) | 0x03, base); - outw(linuxb_to_isib[baud] << 8 | 0x03, base); - channel_setup = 0; - switch (C_CSIZE(tty)) { - case CS5: - channel_setup |= ISICOM_CS5; - break; - case CS6: - channel_setup |= ISICOM_CS6; - break; - case CS7: - channel_setup |= ISICOM_CS7; - break; - case CS8: - channel_setup |= ISICOM_CS8; - break; - } - - if (C_CSTOPB(tty)) - channel_setup |= ISICOM_2SB; - if (C_PARENB(tty)) { - channel_setup |= ISICOM_EVPAR; - if (C_PARODD(tty)) - channel_setup |= ISICOM_ODPAR; - } - outw(channel_setup, base); - InterruptTheCard(base); - } - if (C_CLOCAL(tty)) - port->port.flags &= ~ASYNC_CHECK_CD; - else - port->port.flags |= ASYNC_CHECK_CD; - - /* flow control settings ...*/ - flow_ctrl = 0; - port->port.flags &= ~ASYNC_CTS_FLOW; - if (C_CRTSCTS(tty)) { - port->port.flags |= ASYNC_CTS_FLOW; - flow_ctrl |= ISICOM_CTSRTS; - } - if (I_IXON(tty)) - flow_ctrl |= ISICOM_RESPOND_XONXOFF; - if (I_IXOFF(tty)) - flow_ctrl |= ISICOM_INITIATE_XONXOFF; - - if (WaitTillCardIsFree(base) == 0) { - outw(0x8000 | (channel << shift_count) | 0x04, base); - outw(flow_ctrl << 8 | 0x05, base); - outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); - InterruptTheCard(base); - } - - /* rx enabled -> enable port for rx on the card */ - if (C_CREAD(tty)) { - card->port_status |= (1 << channel); - outw(card->port_status, base + 0x02); - } -} - -/* open et all */ - -static inline void isicom_setup_board(struct isi_board *bp) -{ - int channel; - struct isi_port *port; - - bp->count++; - if (!(bp->status & BOARD_INIT)) { - port = bp->ports; - for (channel = 0; channel < bp->port_count; channel++, port++) - drop_dtr_rts(port); - } - bp->status |= BOARD_ACTIVE | BOARD_INIT; -} - -/* Activate and thus setup board are protected from races against shutdown - by the tty_port mutex */ - -static int isicom_activate(struct tty_port *tport, struct tty_struct *tty) -{ - struct isi_port *port = container_of(tport, struct isi_port, port); - struct isi_board *card = port->card; - unsigned long flags; - - if (tty_port_alloc_xmit_buf(tport) < 0) - return -ENOMEM; - - spin_lock_irqsave(&card->card_lock, flags); - isicom_setup_board(card); - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - - /* discard any residual data */ - if (WaitTillCardIsFree(card->base) == 0) { - outw(0x8000 | (port->channel << card->shift_count) | 0x02, - card->base); - outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); - InterruptTheCard(card->base); - } - isicom_config_port(tty); - spin_unlock_irqrestore(&card->card_lock, flags); - - return 0; -} - -static int isicom_carrier_raised(struct tty_port *port) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - return (ip->status & ISI_DCD)?1 : 0; -} - -static struct tty_port *isicom_find_port(struct tty_struct *tty) -{ - struct isi_port *port; - struct isi_board *card; - unsigned int board; - int line = tty->index; - - if (line < 0 || line > PORT_COUNT-1) - return NULL; - board = BOARD(line); - card = &isi_card[board]; - - if (!(card->status & FIRMWARE_LOADED)) - return NULL; - - /* open on a port greater than the port count for the card !!! */ - if (line > ((board * 16) + card->port_count - 1)) - return NULL; - - port = &isi_ports[line]; - if (isicom_paranoia_check(port, tty->name, "isicom_open")) - return NULL; - - return &port->port; -} - -static int isicom_open(struct tty_struct *tty, struct file *filp) -{ - struct isi_port *port; - struct tty_port *tport; - - tport = isicom_find_port(tty); - if (tport == NULL) - return -ENODEV; - port = container_of(tport, struct isi_port, port); - - tty->driver_data = port; - return tty_port_open(tport, tty, filp); -} - -/* close et all */ - -/* card->lock HAS to be held */ -static void isicom_shutdown_port(struct isi_port *port) -{ - struct isi_board *card = port->card; - - if (--card->count < 0) { - pr_debug("%s: bad board(0x%lx) count %d.\n", - __func__, card->base, card->count); - card->count = 0; - } - /* last port was closed, shutdown that board too */ - if (!card->count) - card->status &= BOARD_ACTIVE; -} - -static void isicom_flush_buffer(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) - return; - - spin_lock_irqsave(&card->card_lock, flags); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - spin_unlock_irqrestore(&card->card_lock, flags); - - tty_wakeup(tty); -} - -static void isicom_shutdown(struct tty_port *port) -{ - struct isi_port *ip = container_of(port, struct isi_port, port); - struct isi_board *card = ip->card; - unsigned long flags; - - /* indicate to the card that no more data can be received - on this port */ - spin_lock_irqsave(&card->card_lock, flags); - card->port_status &= ~(1 << ip->channel); - outw(card->port_status, card->base + 0x02); - isicom_shutdown_port(ip); - spin_unlock_irqrestore(&card->card_lock, flags); - tty_port_free_xmit_buf(port); -} - -static void isicom_close(struct tty_struct *tty, struct file *filp) -{ - struct isi_port *ip = tty->driver_data; - struct tty_port *port; - - if (ip == NULL) - return; - - port = &ip->port; - if (isicom_paranoia_check(ip, tty->name, "isicom_close")) - return; - tty_port_close(port, tty, filp); -} - -/* write et all */ -static int isicom_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - int cnt, total = 0; - - if (isicom_paranoia_check(port, tty->name, "isicom_write")) - return 0; - - spin_lock_irqsave(&card->card_lock, flags); - - while (1) { - cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - - 1, SERIAL_XMIT_SIZE - port->xmit_head)); - if (cnt <= 0) - break; - - memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt); - port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - - 1); - port->xmit_cnt += cnt; - buf += cnt; - count -= cnt; - total += cnt; - } - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) - port->status |= ISI_TXOK; - spin_unlock_irqrestore(&card->card_lock, flags); - return total; -} - -/* put_char et all */ -static int isicom_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_put_char")) - return 0; - - spin_lock_irqsave(&card->card_lock, flags); - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - spin_unlock_irqrestore(&card->card_lock, flags); - return 0; - } - - port->port.xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt++; - spin_unlock_irqrestore(&card->card_lock, flags); - return 1; -} - -/* flush_chars et all */ -static void isicom_flush_chars(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars")) - return; - - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !port->port.xmit_buf) - return; - - /* this tells the transmitter to consider this port for - data output to the card ... that's the best we can do. */ - port->status |= ISI_TXOK; -} - -/* write_room et all */ -static int isicom_write_room(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - int free; - - if (isicom_paranoia_check(port, tty->name, "isicom_write_room")) - return 0; - - free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (free < 0) - free = 0; - return free; -} - -/* chars_in_buffer et all */ -static int isicom_chars_in_buffer(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer")) - return 0; - return port->xmit_cnt; -} - -/* ioctl et all */ -static int isicom_send_break(struct tty_struct *tty, int length) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - unsigned long base = card->base; - - if (length == -1) - return -EOPNOTSUPP; - - if (!lock_card(card)) - return -EINVAL; - - outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); - outw((length & 0xff) << 8 | 0x00, base); - outw((length & 0xff00), base); - InterruptTheCard(base); - - unlock_card(card); - return 0; -} - -static int isicom_tiocmget(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - /* just send the port status */ - u16 status = port->status; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - return ((status & ISI_RTS) ? TIOCM_RTS : 0) | - ((status & ISI_DTR) ? TIOCM_DTR : 0) | - ((status & ISI_DCD) ? TIOCM_CAR : 0) | - ((status & ISI_DSR) ? TIOCM_DSR : 0) | - ((status & ISI_CTS) ? TIOCM_CTS : 0) | - ((status & ISI_RI ) ? TIOCM_RI : 0); -} - -static int isicom_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct isi_port *port = tty->driver_data; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - spin_lock_irqsave(&port->card->card_lock, flags); - if (set & TIOCM_RTS) - raise_rts(port); - if (set & TIOCM_DTR) - raise_dtr(port); - - if (clear & TIOCM_RTS) - drop_rts(port); - if (clear & TIOCM_DTR) - drop_dtr(port); - spin_unlock_irqrestore(&port->card->card_lock, flags); - - return 0; -} - -static int isicom_set_serial_info(struct tty_struct *tty, - struct serial_struct __user *info) -{ - struct isi_port *port = tty->driver_data; - struct serial_struct newinfo; - int reconfig_port; - - if (copy_from_user(&newinfo, info, sizeof(newinfo))) - return -EFAULT; - - mutex_lock(&port->port.mutex); - reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) != - (newinfo.flags & ASYNC_SPD_MASK)); - - if (!capable(CAP_SYS_ADMIN)) { - if ((newinfo.close_delay != port->port.close_delay) || - (newinfo.closing_wait != port->port.closing_wait) || - ((newinfo.flags & ~ASYNC_USR_MASK) != - (port->port.flags & ~ASYNC_USR_MASK))) { - mutex_unlock(&port->port.mutex); - return -EPERM; - } - port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | - (newinfo.flags & ASYNC_USR_MASK)); - } else { - port->port.close_delay = newinfo.close_delay; - port->port.closing_wait = newinfo.closing_wait; - port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | - (newinfo.flags & ASYNC_FLAGS)); - } - if (reconfig_port) { - unsigned long flags; - spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(tty); - spin_unlock_irqrestore(&port->card->card_lock, flags); - } - mutex_unlock(&port->port.mutex); - return 0; -} - -static int isicom_get_serial_info(struct isi_port *port, - struct serial_struct __user *info) -{ - struct serial_struct out_info; - - mutex_lock(&port->port.mutex); - memset(&out_info, 0, sizeof(out_info)); -/* out_info.type = ? */ - out_info.line = port - isi_ports; - out_info.port = port->card->base; - out_info.irq = port->card->irq; - out_info.flags = port->port.flags; -/* out_info.baud_base = ? */ - out_info.close_delay = port->port.close_delay; - out_info.closing_wait = port->port.closing_wait; - mutex_unlock(&port->port.mutex); - if (copy_to_user(info, &out_info, sizeof(out_info))) - return -EFAULT; - return 0; -} - -static int isicom_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct isi_port *port = tty->driver_data; - void __user *argp = (void __user *)arg; - - if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) - return -ENODEV; - - switch (cmd) { - case TIOCGSERIAL: - return isicom_get_serial_info(port, argp); - - case TIOCSSERIAL: - return isicom_set_serial_info(tty, argp); - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -/* set_termios et all */ -static void isicom_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct isi_port *port = tty->driver_data; - unsigned long flags; - - if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) - return; - - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - - spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(tty); - spin_unlock_irqrestore(&port->card->card_lock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - isicom_start(tty); - } -} - -/* throttle et all */ -static void isicom_throttle(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - - if (isicom_paranoia_check(port, tty->name, "isicom_throttle")) - return; - - /* tell the card that this port cannot handle any more data for now */ - card->port_status &= ~(1 << port->channel); - outw(card->port_status, card->base + 0x02); -} - -/* unthrottle et all */ -static void isicom_unthrottle(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - struct isi_board *card = port->card; - - if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle")) - return; - - /* tell the card that this port is ready to accept more data */ - card->port_status |= (1 << port->channel); - outw(card->port_status, card->base + 0x02); -} - -/* stop et all */ -static void isicom_stop(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_stop")) - return; - - /* this tells the transmitter not to consider this port for - data output to the card. */ - port->status &= ~ISI_TXOK; -} - -/* start et all */ -static void isicom_start(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_start")) - return; - - /* this tells the transmitter to consider this port for - data output to the card. */ - port->status |= ISI_TXOK; -} - -static void isicom_hangup(struct tty_struct *tty) -{ - struct isi_port *port = tty->driver_data; - - if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) - return; - tty_port_hangup(&port->port); -} - - -/* - * Driver init and deinit functions - */ - -static const struct tty_operations isicom_ops = { - .open = isicom_open, - .close = isicom_close, - .write = isicom_write, - .put_char = isicom_put_char, - .flush_chars = isicom_flush_chars, - .write_room = isicom_write_room, - .chars_in_buffer = isicom_chars_in_buffer, - .ioctl = isicom_ioctl, - .set_termios = isicom_set_termios, - .throttle = isicom_throttle, - .unthrottle = isicom_unthrottle, - .stop = isicom_stop, - .start = isicom_start, - .hangup = isicom_hangup, - .flush_buffer = isicom_flush_buffer, - .tiocmget = isicom_tiocmget, - .tiocmset = isicom_tiocmset, - .break_ctl = isicom_send_break, -}; - -static const struct tty_port_operations isicom_port_ops = { - .carrier_raised = isicom_carrier_raised, - .dtr_rts = isicom_dtr_rts, - .activate = isicom_activate, - .shutdown = isicom_shutdown, -}; - -static int __devinit reset_card(struct pci_dev *pdev, - const unsigned int card, unsigned int *signature) -{ - struct isi_board *board = pci_get_drvdata(pdev); - unsigned long base = board->base; - unsigned int sig, portcount = 0; - int retval = 0; - - dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1, - base); - - inw(base + 0x8); - - msleep(10); - - outw(0, base + 0x8); /* Reset */ - - msleep(1000); - - sig = inw(base + 0x4) & 0xff; - - if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd && - sig != 0xee) { - dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible " - "bad I/O Port Address 0x%lx).\n", card + 1, base); - dev_dbg(&pdev->dev, "Sig=0x%x\n", sig); - retval = -EIO; - goto end; - } - - msleep(10); - - portcount = inw(base + 0x2); - if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 && - portcount != 8 && portcount != 16)) { - dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n", - card + 1); - retval = -EIO; - goto end; - } - - switch (sig) { - case 0xa5: - case 0xbb: - case 0xdd: - board->port_count = (portcount == 4) ? 4 : 8; - board->shift_count = 12; - break; - case 0xcc: - case 0xee: - board->port_count = 16; - board->shift_count = 11; - break; - } - dev_info(&pdev->dev, "-Done\n"); - *signature = sig; - -end: - return retval; -} - -static int __devinit load_firmware(struct pci_dev *pdev, - const unsigned int index, const unsigned int signature) -{ - struct isi_board *board = pci_get_drvdata(pdev); - const struct firmware *fw; - unsigned long base = board->base; - unsigned int a; - u16 word_count, status; - int retval = -EIO; - char *name; - u8 *data; - - struct stframe { - u16 addr; - u16 count; - u8 data[0]; - } *frame; - - switch (signature) { - case 0xa5: - name = "isi608.bin"; - break; - case 0xbb: - name = "isi608em.bin"; - break; - case 0xcc: - name = "isi616em.bin"; - break; - case 0xdd: - name = "isi4608.bin"; - break; - case 0xee: - name = "isi4616.bin"; - break; - default: - dev_err(&pdev->dev, "Unknown signature.\n"); - goto end; - } - - retval = request_firmware(&fw, name, &pdev->dev); - if (retval) - goto end; - - retval = -EIO; - - for (frame = (struct stframe *)fw->data; - frame < (struct stframe *)(fw->data + fw->size); - frame = (struct stframe *)((u8 *)(frame + 1) + - frame->count)) { - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf0, base); /* start upload sequence */ - outw(0x00, base); - outw(frame->addr, base); /* lsb of address */ - - word_count = frame->count / 2 + frame->count % 2; - outw(word_count, base); - InterruptTheCard(base); - - udelay(100); /* 0x2f */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_warn(&pdev->dev, "Card%d rejected load header:\n" - "Address:0x%x\n" - "Count:0x%x\n" - "Status:0x%x\n", - index + 1, frame->addr, frame->count, status); - goto errrelfw; - } - outsw(base, frame->data, word_count); - - InterruptTheCard(base); - - udelay(50); /* 0x0f */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_err(&pdev->dev, "Card%d got out of sync.Card " - "Status:0x%x\n", index + 1, status); - goto errrelfw; - } - } - -/* XXX: should we test it by reading it back and comparing with original like - * in load firmware package? */ - for (frame = (struct stframe *)fw->data; - frame < (struct stframe *)(fw->data + fw->size); - frame = (struct stframe *)((u8 *)(frame + 1) + - frame->count)) { - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf1, base); /* start download sequence */ - outw(0x00, base); - outw(frame->addr, base); /* lsb of address */ - - word_count = (frame->count >> 1) + frame->count % 2; - outw(word_count + 1, base); - InterruptTheCard(base); - - udelay(50); /* 0xf */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_warn(&pdev->dev, "Card%d rejected verify header:\n" - "Address:0x%x\n" - "Count:0x%x\n" - "Status: 0x%x\n", - index + 1, frame->addr, frame->count, status); - goto errrelfw; - } - - data = kmalloc(word_count * 2, GFP_KERNEL); - if (data == NULL) { - dev_err(&pdev->dev, "Card%d, firmware upload " - "failed, not enough memory\n", index + 1); - goto errrelfw; - } - inw(base); - insw(base, data, word_count); - InterruptTheCard(base); - - for (a = 0; a < frame->count; a++) - if (data[a] != frame->data[a]) { - kfree(data); - dev_err(&pdev->dev, "Card%d, firmware upload " - "failed\n", index + 1); - goto errrelfw; - } - kfree(data); - - udelay(50); /* 0xf */ - - if (WaitTillCardIsFree(base)) - goto errrelfw; - - status = inw(base + 0x4); - if (status != 0) { - dev_err(&pdev->dev, "Card%d verify got out of sync. " - "Card Status:0x%x\n", index + 1, status); - goto errrelfw; - } - } - - /* xfer ctrl */ - if (WaitTillCardIsFree(base)) - goto errrelfw; - - outw(0xf2, base); - outw(0x800, base); - outw(0x0, base); - outw(0x0, base); - InterruptTheCard(base); - outw(0x0, base + 0x4); /* for ISI4608 cards */ - - board->status |= FIRMWARE_LOADED; - retval = 0; - -errrelfw: - release_firmware(fw); -end: - return retval; -} - -/* - * Insmod can set static symbols so keep these static - */ -static unsigned int card_count; - -static int __devinit isicom_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - unsigned int uninitialized_var(signature), index; - int retval = -EPERM; - struct isi_board *board = NULL; - - if (card_count >= BOARD_COUNT) - goto err; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "failed to enable\n"); - goto err; - } - - dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); - - /* allot the first empty slot in the array */ - for (index = 0; index < BOARD_COUNT; index++) { - if (isi_card[index].base == 0) { - board = &isi_card[index]; - break; - } - } - if (index == BOARD_COUNT) { - retval = -ENODEV; - goto err_disable; - } - - board->index = index; - board->base = pci_resource_start(pdev, 3); - board->irq = pdev->irq; - card_count++; - - pci_set_drvdata(pdev, board); - - retval = pci_request_region(pdev, 3, ISICOM_NAME); - if (retval) { - dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d " - "will be disabled.\n", board->base, board->base + 15, - index + 1); - retval = -EBUSY; - goto errdec; - } - - retval = request_irq(board->irq, isicom_interrupt, - IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board); - if (retval < 0) { - dev_err(&pdev->dev, "Could not install handler at Irq %d. " - "Card%d will be disabled.\n", board->irq, index + 1); - goto errunrr; - } - - retval = reset_card(pdev, index, &signature); - if (retval < 0) - goto errunri; - - retval = load_firmware(pdev, index, signature); - if (retval < 0) - goto errunri; - - for (index = 0; index < board->port_count; index++) - tty_register_device(isicom_normal, board->index * 16 + index, - &pdev->dev); - - return 0; - -errunri: - free_irq(board->irq, board); -errunrr: - pci_release_region(pdev, 3); -errdec: - board->base = 0; - card_count--; -err_disable: - pci_disable_device(pdev); -err: - return retval; -} - -static void __devexit isicom_remove(struct pci_dev *pdev) -{ - struct isi_board *board = pci_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < board->port_count; i++) - tty_unregister_device(isicom_normal, board->index * 16 + i); - - free_irq(board->irq, board); - pci_release_region(pdev, 3); - board->base = 0; - card_count--; - pci_disable_device(pdev); -} - -static int __init isicom_init(void) -{ - int retval, idx, channel; - struct isi_port *port; - - for (idx = 0; idx < BOARD_COUNT; idx++) { - port = &isi_ports[idx * 16]; - isi_card[idx].ports = port; - spin_lock_init(&isi_card[idx].card_lock); - for (channel = 0; channel < 16; channel++, port++) { - tty_port_init(&port->port); - port->port.ops = &isicom_port_ops; - port->magic = ISICOM_MAGIC; - port->card = &isi_card[idx]; - port->channel = channel; - port->port.close_delay = 50 * HZ/100; - port->port.closing_wait = 3000 * HZ/100; - port->status = 0; - /* . . . */ - } - isi_card[idx].base = 0; - isi_card[idx].irq = 0; - } - - /* tty driver structure initialization */ - isicom_normal = alloc_tty_driver(PORT_COUNT); - if (!isicom_normal) { - retval = -ENOMEM; - goto error; - } - - isicom_normal->owner = THIS_MODULE; - isicom_normal->name = "ttyM"; - isicom_normal->major = ISICOM_NMAJOR; - isicom_normal->minor_start = 0; - isicom_normal->type = TTY_DRIVER_TYPE_SERIAL; - isicom_normal->subtype = SERIAL_TYPE_NORMAL; - isicom_normal->init_termios = tty_std_termios; - isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | - CLOCAL; - isicom_normal->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK; - tty_set_operations(isicom_normal, &isicom_ops); - - retval = tty_register_driver(isicom_normal); - if (retval) { - pr_debug("Couldn't register the dialin driver\n"); - goto err_puttty; - } - - retval = pci_register_driver(&isicom_driver); - if (retval < 0) { - pr_err("Unable to register pci driver.\n"); - goto err_unrtty; - } - - mod_timer(&tx, jiffies + 1); - - return 0; -err_unrtty: - tty_unregister_driver(isicom_normal); -err_puttty: - put_tty_driver(isicom_normal); -error: - return retval; -} - -static void __exit isicom_exit(void) -{ - del_timer_sync(&tx); - - pci_unregister_driver(&isicom_driver); - tty_unregister_driver(isicom_normal); - put_tty_driver(isicom_normal); -} - -module_init(isicom_init); -module_exit(isicom_exit); - -MODULE_AUTHOR("MultiTech"); -MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("isi608.bin"); -MODULE_FIRMWARE("isi608em.bin"); -MODULE_FIRMWARE("isi616em.bin"); -MODULE_FIRMWARE("isi4608.bin"); -MODULE_FIRMWARE("isi4616.bin"); diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c deleted file mode 100644 index 35b0c38590e6..000000000000 --- a/drivers/char/moxa.c +++ /dev/null @@ -1,2092 +0,0 @@ -/*****************************************************************************/ -/* - * moxa.c -- MOXA Intellio family multiport serial driver. - * - * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com). - * Copyright (c) 2007 Jiri Slaby - * - * This code is loosely based on the Linux serial driver, written by - * Linus Torvalds, Theodore T'so and others. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -/* - * MOXA Intellio Series Driver - * for : LINUX - * date : 1999/1/7 - * version : 5.1 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "moxa.h" - -#define MOXA_VERSION "6.0k" - -#define MOXA_FW_HDRLEN 32 - -#define MOXAMAJOR 172 - -#define MAX_BOARDS 4 /* Don't change this value */ -#define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ -#define MAX_PORTS (MAX_BOARDS * MAX_PORTS_PER_BOARD) - -#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \ - (brd)->boardType == MOXA_BOARD_C320_PCI) - -/* - * Define the Moxa PCI vendor and device IDs. - */ -#define MOXA_BUS_TYPE_ISA 0 -#define MOXA_BUS_TYPE_PCI 1 - -enum { - MOXA_BOARD_C218_PCI = 1, - MOXA_BOARD_C218_ISA, - MOXA_BOARD_C320_PCI, - MOXA_BOARD_C320_ISA, - MOXA_BOARD_CP204J, -}; - -static char *moxa_brdname[] = -{ - "C218 Turbo PCI series", - "C218 Turbo ISA series", - "C320 Turbo PCI series", - "C320 Turbo ISA series", - "CP-204J series", -}; - -#ifdef CONFIG_PCI -static struct pci_device_id moxa_pcibrds[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218), - .driver_data = MOXA_BOARD_C218_PCI }, - { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320), - .driver_data = MOXA_BOARD_C320_PCI }, - { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J), - .driver_data = MOXA_BOARD_CP204J }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, moxa_pcibrds); -#endif /* CONFIG_PCI */ - -struct moxa_port; - -static struct moxa_board_conf { - int boardType; - int numPorts; - int busType; - - unsigned int ready; - - struct moxa_port *ports; - - void __iomem *basemem; - void __iomem *intNdx; - void __iomem *intPend; - void __iomem *intTable; -} moxa_boards[MAX_BOARDS]; - -struct mxser_mstatus { - tcflag_t cflag; - int cts; - int dsr; - int ri; - int dcd; -}; - -struct moxaq_str { - int inq; - int outq; -}; - -struct moxa_port { - struct tty_port port; - struct moxa_board_conf *board; - void __iomem *tableAddr; - - int type; - int cflag; - unsigned long statusflags; - - u8 DCDState; /* Protected by the port lock */ - u8 lineCtrl; - u8 lowChkFlag; -}; - -struct mon_str { - int tick; - int rxcnt[MAX_PORTS]; - int txcnt[MAX_PORTS]; -}; - -/* statusflags */ -#define TXSTOPPED 1 -#define LOWWAIT 2 -#define EMPTYWAIT 3 - -#define SERIAL_DO_RESTART - -#define WAKEUP_CHARS 256 - -static int ttymajor = MOXAMAJOR; -static struct mon_str moxaLog; -static unsigned int moxaFuncTout = HZ / 2; -static unsigned int moxaLowWaterChk; -static DEFINE_MUTEX(moxa_openlock); -static DEFINE_SPINLOCK(moxa_lock); - -static unsigned long baseaddr[MAX_BOARDS]; -static unsigned int type[MAX_BOARDS]; -static unsigned int numports[MAX_BOARDS]; - -MODULE_AUTHOR("William Chen"); -MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("c218tunx.cod"); -MODULE_FIRMWARE("cp204unx.cod"); -MODULE_FIRMWARE("c320tunx.cod"); - -module_param_array(type, uint, NULL, 0); -MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); -module_param_array(baseaddr, ulong, NULL, 0); -MODULE_PARM_DESC(baseaddr, "base address"); -module_param_array(numports, uint, NULL, 0); -MODULE_PARM_DESC(numports, "numports (ignored for C218)"); - -module_param(ttymajor, int, 0); - -/* - * static functions: - */ -static int moxa_open(struct tty_struct *, struct file *); -static void moxa_close(struct tty_struct *, struct file *); -static int moxa_write(struct tty_struct *, const unsigned char *, int); -static int moxa_write_room(struct tty_struct *); -static void moxa_flush_buffer(struct tty_struct *); -static int moxa_chars_in_buffer(struct tty_struct *); -static void moxa_set_termios(struct tty_struct *, struct ktermios *); -static void moxa_stop(struct tty_struct *); -static void moxa_start(struct tty_struct *); -static void moxa_hangup(struct tty_struct *); -static int moxa_tiocmget(struct tty_struct *tty); -static int moxa_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static void moxa_poll(unsigned long); -static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); -static void moxa_shutdown(struct tty_port *); -static int moxa_carrier_raised(struct tty_port *); -static void moxa_dtr_rts(struct tty_port *, int); -/* - * moxa board interface functions: - */ -static void MoxaPortEnable(struct moxa_port *); -static void MoxaPortDisable(struct moxa_port *); -static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t); -static int MoxaPortGetLineOut(struct moxa_port *, int *, int *); -static void MoxaPortLineCtrl(struct moxa_port *, int, int); -static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); -static int MoxaPortLineStatus(struct moxa_port *); -static void MoxaPortFlushData(struct moxa_port *, int); -static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int); -static int MoxaPortReadData(struct moxa_port *); -static int MoxaPortTxQueue(struct moxa_port *); -static int MoxaPortRxQueue(struct moxa_port *); -static int MoxaPortTxFree(struct moxa_port *); -static void MoxaPortTxDisable(struct moxa_port *); -static void MoxaPortTxEnable(struct moxa_port *); -static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *); -static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *); -static void MoxaSetFifo(struct moxa_port *port, int enable); - -/* - * I/O functions - */ - -static DEFINE_SPINLOCK(moxafunc_lock); - -static void moxa_wait_finish(void __iomem *ofsAddr) -{ - unsigned long end = jiffies + moxaFuncTout; - - while (readw(ofsAddr + FuncCode) != 0) - if (time_after(jiffies, end)) - return; - if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit()) - printk(KERN_WARNING "moxa function expired\n"); -} - -static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg) -{ - unsigned long flags; - spin_lock_irqsave(&moxafunc_lock, flags); - writew(arg, ofsAddr + FuncArg); - writew(cmd, ofsAddr + FuncCode); - moxa_wait_finish(ofsAddr); - spin_unlock_irqrestore(&moxafunc_lock, flags); -} - -static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg) -{ - unsigned long flags; - u16 ret; - spin_lock_irqsave(&moxafunc_lock, flags); - writew(arg, ofsAddr + FuncArg); - writew(cmd, ofsAddr + FuncCode); - moxa_wait_finish(ofsAddr); - ret = readw(ofsAddr + FuncArg); - spin_unlock_irqrestore(&moxafunc_lock, flags); - return ret; -} - -static void moxa_low_water_check(void __iomem *ofsAddr) -{ - u16 rptr, wptr, mask, len; - - if (readb(ofsAddr + FlagStat) & Xoff_state) { - rptr = readw(ofsAddr + RXrptr); - wptr = readw(ofsAddr + RXwptr); - mask = readw(ofsAddr + RX_mask); - len = (wptr - rptr) & mask; - if (len <= Low_water) - moxafunc(ofsAddr, FC_SendXon, 0); - } -} - -/* - * TTY operations - */ - -static int moxa_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct moxa_port *ch = tty->driver_data; - void __user *argp = (void __user *)arg; - int status, ret = 0; - - if (tty->index == MAX_PORTS) { - if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE && - cmd != MOXA_GETMSTATUS) - return -EINVAL; - } else if (!ch) - return -ENODEV; - - switch (cmd) { - case MOXA_GETDATACOUNT: - moxaLog.tick = jiffies; - if (copy_to_user(argp, &moxaLog, sizeof(moxaLog))) - ret = -EFAULT; - break; - case MOXA_FLUSH_QUEUE: - MoxaPortFlushData(ch, arg); - break; - case MOXA_GET_IOQUEUE: { - struct moxaq_str __user *argm = argp; - struct moxaq_str tmp; - struct moxa_port *p; - unsigned int i, j; - - for (i = 0; i < MAX_BOARDS; i++) { - p = moxa_boards[i].ports; - for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { - memset(&tmp, 0, sizeof(tmp)); - spin_lock_bh(&moxa_lock); - if (moxa_boards[i].ready) { - tmp.inq = MoxaPortRxQueue(p); - tmp.outq = MoxaPortTxQueue(p); - } - spin_unlock_bh(&moxa_lock); - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - } - break; - } case MOXA_GET_OQUEUE: - status = MoxaPortTxQueue(ch); - ret = put_user(status, (unsigned long __user *)argp); - break; - case MOXA_GET_IQUEUE: - status = MoxaPortRxQueue(ch); - ret = put_user(status, (unsigned long __user *)argp); - break; - case MOXA_GETMSTATUS: { - struct mxser_mstatus __user *argm = argp; - struct mxser_mstatus tmp; - struct moxa_port *p; - unsigned int i, j; - - for (i = 0; i < MAX_BOARDS; i++) { - p = moxa_boards[i].ports; - for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { - struct tty_struct *ttyp; - memset(&tmp, 0, sizeof(tmp)); - spin_lock_bh(&moxa_lock); - if (!moxa_boards[i].ready) { - spin_unlock_bh(&moxa_lock); - goto copy; - } - - status = MoxaPortLineStatus(p); - spin_unlock_bh(&moxa_lock); - - if (status & 1) - tmp.cts = 1; - if (status & 2) - tmp.dsr = 1; - if (status & 4) - tmp.dcd = 1; - - ttyp = tty_port_tty_get(&p->port); - if (!ttyp || !ttyp->termios) - tmp.cflag = p->cflag; - else - tmp.cflag = ttyp->termios->c_cflag; - tty_kref_put(tty); -copy: - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - } - break; - } - case TIOCGSERIAL: - mutex_lock(&ch->port.mutex); - ret = moxa_get_serial_info(ch, argp); - mutex_unlock(&ch->port.mutex); - break; - case TIOCSSERIAL: - mutex_lock(&ch->port.mutex); - ret = moxa_set_serial_info(ch, argp); - mutex_unlock(&ch->port.mutex); - break; - default: - ret = -ENOIOCTLCMD; - } - return ret; -} - -static int moxa_break_ctl(struct tty_struct *tty, int state) -{ - struct moxa_port *port = tty->driver_data; - - moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, - Magic_code); - return 0; -} - -static const struct tty_operations moxa_ops = { - .open = moxa_open, - .close = moxa_close, - .write = moxa_write, - .write_room = moxa_write_room, - .flush_buffer = moxa_flush_buffer, - .chars_in_buffer = moxa_chars_in_buffer, - .ioctl = moxa_ioctl, - .set_termios = moxa_set_termios, - .stop = moxa_stop, - .start = moxa_start, - .hangup = moxa_hangup, - .break_ctl = moxa_break_ctl, - .tiocmget = moxa_tiocmget, - .tiocmset = moxa_tiocmset, -}; - -static const struct tty_port_operations moxa_port_ops = { - .carrier_raised = moxa_carrier_raised, - .dtr_rts = moxa_dtr_rts, - .shutdown = moxa_shutdown, -}; - -static struct tty_driver *moxaDriver; -static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0); - -/* - * HW init - */ - -static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model) -{ - switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - if (model != 1) - goto err; - break; - case MOXA_BOARD_CP204J: - if (model != 3) - goto err; - break; - default: - if (model != 2) - goto err; - break; - } - return 0; -err: - return -EINVAL; -} - -static int moxa_check_fw(const void *ptr) -{ - const __le16 *lptr = ptr; - - if (*lptr != cpu_to_le16(0x7980)) - return -EINVAL; - - return 0; -} - -static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf, - size_t len) -{ - void __iomem *baseAddr = brd->basemem; - u16 tmp; - - writeb(HW_reset, baseAddr + Control_reg); /* reset */ - msleep(10); - memset_io(baseAddr, 0, 4096); - memcpy_toio(baseAddr, buf, len); /* download BIOS */ - writeb(0, baseAddr + Control_reg); /* restart */ - - msleep(2000); - - switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - tmp = readw(baseAddr + C218_key); - if (tmp != C218_KeyCode) - goto err; - break; - case MOXA_BOARD_CP204J: - tmp = readw(baseAddr + C218_key); - if (tmp != CP204J_KeyCode) - goto err; - break; - default: - tmp = readw(baseAddr + C320_key); - if (tmp != C320_KeyCode) - goto err; - tmp = readw(baseAddr + C320_status); - if (tmp != STS_init) { - printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic " - "module not found\n"); - return -EIO; - } - break; - } - - return 0; -err: - printk(KERN_ERR "MOXA: bios upload failed -- board not found\n"); - return -EIO; -} - -static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr, - size_t len) -{ - void __iomem *baseAddr = brd->basemem; - - if (len < 7168) { - printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n"); - return -EINVAL; - } - - writew(len - 7168 - 2, baseAddr + C320bapi_len); - writeb(1, baseAddr + Control_reg); /* Select Page 1 */ - memcpy_toio(baseAddr + DynPage_addr, ptr, 7168); - writeb(2, baseAddr + Control_reg); /* Select Page 2 */ - memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168); - - return 0; -} - -static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr, - size_t len) -{ - void __iomem *baseAddr = brd->basemem; - const __le16 *uptr = ptr; - size_t wlen, len2, j; - unsigned long key, loadbuf, loadlen, checksum, checksum_ok; - unsigned int i, retry; - u16 usum, keycode; - - keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode : - C218_KeyCode; - - switch (brd->boardType) { - case MOXA_BOARD_CP204J: - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - key = C218_key; - loadbuf = C218_LoadBuf; - loadlen = C218DLoad_len; - checksum = C218check_sum; - checksum_ok = C218chksum_ok; - break; - default: - key = C320_key; - keycode = C320_KeyCode; - loadbuf = C320_LoadBuf; - loadlen = C320DLoad_len; - checksum = C320check_sum; - checksum_ok = C320chksum_ok; - break; - } - - usum = 0; - wlen = len >> 1; - for (i = 0; i < wlen; i++) - usum += le16_to_cpu(uptr[i]); - retry = 0; - do { - wlen = len >> 1; - j = 0; - while (wlen) { - len2 = (wlen > 2048) ? 2048 : wlen; - wlen -= len2; - memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1); - j += len2 << 1; - - writew(len2, baseAddr + loadlen); - writew(0, baseAddr + key); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + key) == keycode) - break; - msleep(10); - } - if (readw(baseAddr + key) != keycode) - return -EIO; - } - writew(0, baseAddr + loadlen); - writew(usum, baseAddr + checksum); - writew(0, baseAddr + key); - for (i = 0; i < 100; i++) { - if (readw(baseAddr + key) == keycode) - break; - msleep(10); - } - retry++; - } while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3)); - if (readb(baseAddr + checksum_ok) != 1) - return -EIO; - - writew(0, baseAddr + key); - for (i = 0; i < 600; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return -EIO; - - if (MOXA_IS_320(brd)) { - if (brd->busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ - writew(0x3800, baseAddr + TMS320_PORT1); - writew(0x3900, baseAddr + TMS320_PORT2); - writew(28499, baseAddr + TMS320_CLOCK); - } else { - writew(0x3200, baseAddr + TMS320_PORT1); - writew(0x3400, baseAddr + TMS320_PORT2); - writew(19999, baseAddr + TMS320_CLOCK); - } - } - writew(1, baseAddr + Disable_IRQ); - writew(0, baseAddr + Magic_no); - for (i = 0; i < 500; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return -EIO; - - if (MOXA_IS_320(brd)) { - j = readw(baseAddr + Module_cnt); - if (j <= 0) - return -EIO; - brd->numPorts = j * 8; - writew(j, baseAddr + Module_no); - writew(0, baseAddr + Magic_no); - for (i = 0; i < 600; i++) { - if (readw(baseAddr + Magic_no) == Magic_code) - break; - msleep(10); - } - if (readw(baseAddr + Magic_no) != Magic_code) - return -EIO; - } - brd->intNdx = baseAddr + IRQindex; - brd->intPend = baseAddr + IRQpending; - brd->intTable = baseAddr + IRQtable; - - return 0; -} - -static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr, - size_t len) -{ - void __iomem *ofsAddr, *baseAddr = brd->basemem; - struct moxa_port *port; - int retval, i; - - if (len % 2) { - printk(KERN_ERR "MOXA: bios length is not even\n"); - return -EINVAL; - } - - retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */ - if (retval) - return retval; - - switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - case MOXA_BOARD_CP204J: - port = brd->ports; - for (i = 0; i < brd->numPorts; i++, port++) { - port->board = brd; - port->DCDState = 0; - port->tableAddr = baseAddr + Extern_table + - Extern_size * i; - ofsAddr = port->tableAddr; - writew(C218rx_mask, ofsAddr + RX_mask); - writew(C218tx_mask, ofsAddr + TX_mask); - writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); - - writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); - - } - break; - default: - port = brd->ports; - for (i = 0; i < brd->numPorts; i++, port++) { - port->board = brd; - port->DCDState = 0; - port->tableAddr = baseAddr + Extern_table + - Extern_size * i; - ofsAddr = port->tableAddr; - switch (brd->numPorts) { - case 8: - writew(C320p8rx_mask, ofsAddr + RX_mask); - writew(C320p8tx_mask, ofsAddr + TX_mask); - writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); - - break; - case 16: - writew(C320p16rx_mask, ofsAddr + RX_mask); - writew(C320p16tx_mask, ofsAddr + TX_mask); - writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); - break; - - case 24: - writew(C320p24rx_mask, ofsAddr + RX_mask); - writew(C320p24tx_mask, ofsAddr + TX_mask); - writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); - writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); - writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); - break; - case 32: - writew(C320p32rx_mask, ofsAddr + RX_mask); - writew(C320p32tx_mask, ofsAddr + TX_mask); - writew(C320p32tx_ofs, ofsAddr + Ofs_txb); - writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); - writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); - writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); - writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); - break; - } - } - break; - } - return 0; -} - -static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw) -{ - const void *ptr = fw->data; - char rsn[64]; - u16 lens[5]; - size_t len; - unsigned int a, lenp, lencnt; - int ret = -EINVAL; - struct { - __le32 magic; /* 0x34303430 */ - u8 reserved1[2]; - u8 type; /* UNIX = 3 */ - u8 model; /* C218T=1, C320T=2, CP204=3 */ - u8 reserved2[8]; - __le16 len[5]; - } const *hdr = ptr; - - BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens)); - - if (fw->size < MOXA_FW_HDRLEN) { - strcpy(rsn, "too short (even header won't fit)"); - goto err; - } - if (hdr->magic != cpu_to_le32(0x30343034)) { - sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic)); - goto err; - } - if (hdr->type != 3) { - sprintf(rsn, "not for linux, type is %u", hdr->type); - goto err; - } - if (moxa_check_fw_model(brd, hdr->model)) { - sprintf(rsn, "not for this card, model is %u", hdr->model); - goto err; - } - - len = MOXA_FW_HDRLEN; - lencnt = hdr->model == 2 ? 5 : 3; - for (a = 0; a < ARRAY_SIZE(lens); a++) { - lens[a] = le16_to_cpu(hdr->len[a]); - if (lens[a] && len + lens[a] <= fw->size && - moxa_check_fw(&fw->data[len])) - printk(KERN_WARNING "MOXA firmware: unexpected input " - "at offset %u, but going on\n", (u32)len); - if (!lens[a] && a < lencnt) { - sprintf(rsn, "too few entries in fw file"); - goto err; - } - len += lens[a]; - } - - if (len != fw->size) { - sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size, - (u32)len); - goto err; - } - - ptr += MOXA_FW_HDRLEN; - lenp = 0; /* bios */ - - strcpy(rsn, "read above"); - - ret = moxa_load_bios(brd, ptr, lens[lenp]); - if (ret) - goto err; - - /* we skip the tty section (lens[1]), since we don't need it */ - ptr += lens[lenp] + lens[lenp + 1]; - lenp += 2; /* comm */ - - if (hdr->model == 2) { - ret = moxa_load_320b(brd, ptr, lens[lenp]); - if (ret) - goto err; - /* skip another tty */ - ptr += lens[lenp] + lens[lenp + 1]; - lenp += 2; - } - - ret = moxa_load_code(brd, ptr, lens[lenp]); - if (ret) - goto err; - - return 0; -err: - printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn); - return ret; -} - -static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) -{ - const struct firmware *fw; - const char *file; - struct moxa_port *p; - unsigned int i; - int ret; - - brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports), - GFP_KERNEL); - if (brd->ports == NULL) { - printk(KERN_ERR "cannot allocate memory for ports\n"); - ret = -ENOMEM; - goto err; - } - - for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { - tty_port_init(&p->port); - p->port.ops = &moxa_port_ops; - p->type = PORT_16550A; - p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - } - - switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - file = "c218tunx.cod"; - break; - case MOXA_BOARD_CP204J: - file = "cp204unx.cod"; - break; - default: - file = "c320tunx.cod"; - break; - } - - ret = request_firmware(&fw, file, dev); - if (ret) { - printk(KERN_ERR "MOXA: request_firmware failed. Make sure " - "you've placed '%s' file into your firmware " - "loader directory (e.g. /lib/firmware)\n", - file); - goto err_free; - } - - ret = moxa_load_fw(brd, fw); - - release_firmware(fw); - - if (ret) - goto err_free; - - spin_lock_bh(&moxa_lock); - brd->ready = 1; - if (!timer_pending(&moxaTimer)) - mod_timer(&moxaTimer, jiffies + HZ / 50); - spin_unlock_bh(&moxa_lock); - - return 0; -err_free: - kfree(brd->ports); -err: - return ret; -} - -static void moxa_board_deinit(struct moxa_board_conf *brd) -{ - unsigned int a, opened; - - mutex_lock(&moxa_openlock); - spin_lock_bh(&moxa_lock); - brd->ready = 0; - spin_unlock_bh(&moxa_lock); - - /* pci hot-un-plug support */ - for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { - struct tty_struct *tty = tty_port_tty_get( - &brd->ports[a].port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } - while (1) { - opened = 0; - for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) - opened++; - mutex_unlock(&moxa_openlock); - if (!opened) - break; - msleep(50); - mutex_lock(&moxa_openlock); - } - - iounmap(brd->basemem); - brd->basemem = NULL; - kfree(brd->ports); -} - -#ifdef CONFIG_PCI -static int __devinit moxa_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct moxa_board_conf *board; - unsigned int i; - int board_type = ent->driver_data; - int retval; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "can't enable pci device\n"); - goto err; - } - - for (i = 0; i < MAX_BOARDS; i++) - if (moxa_boards[i].basemem == NULL) - break; - - retval = -ENODEV; - if (i >= MAX_BOARDS) { - dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards " - "found. Board is ignored.\n", MAX_BOARDS); - goto err; - } - - board = &moxa_boards[i]; - - retval = pci_request_region(pdev, 2, "moxa-base"); - if (retval) { - dev_err(&pdev->dev, "can't request pci region 2\n"); - goto err; - } - - board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000); - if (board->basemem == NULL) { - dev_err(&pdev->dev, "can't remap io space 2\n"); - goto err_reg; - } - - board->boardType = board_type; - switch (board_type) { - case MOXA_BOARD_C218_ISA: - case MOXA_BOARD_C218_PCI: - board->numPorts = 8; - break; - - case MOXA_BOARD_CP204J: - board->numPorts = 4; - break; - default: - board->numPorts = 0; - break; - } - board->busType = MOXA_BUS_TYPE_PCI; - - retval = moxa_init_board(board, &pdev->dev); - if (retval) - goto err_base; - - pci_set_drvdata(pdev, board); - - dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n", - moxa_brdname[board_type - 1], board->numPorts); - - return 0; -err_base: - iounmap(board->basemem); - board->basemem = NULL; -err_reg: - pci_release_region(pdev, 2); -err: - return retval; -} - -static void __devexit moxa_pci_remove(struct pci_dev *pdev) -{ - struct moxa_board_conf *brd = pci_get_drvdata(pdev); - - moxa_board_deinit(brd); - - pci_release_region(pdev, 2); -} - -static struct pci_driver moxa_pci_driver = { - .name = "moxa", - .id_table = moxa_pcibrds, - .probe = moxa_pci_probe, - .remove = __devexit_p(moxa_pci_remove) -}; -#endif /* CONFIG_PCI */ - -static int __init moxa_init(void) -{ - unsigned int isabrds = 0; - int retval = 0; - struct moxa_board_conf *brd = moxa_boards; - unsigned int i; - - printk(KERN_INFO "MOXA Intellio family driver version %s\n", - MOXA_VERSION); - moxaDriver = alloc_tty_driver(MAX_PORTS + 1); - if (!moxaDriver) - return -ENOMEM; - - moxaDriver->owner = THIS_MODULE; - moxaDriver->name = "ttyMX"; - moxaDriver->major = ttymajor; - moxaDriver->minor_start = 0; - moxaDriver->type = TTY_DRIVER_TYPE_SERIAL; - moxaDriver->subtype = SERIAL_TYPE_NORMAL; - moxaDriver->init_termios = tty_std_termios; - moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - moxaDriver->init_termios.c_ispeed = 9600; - moxaDriver->init_termios.c_ospeed = 9600; - moxaDriver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(moxaDriver, &moxa_ops); - - if (tty_register_driver(moxaDriver)) { - printk(KERN_ERR "can't register MOXA Smartio tty driver!\n"); - put_tty_driver(moxaDriver); - return -1; - } - - /* Find the boards defined from module args. */ - - for (i = 0; i < MAX_BOARDS; i++) { - if (!baseaddr[i]) - break; - if (type[i] == MOXA_BOARD_C218_ISA || - type[i] == MOXA_BOARD_C320_ISA) { - pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n", - isabrds + 1, moxa_brdname[type[i] - 1], - baseaddr[i]); - brd->boardType = type[i]; - brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 : - numports[i]; - brd->busType = MOXA_BUS_TYPE_ISA; - brd->basemem = ioremap_nocache(baseaddr[i], 0x4000); - if (!brd->basemem) { - printk(KERN_ERR "MOXA: can't remap %lx\n", - baseaddr[i]); - continue; - } - if (moxa_init_board(brd, NULL)) { - iounmap(brd->basemem); - brd->basemem = NULL; - continue; - } - - printk(KERN_INFO "MOXA isa board found at 0x%.8lu and " - "ready (%u ports, firmware loaded)\n", - baseaddr[i], brd->numPorts); - - brd++; - isabrds++; - } - } - -#ifdef CONFIG_PCI - retval = pci_register_driver(&moxa_pci_driver); - if (retval) { - printk(KERN_ERR "Can't register MOXA pci driver!\n"); - if (isabrds) - retval = 0; - } -#endif - - return retval; -} - -static void __exit moxa_exit(void) -{ - unsigned int i; - -#ifdef CONFIG_PCI - pci_unregister_driver(&moxa_pci_driver); -#endif - - for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */ - if (moxa_boards[i].ready) - moxa_board_deinit(&moxa_boards[i]); - - del_timer_sync(&moxaTimer); - - if (tty_unregister_driver(moxaDriver)) - printk(KERN_ERR "Couldn't unregister MOXA Intellio family " - "serial driver\n"); - put_tty_driver(moxaDriver); -} - -module_init(moxa_init); -module_exit(moxa_exit); - -static void moxa_shutdown(struct tty_port *port) -{ - struct moxa_port *ch = container_of(port, struct moxa_port, port); - MoxaPortDisable(ch); - MoxaPortFlushData(ch, 2); - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); -} - -static int moxa_carrier_raised(struct tty_port *port) -{ - struct moxa_port *ch = container_of(port, struct moxa_port, port); - int dcd; - - spin_lock_irq(&port->lock); - dcd = ch->DCDState; - spin_unlock_irq(&port->lock); - return dcd; -} - -static void moxa_dtr_rts(struct tty_port *port, int onoff) -{ - struct moxa_port *ch = container_of(port, struct moxa_port, port); - MoxaPortLineCtrl(ch, onoff, onoff); -} - - -static int moxa_open(struct tty_struct *tty, struct file *filp) -{ - struct moxa_board_conf *brd; - struct moxa_port *ch; - int port; - int retval; - - port = tty->index; - if (port == MAX_PORTS) { - return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; - } - if (mutex_lock_interruptible(&moxa_openlock)) - return -ERESTARTSYS; - brd = &moxa_boards[port / MAX_PORTS_PER_BOARD]; - if (!brd->ready) { - mutex_unlock(&moxa_openlock); - return -ENODEV; - } - - if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) { - mutex_unlock(&moxa_openlock); - return -ENODEV; - } - - ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; - ch->port.count++; - tty->driver_data = ch; - tty_port_tty_set(&ch->port, tty); - mutex_lock(&ch->port.mutex); - if (!(ch->port.flags & ASYNC_INITIALIZED)) { - ch->statusflags = 0; - moxa_set_tty_param(tty, tty->termios); - MoxaPortLineCtrl(ch, 1, 1); - MoxaPortEnable(ch); - MoxaSetFifo(ch, ch->type == PORT_16550A); - ch->port.flags |= ASYNC_INITIALIZED; - } - mutex_unlock(&ch->port.mutex); - mutex_unlock(&moxa_openlock); - - retval = tty_port_block_til_ready(&ch->port, tty, filp); - if (retval == 0) - set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags); - return retval; -} - -static void moxa_close(struct tty_struct *tty, struct file *filp) -{ - struct moxa_port *ch = tty->driver_data; - ch->cflag = tty->termios->c_cflag; - tty_port_close(&ch->port, tty, filp); -} - -static int moxa_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct moxa_port *ch = tty->driver_data; - int len; - - if (ch == NULL) - return 0; - - spin_lock_bh(&moxa_lock); - len = MoxaPortWriteData(tty, buf, count); - spin_unlock_bh(&moxa_lock); - - set_bit(LOWWAIT, &ch->statusflags); - return len; -} - -static int moxa_write_room(struct tty_struct *tty) -{ - struct moxa_port *ch; - - if (tty->stopped) - return 0; - ch = tty->driver_data; - if (ch == NULL) - return 0; - return MoxaPortTxFree(ch); -} - -static void moxa_flush_buffer(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - - if (ch == NULL) - return; - MoxaPortFlushData(ch, 1); - tty_wakeup(tty); -} - -static int moxa_chars_in_buffer(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - int chars; - - chars = MoxaPortTxQueue(ch); - if (chars) - /* - * Make it possible to wakeup anything waiting for output - * in tty_ioctl.c, etc. - */ - set_bit(EMPTYWAIT, &ch->statusflags); - return chars; -} - -static int moxa_tiocmget(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - int flag = 0, dtr, rts; - - MoxaPortGetLineOut(ch, &dtr, &rts); - if (dtr) - flag |= TIOCM_DTR; - if (rts) - flag |= TIOCM_RTS; - dtr = MoxaPortLineStatus(ch); - if (dtr & 1) - flag |= TIOCM_CTS; - if (dtr & 2) - flag |= TIOCM_DSR; - if (dtr & 4) - flag |= TIOCM_CD; - return flag; -} - -static int moxa_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct moxa_port *ch; - int port; - int dtr, rts; - - port = tty->index; - mutex_lock(&moxa_openlock); - ch = tty->driver_data; - if (!ch) { - mutex_unlock(&moxa_openlock); - return -EINVAL; - } - - MoxaPortGetLineOut(ch, &dtr, &rts); - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; - MoxaPortLineCtrl(ch, dtr, rts); - mutex_unlock(&moxa_openlock); - return 0; -} - -static void moxa_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct moxa_port *ch = tty->driver_data; - - if (ch == NULL) - return; - moxa_set_tty_param(tty, old_termios); - if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) - wake_up_interruptible(&ch->port.open_wait); -} - -static void moxa_stop(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - - if (ch == NULL) - return; - MoxaPortTxDisable(ch); - set_bit(TXSTOPPED, &ch->statusflags); -} - - -static void moxa_start(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - - if (ch == NULL) - return; - - if (!(ch->statusflags & TXSTOPPED)) - return; - - MoxaPortTxEnable(ch); - clear_bit(TXSTOPPED, &ch->statusflags); -} - -static void moxa_hangup(struct tty_struct *tty) -{ - struct moxa_port *ch = tty->driver_data; - tty_port_hangup(&ch->port); -} - -static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) -{ - struct tty_struct *tty; - unsigned long flags; - dcd = !!dcd; - - spin_lock_irqsave(&p->port.lock, flags); - if (dcd != p->DCDState) { - p->DCDState = dcd; - spin_unlock_irqrestore(&p->port.lock, flags); - tty = tty_port_tty_get(&p->port); - if (tty && C_CLOCAL(tty) && !dcd) - tty_hangup(tty); - tty_kref_put(tty); - } - else - spin_unlock_irqrestore(&p->port.lock, flags); -} - -static int moxa_poll_port(struct moxa_port *p, unsigned int handle, - u16 __iomem *ip) -{ - struct tty_struct *tty = tty_port_tty_get(&p->port); - void __iomem *ofsAddr; - unsigned int inited = p->port.flags & ASYNC_INITIALIZED; - u16 intr; - - if (tty) { - if (test_bit(EMPTYWAIT, &p->statusflags) && - MoxaPortTxQueue(p) == 0) { - clear_bit(EMPTYWAIT, &p->statusflags); - tty_wakeup(tty); - } - if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped && - MoxaPortTxQueue(p) <= WAKEUP_CHARS) { - clear_bit(LOWWAIT, &p->statusflags); - tty_wakeup(tty); - } - - if (inited && !test_bit(TTY_THROTTLED, &tty->flags) && - MoxaPortRxQueue(p) > 0) { /* RX */ - MoxaPortReadData(p); - tty_schedule_flip(tty); - } - } else { - clear_bit(EMPTYWAIT, &p->statusflags); - MoxaPortFlushData(p, 0); /* flush RX */ - } - - if (!handle) /* nothing else to do */ - goto put; - - intr = readw(ip); /* port irq status */ - if (intr == 0) - goto put; - - writew(0, ip); /* ACK port */ - ofsAddr = p->tableAddr; - if (intr & IntrTx) /* disable tx intr */ - writew(readw(ofsAddr + HostStat) & ~WakeupTx, - ofsAddr + HostStat); - - if (!inited) - goto put; - - if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); - } - - if (intr & IntrLine) - moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state); -put: - tty_kref_put(tty); - - return 0; -} - -static void moxa_poll(unsigned long ignored) -{ - struct moxa_board_conf *brd; - u16 __iomem *ip; - unsigned int card, port, served = 0; - - spin_lock(&moxa_lock); - for (card = 0; card < MAX_BOARDS; card++) { - brd = &moxa_boards[card]; - if (!brd->ready) - continue; - - served++; - - ip = NULL; - if (readb(brd->intPend) == 0xff) - ip = brd->intTable + readb(brd->intNdx); - - for (port = 0; port < brd->numPorts; port++) - moxa_poll_port(&brd->ports[port], !!ip, ip + port); - - if (ip) - writeb(0, brd->intPend); /* ACK */ - - if (moxaLowWaterChk) { - struct moxa_port *p = brd->ports; - for (port = 0; port < brd->numPorts; port++, p++) - if (p->lowChkFlag) { - p->lowChkFlag = 0; - moxa_low_water_check(p->tableAddr); - } - } - } - moxaLowWaterChk = 0; - - if (served) - mod_timer(&moxaTimer, jiffies + HZ / 50); - spin_unlock(&moxa_lock); -} - -/******************************************************************************/ - -static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios) -{ - register struct ktermios *ts = tty->termios; - struct moxa_port *ch = tty->driver_data; - int rts, cts, txflow, rxflow, xany, baud; - - rts = cts = txflow = rxflow = xany = 0; - if (ts->c_cflag & CRTSCTS) - rts = cts = 1; - if (ts->c_iflag & IXON) - txflow = 1; - if (ts->c_iflag & IXOFF) - rxflow = 1; - if (ts->c_iflag & IXANY) - xany = 1; - - /* Clear the features we don't support */ - ts->c_cflag &= ~CMSPAR; - MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany); - baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty)); - if (baud == -1) - baud = tty_termios_baud_rate(old_termios); - /* Not put the baud rate into the termios data */ - tty_encode_baud_rate(tty, baud, baud); -} - -/***************************************************************************** - * Driver level functions: * - *****************************************************************************/ - -static void MoxaPortFlushData(struct moxa_port *port, int mode) -{ - void __iomem *ofsAddr; - if (mode < 0 || mode > 2) - return; - ofsAddr = port->tableAddr; - moxafunc(ofsAddr, FC_FlushQueue, mode); - if (mode != 1) { - port->lowChkFlag = 0; - moxa_low_water_check(ofsAddr); - } -} - -/* - * Moxa Port Number Description: - * - * MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And, - * the port number using in MOXA driver functions will be 0 to 31 for - * first MOXA board, 32 to 63 for second, 64 to 95 for third and 96 - * to 127 for fourth. For example, if you setup three MOXA boards, - * first board is C218, second board is C320-16 and third board is - * C320-32. The port number of first board (C218 - 8 ports) is from - * 0 to 7. The port number of second board (C320 - 16 ports) is form - * 32 to 47. The port number of third board (C320 - 32 ports) is from - * 64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to - * 127 will be invalid. - * - * - * Moxa Functions Description: - * - * Function 1: Driver initialization routine, this routine must be - * called when initialized driver. - * Syntax: - * void MoxaDriverInit(); - * - * - * Function 2: Moxa driver private IOCTL command processing. - * Syntax: - * int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); - * - * unsigned int cmd : IOCTL command - * unsigned long arg : IOCTL argument - * int port : port number (0 - 127) - * - * return: 0 (OK) - * -EINVAL - * -ENOIOCTLCMD - * - * - * Function 6: Enable this port to start Tx/Rx data. - * Syntax: - * void MoxaPortEnable(int port); - * int port : port number (0 - 127) - * - * - * Function 7: Disable this port - * Syntax: - * void MoxaPortDisable(int port); - * int port : port number (0 - 127) - * - * - * Function 10: Setting baud rate of this port. - * Syntax: - * speed_t MoxaPortSetBaud(int port, speed_t baud); - * int port : port number (0 - 127) - * long baud : baud rate (50 - 115200) - * - * return: 0 : this port is invalid or baud < 50 - * 50 - 115200 : the real baud rate set to the port, if - * the argument baud is large than maximun - * available baud rate, the real setting - * baud rate will be the maximun baud rate. - * - * - * Function 12: Configure the port. - * Syntax: - * int MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud); - * int port : port number (0 - 127) - * struct ktermios * termio : termio structure pointer - * speed_t baud : baud rate - * - * return: -1 : this port is invalid or termio == NULL - * 0 : setting O.K. - * - * - * Function 13: Get the DTR/RTS state of this port. - * Syntax: - * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); - * int port : port number (0 - 127) - * int * dtrState : pointer to INT to receive the current DTR - * state. (if NULL, this function will not - * write to this address) - * int * rtsState : pointer to INT to receive the current RTS - * state. (if NULL, this function will not - * write to this address) - * - * return: -1 : this port is invalid - * 0 : O.K. - * - * - * Function 14: Setting the DTR/RTS output state of this port. - * Syntax: - * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); - * int port : port number (0 - 127) - * int dtrState : DTR output state (0: off, 1: on) - * int rtsState : RTS output state (0: off, 1: on) - * - * - * Function 15: Setting the flow control of this port. - * Syntax: - * void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow, - * int txFlow,int xany); - * int port : port number (0 - 127) - * int rtsFlow : H/W RTS flow control (0: no, 1: yes) - * int ctsFlow : H/W CTS flow control (0: no, 1: yes) - * int rxFlow : S/W Rx XON/XOFF flow control (0: no, 1: yes) - * int txFlow : S/W Tx XON/XOFF flow control (0: no, 1: yes) - * int xany : S/W XANY flow control (0: no, 1: yes) - * - * - * Function 16: Get ths line status of this port - * Syntax: - * int MoxaPortLineStatus(int port); - * int port : port number (0 - 127) - * - * return: Bit 0 - CTS state (0: off, 1: on) - * Bit 1 - DSR state (0: off, 1: on) - * Bit 2 - DCD state (0: off, 1: on) - * - * - * Function 19: Flush the Rx/Tx buffer data of this port. - * Syntax: - * void MoxaPortFlushData(int port, int mode); - * int port : port number (0 - 127) - * int mode - * 0 : flush the Rx buffer - * 1 : flush the Tx buffer - * 2 : flush the Rx and Tx buffer - * - * - * Function 20: Write data. - * Syntax: - * int MoxaPortWriteData(int port, unsigned char * buffer, int length); - * int port : port number (0 - 127) - * unsigned char * buffer : pointer to write data buffer. - * int length : write data length - * - * return: 0 - length : real write data length - * - * - * Function 21: Read data. - * Syntax: - * int MoxaPortReadData(int port, struct tty_struct *tty); - * int port : port number (0 - 127) - * struct tty_struct *tty : tty for data - * - * return: 0 - length : real read data length - * - * - * Function 24: Get the Tx buffer current queued data bytes - * Syntax: - * int MoxaPortTxQueue(int port); - * int port : port number (0 - 127) - * - * return: .. : Tx buffer current queued data bytes - * - * - * Function 25: Get the Tx buffer current free space - * Syntax: - * int MoxaPortTxFree(int port); - * int port : port number (0 - 127) - * - * return: .. : Tx buffer current free space - * - * - * Function 26: Get the Rx buffer current queued data bytes - * Syntax: - * int MoxaPortRxQueue(int port); - * int port : port number (0 - 127) - * - * return: .. : Rx buffer current queued data bytes - * - * - * Function 28: Disable port data transmission. - * Syntax: - * void MoxaPortTxDisable(int port); - * int port : port number (0 - 127) - * - * - * Function 29: Enable port data transmission. - * Syntax: - * void MoxaPortTxEnable(int port); - * int port : port number (0 - 127) - * - * - * Function 31: Get the received BREAK signal count and reset it. - * Syntax: - * int MoxaPortResetBrkCnt(int port); - * int port : port number (0 - 127) - * - * return: 0 - .. : BREAK signal count - * - * - */ - -static void MoxaPortEnable(struct moxa_port *port) -{ - void __iomem *ofsAddr; - u16 lowwater = 512; - - ofsAddr = port->tableAddr; - writew(lowwater, ofsAddr + Low_water); - if (MOXA_IS_320(port->board)) - moxafunc(ofsAddr, FC_SetBreakIrq, 0); - else - writew(readw(ofsAddr + HostStat) | WakeupBreak, - ofsAddr + HostStat); - - moxafunc(ofsAddr, FC_SetLineIrq, Magic_code); - moxafunc(ofsAddr, FC_FlushQueue, 2); - - moxafunc(ofsAddr, FC_EnableCH, Magic_code); - MoxaPortLineStatus(port); -} - -static void MoxaPortDisable(struct moxa_port *port) -{ - void __iomem *ofsAddr = port->tableAddr; - - moxafunc(ofsAddr, FC_SetFlowCtl, 0); /* disable flow control */ - moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code); - writew(0, ofsAddr + HostStat); - moxafunc(ofsAddr, FC_DisableCH, Magic_code); -} - -static speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud) -{ - void __iomem *ofsAddr = port->tableAddr; - unsigned int clock, val; - speed_t max; - - max = MOXA_IS_320(port->board) ? 460800 : 921600; - if (baud < 50) - return 0; - if (baud > max) - baud = max; - clock = 921600; - val = clock / baud; - moxafunc(ofsAddr, FC_SetBaud, val); - baud = clock / val; - return baud; -} - -static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, - speed_t baud) -{ - void __iomem *ofsAddr; - tcflag_t cflag; - tcflag_t mode = 0; - - ofsAddr = port->tableAddr; - cflag = termio->c_cflag; /* termio->c_cflag */ - - mode = termio->c_cflag & CSIZE; - if (mode == CS5) - mode = MX_CS5; - else if (mode == CS6) - mode = MX_CS6; - else if (mode == CS7) - mode = MX_CS7; - else if (mode == CS8) - mode = MX_CS8; - - if (termio->c_cflag & CSTOPB) { - if (mode == MX_CS5) - mode |= MX_STOP15; - else - mode |= MX_STOP2; - } else - mode |= MX_STOP1; - - if (termio->c_cflag & PARENB) { - if (termio->c_cflag & PARODD) - mode |= MX_PARODD; - else - mode |= MX_PAREVEN; - } else - mode |= MX_PARNONE; - - moxafunc(ofsAddr, FC_SetDataMode, (u16)mode); - - if (MOXA_IS_320(port->board) && baud >= 921600) - return -1; - - baud = MoxaPortSetBaud(port, baud); - - if (termio->c_iflag & (IXON | IXOFF | IXANY)) { - spin_lock_irq(&moxafunc_lock); - writeb(termio->c_cc[VSTART], ofsAddr + FuncArg); - writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1); - writeb(FC_SetXonXoff, ofsAddr + FuncCode); - moxa_wait_finish(ofsAddr); - spin_unlock_irq(&moxafunc_lock); - - } - return baud; -} - -static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState, - int *rtsState) -{ - if (dtrState) - *dtrState = !!(port->lineCtrl & DTR_ON); - if (rtsState) - *rtsState = !!(port->lineCtrl & RTS_ON); - - return 0; -} - -static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts) -{ - u8 mode = 0; - - if (dtr) - mode |= DTR_ON; - if (rts) - mode |= RTS_ON; - port->lineCtrl = mode; - moxafunc(port->tableAddr, FC_LineControl, mode); -} - -static void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts, - int txflow, int rxflow, int txany) -{ - int mode = 0; - - if (rts) - mode |= RTS_FlowCtl; - if (cts) - mode |= CTS_FlowCtl; - if (txflow) - mode |= Tx_FlowCtl; - if (rxflow) - mode |= Rx_FlowCtl; - if (txany) - mode |= IXM_IXANY; - moxafunc(port->tableAddr, FC_SetFlowCtl, mode); -} - -static int MoxaPortLineStatus(struct moxa_port *port) -{ - void __iomem *ofsAddr; - int val; - - ofsAddr = port->tableAddr; - if (MOXA_IS_320(port->board)) - val = moxafuncret(ofsAddr, FC_LineStatus, 0); - else - val = readw(ofsAddr + FlagStat) >> 4; - val &= 0x0B; - if (val & 8) - val |= 4; - moxa_new_dcdstate(port, val & 8); - val &= 7; - return val; -} - -static int MoxaPortWriteData(struct tty_struct *tty, - const unsigned char *buffer, int len) -{ - struct moxa_port *port = tty->driver_data; - void __iomem *baseAddr, *ofsAddr, *ofs; - unsigned int c, total; - u16 head, tail, tx_mask, spage, epage; - u16 pageno, pageofs, bufhead; - - ofsAddr = port->tableAddr; - baseAddr = port->board->basemem; - tx_mask = readw(ofsAddr + TX_mask); - spage = readw(ofsAddr + Page_txb); - epage = readw(ofsAddr + EndPage_txb); - tail = readw(ofsAddr + TXwptr); - head = readw(ofsAddr + TXrptr); - c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask); - if (c > len) - c = len; - moxaLog.txcnt[port->port.tty->index] += c; - total = c; - if (spage == epage) { - bufhead = readw(ofsAddr + Ofs_txb); - writew(spage, baseAddr + Control_reg); - while (c > 0) { - if (head > tail) - len = head - tail - 1; - else - len = tx_mask + 1 - tail; - len = (c > len) ? len : c; - ofs = baseAddr + DynPage_addr + bufhead + tail; - memcpy_toio(ofs, buffer, len); - buffer += len; - tail = (tail + len) & tx_mask; - c -= len; - } - } else { - pageno = spage + (tail >> 13); - pageofs = tail & Page_mask; - while (c > 0) { - len = Page_size - pageofs; - if (len > c) - len = c; - writeb(pageno, baseAddr + Control_reg); - ofs = baseAddr + DynPage_addr + pageofs; - memcpy_toio(ofs, buffer, len); - buffer += len; - if (++pageno == epage) - pageno = spage; - pageofs = 0; - c -= len; - } - tail = (tail + total) & tx_mask; - } - writew(tail, ofsAddr + TXwptr); - writeb(1, ofsAddr + CD180TXirq); /* start to send */ - return total; -} - -static int MoxaPortReadData(struct moxa_port *port) -{ - struct tty_struct *tty = port->port.tty; - unsigned char *dst; - void __iomem *baseAddr, *ofsAddr, *ofs; - unsigned int count, len, total; - u16 tail, rx_mask, spage, epage; - u16 pageno, pageofs, bufhead, head; - - ofsAddr = port->tableAddr; - baseAddr = port->board->basemem; - head = readw(ofsAddr + RXrptr); - tail = readw(ofsAddr + RXwptr); - rx_mask = readw(ofsAddr + RX_mask); - spage = readw(ofsAddr + Page_rxb); - epage = readw(ofsAddr + EndPage_rxb); - count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1); - if (count == 0) - return 0; - - total = count; - moxaLog.rxcnt[tty->index] += total; - if (spage == epage) { - bufhead = readw(ofsAddr + Ofs_rxb); - writew(spage, baseAddr + Control_reg); - while (count > 0) { - ofs = baseAddr + DynPage_addr + bufhead + head; - len = (tail >= head) ? (tail - head) : - (rx_mask + 1 - head); - len = tty_prepare_flip_string(tty, &dst, - min(len, count)); - memcpy_fromio(dst, ofs, len); - head = (head + len) & rx_mask; - count -= len; - } - } else { - pageno = spage + (head >> 13); - pageofs = head & Page_mask; - while (count > 0) { - writew(pageno, baseAddr + Control_reg); - ofs = baseAddr + DynPage_addr + pageofs; - len = tty_prepare_flip_string(tty, &dst, - min(Page_size - pageofs, count)); - memcpy_fromio(dst, ofs, len); - - count -= len; - pageofs = (pageofs + len) & Page_mask; - if (pageofs == 0 && ++pageno == epage) - pageno = spage; - } - head = (head + total) & rx_mask; - } - writew(head, ofsAddr + RXrptr); - if (readb(ofsAddr + FlagStat) & Xoff_state) { - moxaLowWaterChk = 1; - port->lowChkFlag = 1; - } - return total; -} - - -static int MoxaPortTxQueue(struct moxa_port *port) -{ - void __iomem *ofsAddr = port->tableAddr; - u16 rptr, wptr, mask; - - rptr = readw(ofsAddr + TXrptr); - wptr = readw(ofsAddr + TXwptr); - mask = readw(ofsAddr + TX_mask); - return (wptr - rptr) & mask; -} - -static int MoxaPortTxFree(struct moxa_port *port) -{ - void __iomem *ofsAddr = port->tableAddr; - u16 rptr, wptr, mask; - - rptr = readw(ofsAddr + TXrptr); - wptr = readw(ofsAddr + TXwptr); - mask = readw(ofsAddr + TX_mask); - return mask - ((wptr - rptr) & mask); -} - -static int MoxaPortRxQueue(struct moxa_port *port) -{ - void __iomem *ofsAddr = port->tableAddr; - u16 rptr, wptr, mask; - - rptr = readw(ofsAddr + RXrptr); - wptr = readw(ofsAddr + RXwptr); - mask = readw(ofsAddr + RX_mask); - return (wptr - rptr) & mask; -} - -static void MoxaPortTxDisable(struct moxa_port *port) -{ - moxafunc(port->tableAddr, FC_SetXoffState, Magic_code); -} - -static void MoxaPortTxEnable(struct moxa_port *port) -{ - moxafunc(port->tableAddr, FC_SetXonState, Magic_code); -} - -static int moxa_get_serial_info(struct moxa_port *info, - struct serial_struct __user *retinfo) -{ - struct serial_struct tmp = { - .type = info->type, - .line = info->port.tty->index, - .flags = info->port.flags, - .baud_base = 921600, - .close_delay = info->port.close_delay - }; - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} - - -static int moxa_set_serial_info(struct moxa_port *info, - struct serial_struct __user *new_info) -{ - struct serial_struct new_serial; - - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - if (new_serial.irq != 0 || new_serial.port != 0 || - new_serial.custom_divisor != 0 || - new_serial.baud_base != 921600) - return -EPERM; - - if (!capable(CAP_SYS_ADMIN)) { - if (((new_serial.flags & ~ASYNC_USR_MASK) != - (info->port.flags & ~ASYNC_USR_MASK))) - return -EPERM; - } else - info->port.close_delay = new_serial.close_delay * HZ / 100; - - new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); - new_serial.flags |= (info->port.flags & ASYNC_FLAGS); - - MoxaSetFifo(info, new_serial.type == PORT_16550A); - - info->type = new_serial.type; - return 0; -} - - - -/***************************************************************************** - * Static local functions: * - *****************************************************************************/ - -static void MoxaSetFifo(struct moxa_port *port, int enable) -{ - void __iomem *ofsAddr = port->tableAddr; - - if (!enable) { - moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0); - moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1); - } else { - moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3); - moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16); - } -} diff --git a/drivers/char/moxa.h b/drivers/char/moxa.h deleted file mode 100644 index 87d16ce57be7..000000000000 --- a/drivers/char/moxa.h +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef MOXA_H_FILE -#define MOXA_H_FILE - -#define MOXA 0x400 -#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ -#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ -#define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_GET_IOQUEUE (MOXA + 27) -#define MOXA_FLUSH_QUEUE (MOXA + 28) -#define MOXA_GETMSTATUS (MOXA + 65) - -/* - * System Configuration - */ - -#define Magic_code 0x404 - -/* - * for C218 BIOS initialization - */ -#define C218_ConfBase 0x800 -#define C218_status (C218_ConfBase + 0) /* BIOS running status */ -#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ -#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ -#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ -#define C218check_sum (C218_ConfBase + 8) /* BYTE */ -#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ -#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ -#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ -#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ -#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ - -#define C218_LoadBuf 0x0F00 -#define C218_KeyCode 0x218 -#define CP204J_KeyCode 0x204 - -/* - * for C320 BIOS initialization - */ -#define C320_ConfBase 0x800 -#define C320_LoadBuf 0x0f00 -#define STS_init 0x05 /* for C320_status */ - -#define C320_status C320_ConfBase + 0 /* BIOS running status */ -#define C320_diag C320_ConfBase + 2 /* diagnostic status */ -#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ -#define C320DLoad_len C320_ConfBase + 6 /* WORD */ -#define C320check_sum C320_ConfBase + 8 /* WORD */ -#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ -#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ -#define C320UART_no C320_ConfBase + 0x0e /* WORD */ - -#define C320_KeyCode 0x320 - -#define FixPage_addr 0x0000 /* starting addr of static page */ -#define DynPage_addr 0x2000 /* starting addr of dynamic page */ -#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ -#define Control_reg 0x1ff0 /* select page and reset control */ -#define HW_reset 0x80 - -/* - * Function Codes - */ -#define FC_CardReset 0x80 -#define FC_ChannelReset 1 /* C320 firmware not supported */ -#define FC_EnableCH 2 -#define FC_DisableCH 3 -#define FC_SetParam 4 -#define FC_SetMode 5 -#define FC_SetRate 6 -#define FC_LineControl 7 -#define FC_LineStatus 8 -#define FC_XmitControl 9 -#define FC_FlushQueue 10 -#define FC_SendBreak 11 -#define FC_StopBreak 12 -#define FC_LoopbackON 13 -#define FC_LoopbackOFF 14 -#define FC_ClrIrqTable 15 -#define FC_SendXon 16 -#define FC_SetTermIrq 17 /* C320 firmware not supported */ -#define FC_SetCntIrq 18 /* C320 firmware not supported */ -#define FC_SetBreakIrq 19 -#define FC_SetLineIrq 20 -#define FC_SetFlowCtl 21 -#define FC_GenIrq 22 -#define FC_InCD180 23 -#define FC_OutCD180 24 -#define FC_InUARTreg 23 -#define FC_OutUARTreg 24 -#define FC_SetXonXoff 25 -#define FC_OutCD180CCR 26 -#define FC_ExtIQueue 27 -#define FC_ExtOQueue 28 -#define FC_ClrLineIrq 29 -#define FC_HWFlowCtl 30 -#define FC_GetClockRate 35 -#define FC_SetBaud 36 -#define FC_SetDataMode 41 -#define FC_GetCCSR 43 -#define FC_GetDataError 45 -#define FC_RxControl 50 -#define FC_ImmSend 51 -#define FC_SetXonState 52 -#define FC_SetXoffState 53 -#define FC_SetRxFIFOTrig 54 -#define FC_SetTxFIFOCnt 55 -#define FC_UnixRate 56 -#define FC_UnixResetTimer 57 - -#define RxFIFOTrig1 0 -#define RxFIFOTrig4 1 -#define RxFIFOTrig8 2 -#define RxFIFOTrig14 3 - -/* - * Dual-Ported RAM - */ -#define DRAM_global 0 -#define INT_data (DRAM_global + 0) -#define Config_base (DRAM_global + 0x108) - -#define IRQindex (INT_data + 0) -#define IRQpending (INT_data + 4) -#define IRQtable (INT_data + 8) - -/* - * Interrupt Status - */ -#define IntrRx 0x01 /* receiver data O.K. */ -#define IntrTx 0x02 /* transmit buffer empty */ -#define IntrFunc 0x04 /* function complete */ -#define IntrBreak 0x08 /* received break */ -#define IntrLine 0x10 /* line status change - for transmitter */ -#define IntrIntr 0x20 /* received INTR code */ -#define IntrQuit 0x40 /* received QUIT code */ -#define IntrEOF 0x80 /* received EOF code */ - -#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ -#define IntrTxTrigger 0x200 /* tx data count below trigger value */ - -#define Magic_no (Config_base + 0) -#define Card_model_no (Config_base + 2) -#define Total_ports (Config_base + 4) -#define Module_cnt (Config_base + 8) -#define Module_no (Config_base + 10) -#define Timer_10ms (Config_base + 14) -#define Disable_IRQ (Config_base + 20) -#define TMS320_PORT1 (Config_base + 22) -#define TMS320_PORT2 (Config_base + 24) -#define TMS320_CLOCK (Config_base + 26) - -/* - * DATA BUFFER in DRAM - */ -#define Extern_table 0x400 /* Base address of the external table - (24 words * 64) total 3K bytes - (24 words * 128) total 6K bytes */ -#define Extern_size 0x60 /* 96 bytes */ -#define RXrptr 0x00 /* read pointer for RX buffer */ -#define RXwptr 0x02 /* write pointer for RX buffer */ -#define TXrptr 0x04 /* read pointer for TX buffer */ -#define TXwptr 0x06 /* write pointer for TX buffer */ -#define HostStat 0x08 /* IRQ flag and general flag */ -#define FlagStat 0x0A -#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ - /* x x x x | | | | */ - /* | | | + CTS flow */ - /* | | +--- RTS flow */ - /* | +------ TX Xon/Xoff */ - /* +--------- RX Xon/Xoff */ -#define Break_cnt 0x0E /* received break count */ -#define CD180TXirq 0x10 /* if non-0: enable TX irq */ -#define RX_mask 0x12 -#define TX_mask 0x14 -#define Ofs_rxb 0x16 -#define Ofs_txb 0x18 -#define Page_rxb 0x1A -#define Page_txb 0x1C -#define EndPage_rxb 0x1E -#define EndPage_txb 0x20 -#define Data_error 0x22 -#define RxTrigger 0x28 -#define TxTrigger 0x2a - -#define rRXwptr 0x34 -#define Low_water 0x36 - -#define FuncCode 0x40 -#define FuncArg 0x42 -#define FuncArg1 0x44 - -#define C218rx_size 0x2000 /* 8K bytes */ -#define C218tx_size 0x8000 /* 32K bytes */ - -#define C218rx_mask (C218rx_size - 1) -#define C218tx_mask (C218tx_size - 1) - -#define C320p8rx_size 0x2000 -#define C320p8tx_size 0x8000 -#define C320p8rx_mask (C320p8rx_size - 1) -#define C320p8tx_mask (C320p8tx_size - 1) - -#define C320p16rx_size 0x2000 -#define C320p16tx_size 0x4000 -#define C320p16rx_mask (C320p16rx_size - 1) -#define C320p16tx_mask (C320p16tx_size - 1) - -#define C320p24rx_size 0x2000 -#define C320p24tx_size 0x2000 -#define C320p24rx_mask (C320p24rx_size - 1) -#define C320p24tx_mask (C320p24tx_size - 1) - -#define C320p32rx_size 0x1000 -#define C320p32tx_size 0x1000 -#define C320p32rx_mask (C320p32rx_size - 1) -#define C320p32tx_mask (C320p32tx_size - 1) - -#define Page_size 0x2000U -#define Page_mask (Page_size - 1) -#define C218rx_spage 3 -#define C218tx_spage 4 -#define C218rx_pageno 1 -#define C218tx_pageno 4 -#define C218buf_pageno 5 - -#define C320p8rx_spage 3 -#define C320p8tx_spage 4 -#define C320p8rx_pgno 1 -#define C320p8tx_pgno 4 -#define C320p8buf_pgno 5 - -#define C320p16rx_spage 3 -#define C320p16tx_spage 4 -#define C320p16rx_pgno 1 -#define C320p16tx_pgno 2 -#define C320p16buf_pgno 3 - -#define C320p24rx_spage 3 -#define C320p24tx_spage 4 -#define C320p24rx_pgno 1 -#define C320p24tx_pgno 1 -#define C320p24buf_pgno 2 - -#define C320p32rx_spage 3 -#define C320p32tx_ofs C320p32rx_size -#define C320p32tx_spage 3 -#define C320p32buf_pgno 1 - -/* - * Host Status - */ -#define WakeupRx 0x01 -#define WakeupTx 0x02 -#define WakeupBreak 0x08 -#define WakeupLine 0x10 -#define WakeupIntr 0x20 -#define WakeupQuit 0x40 -#define WakeupEOF 0x80 /* used in VTIME control */ -#define WakeupRxTrigger 0x100 -#define WakeupTxTrigger 0x200 -/* - * Flag status - */ -#define Rx_over 0x01 -#define Xoff_state 0x02 -#define Tx_flowOff 0x04 -#define Tx_enable 0x08 -#define CTS_state 0x10 -#define DSR_state 0x20 -#define DCD_state 0x80 -/* - * FlowControl - */ -#define CTS_FlowCtl 1 -#define RTS_FlowCtl 2 -#define Tx_FlowCtl 4 -#define Rx_FlowCtl 8 -#define IXM_IXANY 0x10 - -#define LowWater 128 - -#define DTR_ON 1 -#define RTS_ON 2 -#define CTS_ON 1 -#define DSR_ON 2 -#define DCD_ON 8 - -/* mode definition */ -#define MX_CS8 0x03 -#define MX_CS7 0x02 -#define MX_CS6 0x01 -#define MX_CS5 0x00 - -#define MX_STOP1 0x00 -#define MX_STOP15 0x04 -#define MX_STOP2 0x08 - -#define MX_PARNONE 0x00 -#define MX_PAREVEN 0x40 -#define MX_PARODD 0xC0 - -#endif diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c deleted file mode 100644 index d188f378684d..000000000000 --- a/drivers/char/mxser.c +++ /dev/null @@ -1,2757 +0,0 @@ -/* - * mxser.c -- MOXA Smartio/Industio family multiport serial driver. - * - * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com). - * Copyright (C) 2006-2008 Jiri Slaby - * - * This code is loosely based on the 1.8 moxa driver which is based on - * Linux serial driver, written by Linus Torvalds, Theodore T'so and - * others. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox - * . The original 1.8 code is available on - * www.moxa.com. - * - Fixed x86_64 cleanness - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mxser.h" - -#define MXSER_VERSION "2.0.5" /* 1.14 */ -#define MXSERMAJOR 174 - -#define MXSER_BOARDS 4 /* Max. boards */ -#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ -#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) -#define MXSER_ISR_PASS_LIMIT 100 - -/*CheckIsMoxaMust return value*/ -#define MOXA_OTHER_UART 0x00 -#define MOXA_MUST_MU150_HWID 0x01 -#define MOXA_MUST_MU860_HWID 0x02 - -#define WAKEUP_CHARS 256 - -#define UART_MCR_AFE 0x20 -#define UART_LSR_SPECIAL 0x1E - -#define PCI_DEVICE_ID_POS104UL 0x1044 -#define PCI_DEVICE_ID_CB108 0x1080 -#define PCI_DEVICE_ID_CP102UF 0x1023 -#define PCI_DEVICE_ID_CP112UL 0x1120 -#define PCI_DEVICE_ID_CB114 0x1142 -#define PCI_DEVICE_ID_CP114UL 0x1143 -#define PCI_DEVICE_ID_CB134I 0x1341 -#define PCI_DEVICE_ID_CP138U 0x1380 - - -#define C168_ASIC_ID 1 -#define C104_ASIC_ID 2 -#define C102_ASIC_ID 0xB -#define CI132_ASIC_ID 4 -#define CI134_ASIC_ID 3 -#define CI104J_ASIC_ID 5 - -#define MXSER_HIGHBAUD 1 -#define MXSER_HAS2 2 - -/* This is only for PCI */ -static const struct { - int type; - int tx_fifo; - int rx_fifo; - int xmit_fifo_size; - int rx_high_water; - int rx_trigger; - int rx_low_water; - long max_baud; -} Gpci_uart_info[] = { - {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L}, - {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L}, - {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L} -}; -#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) - -struct mxser_cardinfo { - char *name; - unsigned int nports; - unsigned int flags; -}; - -static const struct mxser_cardinfo mxser_cards[] = { -/* 0*/ { "C168 series", 8, }, - { "C104 series", 4, }, - { "CI-104J series", 4, }, - { "C168H/PCI series", 8, }, - { "C104H/PCI series", 4, }, -/* 5*/ { "C102 series", 4, MXSER_HAS2 }, /* C102-ISA */ - { "CI-132 series", 4, MXSER_HAS2 }, - { "CI-134 series", 4, }, - { "CP-132 series", 2, }, - { "CP-114 series", 4, }, -/*10*/ { "CT-114 series", 4, }, - { "CP-102 series", 2, MXSER_HIGHBAUD }, - { "CP-104U series", 4, }, - { "CP-168U series", 8, }, - { "CP-132U series", 2, }, -/*15*/ { "CP-134U series", 4, }, - { "CP-104JU series", 4, }, - { "Moxa UC7000 Serial", 8, }, /* RC7000 */ - { "CP-118U series", 8, }, - { "CP-102UL series", 2, }, -/*20*/ { "CP-102U series", 2, }, - { "CP-118EL series", 8, }, - { "CP-168EL series", 8, }, - { "CP-104EL series", 4, }, - { "CB-108 series", 8, }, -/*25*/ { "CB-114 series", 4, }, - { "CB-134I series", 4, }, - { "CP-138U series", 8, }, - { "POS-104UL series", 4, }, - { "CP-114UL series", 4, }, -/*30*/ { "CP-102UF series", 2, }, - { "CP-112UL series", 2, }, -}; - -/* driver_data correspond to the lines in the structure above - see also ISA probe function before you change something */ -static struct pci_device_id mxser_pcibrds[] = { - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 9 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 10 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 11 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 24 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 25 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 26 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 30 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 31 }, - { } -}; -MODULE_DEVICE_TABLE(pci, mxser_pcibrds); - -static unsigned long ioaddr[MXSER_BOARDS]; -static int ttymajor = MXSERMAJOR; - -/* Variables for insmod */ - -MODULE_AUTHOR("Casper Yang"); -MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -module_param_array(ioaddr, ulong, NULL, 0); -MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board"); -module_param(ttymajor, int, 0); -MODULE_LICENSE("GPL"); - -struct mxser_log { - int tick; - unsigned long rxcnt[MXSER_PORTS]; - unsigned long txcnt[MXSER_PORTS]; -}; - -struct mxser_mon { - unsigned long rxcnt; - unsigned long txcnt; - unsigned long up_rxcnt; - unsigned long up_txcnt; - int modem_status; - unsigned char hold_reason; -}; - -struct mxser_mon_ext { - unsigned long rx_cnt[32]; - unsigned long tx_cnt[32]; - unsigned long up_rxcnt[32]; - unsigned long up_txcnt[32]; - int modem_status[32]; - - long baudrate[32]; - int databits[32]; - int stopbits[32]; - int parity[32]; - int flowctrl[32]; - int fifo[32]; - int iftype[32]; -}; - -struct mxser_board; - -struct mxser_port { - struct tty_port port; - struct mxser_board *board; - - unsigned long ioaddr; - unsigned long opmode_ioaddr; - int max_baud; - - int rx_high_water; - int rx_trigger; /* Rx fifo trigger level */ - int rx_low_water; - int baud_base; /* max. speed */ - int type; /* UART type */ - - int x_char; /* xon/xoff character */ - int IER; /* Interrupt Enable Register */ - int MCR; /* Modem control register */ - - unsigned char stop_rx; - unsigned char ldisc_stop_rx; - - int custom_divisor; - unsigned char err_shadow; - - struct async_icount icount; /* kernel counters for 4 input interrupts */ - int timeout; - - int read_status_mask; - int ignore_status_mask; - int xmit_fifo_size; - int xmit_head; - int xmit_tail; - int xmit_cnt; - - struct ktermios normal_termios; - - struct mxser_mon mon_data; - - spinlock_t slock; -}; - -struct mxser_board { - unsigned int idx; - int irq; - const struct mxser_cardinfo *info; - unsigned long vector; - unsigned long vector_mask; - - int chip_flag; - int uart_type; - - struct mxser_port ports[MXSER_PORTS_PER_BOARD]; -}; - -struct mxser_mstatus { - tcflag_t cflag; - int cts; - int dsr; - int ri; - int dcd; -}; - -static struct mxser_board mxser_boards[MXSER_BOARDS]; -static struct tty_driver *mxvar_sdriver; -static struct mxser_log mxvar_log; -static int mxser_set_baud_method[MXSER_PORTS + 1]; - -static void mxser_enable_must_enchance_mode(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr |= MOXA_MUST_EFR_EFRB_ENABLE; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -#ifdef CONFIG_PCI -static void mxser_disable_must_enchance_mode(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} -#endif - -static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_BANK_MASK; - efr |= MOXA_MUST_EFR_BANK0; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(value, baseio + MOXA_MUST_XON1_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_BANK_MASK; - efr |= MOXA_MUST_EFR_BANK0; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(value, baseio + MOXA_MUST_XOFF1_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_set_must_fifo_value(struct mxser_port *info) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(info->ioaddr + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR); - - efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_BANK_MASK; - efr |= MOXA_MUST_EFR_BANK1; - - outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER); - outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER); - outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER); - outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER); - outb(oldlcr, info->ioaddr + UART_LCR); -} - -static void mxser_set_must_enum_value(unsigned long baseio, u8 value) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_BANK_MASK; - efr |= MOXA_MUST_EFR_BANK2; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(value, baseio + MOXA_MUST_ENUM_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -#ifdef CONFIG_PCI -static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_BANK_MASK; - efr |= MOXA_MUST_EFR_BANK2; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - *pId = inb(baseio + MOXA_MUST_HWID_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} -#endif - -static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_SF_MASK; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_enable_must_tx_software_flow_control(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_SF_TX_MASK; - efr |= MOXA_MUST_EFR_SF_TX1; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_disable_must_tx_software_flow_control(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_SF_TX_MASK; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_enable_must_rx_software_flow_control(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_SF_RX_MASK; - efr |= MOXA_MUST_EFR_SF_RX1; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -static void mxser_disable_must_rx_software_flow_control(unsigned long baseio) -{ - u8 oldlcr; - u8 efr; - - oldlcr = inb(baseio + UART_LCR); - outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); - - efr = inb(baseio + MOXA_MUST_EFR_REGISTER); - efr &= ~MOXA_MUST_EFR_SF_RX_MASK; - - outb(efr, baseio + MOXA_MUST_EFR_REGISTER); - outb(oldlcr, baseio + UART_LCR); -} - -#ifdef CONFIG_PCI -static int __devinit CheckIsMoxaMust(unsigned long io) -{ - u8 oldmcr, hwid; - int i; - - outb(0, io + UART_LCR); - mxser_disable_must_enchance_mode(io); - oldmcr = inb(io + UART_MCR); - outb(0, io + UART_MCR); - mxser_set_must_xon1_value(io, 0x11); - if ((hwid = inb(io + UART_MCR)) != 0) { - outb(oldmcr, io + UART_MCR); - return MOXA_OTHER_UART; - } - - mxser_get_must_hardware_id(io, &hwid); - for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ - if (hwid == Gpci_uart_info[i].type) - return (int)hwid; - } - return MOXA_OTHER_UART; -} -#endif - -static void process_txrx_fifo(struct mxser_port *info) -{ - int i; - - if ((info->type == PORT_16450) || (info->type == PORT_8250)) { - info->rx_trigger = 1; - info->rx_high_water = 1; - info->rx_low_water = 1; - info->xmit_fifo_size = 1; - } else - for (i = 0; i < UART_INFO_NUM; i++) - if (info->board->chip_flag == Gpci_uart_info[i].type) { - info->rx_trigger = Gpci_uart_info[i].rx_trigger; - info->rx_low_water = Gpci_uart_info[i].rx_low_water; - info->rx_high_water = Gpci_uart_info[i].rx_high_water; - info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size; - break; - } -} - -static unsigned char mxser_get_msr(int baseaddr, int mode, int port) -{ - static unsigned char mxser_msr[MXSER_PORTS + 1]; - unsigned char status = 0; - - status = inb(baseaddr + UART_MSR); - - mxser_msr[port] &= 0x0F; - mxser_msr[port] |= status; - status = mxser_msr[port]; - if (mode) - mxser_msr[port] = 0; - - return status; -} - -static int mxser_carrier_raised(struct tty_port *port) -{ - struct mxser_port *mp = container_of(port, struct mxser_port, port); - return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; -} - -static void mxser_dtr_rts(struct tty_port *port, int on) -{ - struct mxser_port *mp = container_of(port, struct mxser_port, port); - unsigned long flags; - - spin_lock_irqsave(&mp->slock, flags); - if (on) - outb(inb(mp->ioaddr + UART_MCR) | - UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR); - else - outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS), - mp->ioaddr + UART_MCR); - spin_unlock_irqrestore(&mp->slock, flags); -} - -static int mxser_set_baud(struct tty_struct *tty, long newspd) -{ - struct mxser_port *info = tty->driver_data; - int quot = 0, baud; - unsigned char cval; - - if (!info->ioaddr) - return -1; - - if (newspd > info->max_baud) - return -1; - - if (newspd == 134) { - quot = 2 * info->baud_base / 269; - tty_encode_baud_rate(tty, 134, 134); - } else if (newspd) { - quot = info->baud_base / newspd; - if (quot == 0) - quot = 1; - baud = info->baud_base/quot; - tty_encode_baud_rate(tty, baud, baud); - } else { - quot = 0; - } - - info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); - info->timeout += HZ / 50; /* Add .02 seconds of slop */ - - if (quot) { - info->MCR |= UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - } else { - info->MCR &= ~UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - return 0; - } - - cval = inb(info->ioaddr + UART_LCR); - - outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ - - outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ - outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ - outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ - -#ifdef BOTHER - if (C_BAUD(tty) == BOTHER) { - quot = info->baud_base % newspd; - quot *= 8; - if (quot % newspd > newspd / 2) { - quot /= newspd; - quot++; - } else - quot /= newspd; - - mxser_set_must_enum_value(info->ioaddr, quot); - } else -#endif - mxser_set_must_enum_value(info->ioaddr, 0); - - return 0; -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static int mxser_change_speed(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct mxser_port *info = tty->driver_data; - unsigned cflag, cval, fcr; - int ret = 0; - unsigned char status; - - cflag = tty->termios->c_cflag; - if (!info->ioaddr) - return ret; - - if (mxser_set_baud_method[tty->index] == 0) - mxser_set_baud(tty, tty_get_baud_rate(tty)); - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - cval = 0x00; - break; - case CS6: - cval = 0x01; - break; - case CS7: - cval = 0x02; - break; - case CS8: - cval = 0x03; - break; - default: - cval = 0x00; - break; /* too keep GCC shut... */ - } - if (cflag & CSTOPB) - cval |= 0x04; - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; - - if ((info->type == PORT_8250) || (info->type == PORT_16450)) { - if (info->board->chip_flag) { - fcr = UART_FCR_ENABLE_FIFO; - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - mxser_set_must_fifo_value(info); - } else - fcr = 0; - } else { - fcr = UART_FCR_ENABLE_FIFO; - if (info->board->chip_flag) { - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - mxser_set_must_fifo_value(info); - } else { - switch (info->rx_trigger) { - case 1: - fcr |= UART_FCR_TRIGGER_1; - break; - case 4: - fcr |= UART_FCR_TRIGGER_4; - break; - case 8: - fcr |= UART_FCR_TRIGGER_8; - break; - default: - fcr |= UART_FCR_TRIGGER_14; - break; - } - } - } - - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - info->MCR &= ~UART_MCR_AFE; - if (cflag & CRTSCTS) { - info->port.flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - if ((info->type == PORT_16550A) || (info->board->chip_flag)) { - info->MCR |= UART_MCR_AFE; - } else { - status = inb(info->ioaddr + UART_MSR); - if (tty->hw_stopped) { - if (status & UART_MSR_CTS) { - tty->hw_stopped = 0; - if (info->type != PORT_16550A && - !info->board->chip_flag) { - outb(info->IER & ~UART_IER_THRI, - info->ioaddr + - UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + - UART_IER); - } - tty_wakeup(tty); - } - } else { - if (!(status & UART_MSR_CTS)) { - tty->hw_stopped = 1; - if ((info->type != PORT_16550A) && - (!info->board->chip_flag)) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->ioaddr + - UART_IER); - } - } - } - } - } else { - info->port.flags &= ~ASYNC_CTS_FLOW; - } - outb(info->MCR, info->ioaddr + UART_MCR); - if (cflag & CLOCAL) { - info->port.flags &= ~ASYNC_CHECK_CD; - } else { - info->port.flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - outb(info->IER, info->ioaddr + UART_IER); - - /* - * Set up parity check flag - */ - info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(tty)) - info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - info->read_status_mask |= UART_LSR_BI; - - info->ignore_status_mask = 0; - - if (I_IGNBRK(tty)) { - info->ignore_status_mask |= UART_LSR_BI; - info->read_status_mask |= UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(tty)) { - info->ignore_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - info->read_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - } - } - if (info->board->chip_flag) { - mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); - mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); - if (I_IXON(tty)) { - mxser_enable_must_rx_software_flow_control( - info->ioaddr); - } else { - mxser_disable_must_rx_software_flow_control( - info->ioaddr); - } - if (I_IXOFF(tty)) { - mxser_enable_must_tx_software_flow_control( - info->ioaddr); - } else { - mxser_disable_must_tx_software_flow_control( - info->ioaddr); - } - } - - - outb(fcr, info->ioaddr + UART_FCR); /* set fcr */ - outb(cval, info->ioaddr + UART_LCR); - - return ret; -} - -static void mxser_check_modem_status(struct tty_struct *tty, - struct mxser_port *port, int status) -{ - /* update input line counters */ - if (status & UART_MSR_TERI) - port->icount.rng++; - if (status & UART_MSR_DDSR) - port->icount.dsr++; - if (status & UART_MSR_DDCD) - port->icount.dcd++; - if (status & UART_MSR_DCTS) - port->icount.cts++; - port->mon_data.modem_status = status; - wake_up_interruptible(&port->port.delta_msr_wait); - - if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { - if (status & UART_MSR_DCD) - wake_up_interruptible(&port->port.open_wait); - } - - if (port->port.flags & ASYNC_CTS_FLOW) { - if (tty->hw_stopped) { - if (status & UART_MSR_CTS) { - tty->hw_stopped = 0; - - if ((port->type != PORT_16550A) && - (!port->board->chip_flag)) { - outb(port->IER & ~UART_IER_THRI, - port->ioaddr + UART_IER); - port->IER |= UART_IER_THRI; - outb(port->IER, port->ioaddr + - UART_IER); - } - tty_wakeup(tty); - } - } else { - if (!(status & UART_MSR_CTS)) { - tty->hw_stopped = 1; - if (port->type != PORT_16550A && - !port->board->chip_flag) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + - UART_IER); - } - } - } - } -} - -static int mxser_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct mxser_port *info = container_of(port, struct mxser_port, port); - unsigned long page; - unsigned long flags; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - spin_lock_irqsave(&info->slock, flags); - - if (!info->ioaddr || !info->type) { - set_bit(TTY_IO_ERROR, &tty->flags); - free_page(page); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } - info->port.xmit_buf = (unsigned char *) page; - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in mxser_change_speed()) - */ - if (info->board->chip_flag) - outb((UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR); - else - outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->ioaddr + UART_FCR); - - /* - * At this point there's no way the LSR could still be 0xFF; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (inb(info->ioaddr + UART_LSR) == 0xff) { - spin_unlock_irqrestore(&info->slock, flags); - if (capable(CAP_SYS_ADMIN)) { - set_bit(TTY_IO_ERROR, &tty->flags); - return 0; - } else - return -ENODEV; - } - - /* - * Clear the interrupt registers. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - /* - * Now, initialize the UART - */ - outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ - info->MCR = UART_MCR_DTR | UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - - /* - * Finally, enable interrupts - */ - info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - - if (info->board->chip_flag) - info->IER |= MOXA_MUST_IER_EGDAI; - outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ - - /* - * And clear the interrupt registers again for luck. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - mxser_change_speed(tty, NULL); - spin_unlock_irqrestore(&info->slock, flags); - - return 0; -} - -/* - * This routine will shutdown a serial port - */ -static void mxser_shutdown_port(struct tty_port *port) -{ - struct mxser_port *info = container_of(port, struct mxser_port, port); - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be waken up - */ - wake_up_interruptible(&info->port.delta_msr_wait); - - /* - * Free the xmit buffer, if necessary - */ - if (info->port.xmit_buf) { - free_page((unsigned long) info->port.xmit_buf); - info->port.xmit_buf = NULL; - } - - info->IER = 0; - outb(0x00, info->ioaddr + UART_IER); - - /* clear Rx/Tx FIFO's */ - if (info->board->chip_flag) - outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE, - info->ioaddr + UART_FCR); - else - outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - info->ioaddr + UART_FCR); - - /* read data port to reset things */ - (void) inb(info->ioaddr + UART_RX); - - - if (info->board->chip_flag) - SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr); - - spin_unlock_irqrestore(&info->slock, flags); -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int mxser_open(struct tty_struct *tty, struct file *filp) -{ - struct mxser_port *info; - int line; - - line = tty->index; - if (line == MXSER_PORTS) - return 0; - if (line < 0 || line > MXSER_PORTS) - return -ENODEV; - info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD]; - if (!info->ioaddr) - return -ENODEV; - - tty->driver_data = info; - return tty_port_open(&info->port, tty, filp); -} - -static void mxser_flush_buffer(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - char fcr; - unsigned long flags; - - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - fcr = inb(info->ioaddr + UART_FCR); - outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->ioaddr + UART_FCR); - outb(fcr, info->ioaddr + UART_FCR); - - spin_unlock_irqrestore(&info->slock, flags); - - tty_wakeup(tty); -} - - -static void mxser_close_port(struct tty_port *port) -{ - struct mxser_port *info = container_of(port, struct mxser_port, port); - unsigned long timeout; - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->IER &= ~UART_IER_RLSI; - if (info->board->chip_flag) - info->IER &= ~MOXA_MUST_RECV_ISR; - - outb(info->IER, info->ioaddr + UART_IER); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies + HZ; - while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) { - schedule_timeout_interruptible(5); - if (time_after(jiffies, timeout)) - break; - } -} - -/* - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - */ -static void mxser_close(struct tty_struct *tty, struct file *filp) -{ - struct mxser_port *info = tty->driver_data; - struct tty_port *port = &info->port; - - if (tty->index == MXSER_PORTS || info == NULL) - return; - if (tty_port_close_start(port, tty, filp) == 0) - return; - mutex_lock(&port->mutex); - mxser_close_port(port); - mxser_flush_buffer(tty); - mxser_shutdown_port(port); - clear_bit(ASYNCB_INITIALIZED, &port->flags); - mutex_unlock(&port->mutex); - /* Right now the tty_port set is done outside of the close_end helper - as we don't yet have everyone using refcounts */ - tty_port_close_end(port, tty); - tty_port_tty_set(port, NULL); -} - -static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - int c, total = 0; - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (!info->port.xmit_buf) - return 0; - - while (1) { - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; - - memcpy(info->port.xmit_buf + info->xmit_head, buf, c); - spin_lock_irqsave(&info->slock, flags); - info->xmit_head = (info->xmit_head + c) & - (SERIAL_XMIT_SIZE - 1); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->slock, flags); - - buf += c; - count -= c; - total += c; - } - - if (info->xmit_cnt && !tty->stopped) { - if (!tty->hw_stopped || - (info->type == PORT_16550A) || - (info->board->chip_flag)) { - spin_lock_irqsave(&info->slock, flags); - outb(info->IER & ~UART_IER_THRI, info->ioaddr + - UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - spin_unlock_irqrestore(&info->slock, flags); - } - } - return total; -} - -static int mxser_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (!info->port.xmit_buf) - return 0; - - if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) - return 0; - - spin_lock_irqsave(&info->slock, flags); - info->port.xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE - 1; - info->xmit_cnt++; - spin_unlock_irqrestore(&info->slock, flags); - if (!tty->stopped) { - if (!tty->hw_stopped || - (info->type == PORT_16550A) || - info->board->chip_flag) { - spin_lock_irqsave(&info->slock, flags); - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - spin_unlock_irqrestore(&info->slock, flags); - } - } - return 1; -} - - -static void mxser_flush_chars(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf || - (tty->hw_stopped && info->type != PORT_16550A && - !info->board->chip_flag)) - return; - - spin_lock_irqsave(&info->slock, flags); - - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - - spin_unlock_irqrestore(&info->slock, flags); -} - -static int mxser_write_room(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - int ret; - - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - return ret < 0 ? 0 : ret; -} - -static int mxser_chars_in_buffer(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - return info->xmit_cnt; -} - -/* - * ------------------------------------------------------------ - * friends of mxser_ioctl() - * ------------------------------------------------------------ - */ -static int mxser_get_serial_info(struct tty_struct *tty, - struct serial_struct __user *retinfo) -{ - struct mxser_port *info = tty->driver_data; - struct serial_struct tmp = { - .type = info->type, - .line = tty->index, - .port = info->ioaddr, - .irq = info->board->irq, - .flags = info->port.flags, - .baud_base = info->baud_base, - .close_delay = info->port.close_delay, - .closing_wait = info->port.closing_wait, - .custom_divisor = info->custom_divisor, - .hub6 = 0 - }; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int mxser_set_serial_info(struct tty_struct *tty, - struct serial_struct __user *new_info) -{ - struct mxser_port *info = tty->driver_data; - struct tty_port *port = &info->port; - struct serial_struct new_serial; - speed_t baud; - unsigned long sl_flags; - unsigned int flags; - int retval = 0; - - if (!new_info || !info->ioaddr) - return -ENODEV; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - if (new_serial.irq != info->board->irq || - new_serial.port != info->ioaddr) - return -EINVAL; - - flags = port->flags & ASYNC_SPD_MASK; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.close_delay != info->port.close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - } else { - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - port->flags = ((port->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - port->close_delay = new_serial.close_delay * HZ / 100; - port->closing_wait = new_serial.closing_wait * HZ / 100; - tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && - (new_serial.baud_base != info->baud_base || - new_serial.custom_divisor != - info->custom_divisor)) { - if (new_serial.custom_divisor == 0) - return -EINVAL; - baud = new_serial.baud_base / new_serial.custom_divisor; - tty_encode_baud_rate(tty, baud, baud); - } - } - - info->type = new_serial.type; - - process_txrx_fifo(info); - - if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { - if (flags != (port->flags & ASYNC_SPD_MASK)) { - spin_lock_irqsave(&info->slock, sl_flags); - mxser_change_speed(tty, NULL); - spin_unlock_irqrestore(&info->slock, sl_flags); - } - } else { - retval = mxser_activate(port, tty); - if (retval == 0) - set_bit(ASYNCB_INITIALIZED, &port->flags); - } - return retval; -} - -/* - * mxser_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 mxser_get_lsr_info(struct mxser_port *info, - unsigned int __user *value) -{ - unsigned char status; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - status = inb(info->ioaddr + UART_LSR); - spin_unlock_irqrestore(&info->slock, flags); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result, value); -} - -static int mxser_tiocmget(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned char control, status; - unsigned long flags; - - - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - control = info->MCR; - - spin_lock_irqsave(&info->slock, flags); - status = inb(info->ioaddr + UART_MSR); - if (status & UART_MSR_ANY_DELTA) - mxser_check_modem_status(tty, info, status); - spin_unlock_irqrestore(&info->slock, flags); - return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | - ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | - ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | - ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | - ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | - ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); -} - -static int mxser_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - spin_lock_irqsave(&info->slock, flags); - - if (set & TIOCM_RTS) - info->MCR |= UART_MCR_RTS; - if (set & TIOCM_DTR) - info->MCR |= UART_MCR_DTR; - - if (clear & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; - - outb(info->MCR, info->ioaddr + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -static int __init mxser_program_mode(int port) -{ - int id, i, j, n; - - outb(0, port); - outb(0, port); - outb(0, port); - (void)inb(port); - (void)inb(port); - outb(0, port); - (void)inb(port); - - id = inb(port + 1) & 0x1F; - if ((id != C168_ASIC_ID) && - (id != C104_ASIC_ID) && - (id != C102_ASIC_ID) && - (id != CI132_ASIC_ID) && - (id != CI134_ASIC_ID) && - (id != CI104J_ASIC_ID)) - return -1; - for (i = 0, j = 0; i < 4; i++) { - n = inb(port + 2); - if (n == 'M') { - j = 1; - } else if ((j == 1) && (n == 1)) { - j = 2; - break; - } else - j = 0; - } - if (j != 2) - id = -2; - return id; -} - -static void __init mxser_normal_mode(int port) -{ - int i, n; - - outb(0xA5, port + 1); - outb(0x80, port + 3); - outb(12, port + 0); /* 9600 bps */ - outb(0, port + 1); - outb(0x03, port + 3); /* 8 data bits */ - outb(0x13, port + 4); /* loop back mode */ - for (i = 0; i < 16; i++) { - n = inb(port + 5); - if ((n & 0x61) == 0x60) - break; - if ((n & 1) == 1) - (void)inb(port); - } - outb(0x00, port + 4); -} - -#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ -#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ -#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ -#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ -#define EN_CCMD 0x000 /* Chip's command register */ -#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ -#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ -#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ -#define EN0_DCFG 0x00E /* Data configuration reg WR */ -#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ -#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ -#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ -static int __init mxser_read_register(int port, unsigned short *regs) -{ - int i, k, value, id; - unsigned int j; - - id = mxser_program_mode(port); - if (id < 0) - return id; - for (i = 0; i < 14; i++) { - k = (i & 0x3F) | 0x180; - for (j = 0x100; j > 0; j >>= 1) { - outb(CHIP_CS, port); - if (k & j) { - outb(CHIP_CS | CHIP_DO, port); - outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ - } else { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ - } - } - (void)inb(port); - value = 0; - for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); - if (inb(port) & CHIP_DI) - value |= j; - } - regs[i] = value; - outb(0, port); - } - mxser_normal_mode(port); - return id; -} - -static int mxser_ioctl_special(unsigned int cmd, void __user *argp) -{ - struct mxser_port *ip; - struct tty_port *port; - struct tty_struct *tty; - int result, status; - unsigned int i, j; - int ret = 0; - - switch (cmd) { - case MOXA_GET_MAJOR: - if (printk_ratelimit()) - printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl " - "%x (GET_MAJOR), fix your userspace\n", - current->comm, cmd); - return put_user(ttymajor, (int __user *)argp); - - case MOXA_CHKPORTENABLE: - result = 0; - for (i = 0; i < MXSER_BOARDS; i++) - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) - if (mxser_boards[i].ports[j].ioaddr) - result |= (1 << i); - return put_user(result, (unsigned long __user *)argp); - case MOXA_GETDATACOUNT: - /* The receive side is locked by port->slock but it isn't - clear that an exact snapshot is worth copying here */ - if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) - ret = -EFAULT; - return ret; - case MOXA_GETMSTATUS: { - struct mxser_mstatus ms, __user *msu = argp; - for (i = 0; i < MXSER_BOARDS; i++) - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { - ip = &mxser_boards[i].ports[j]; - port = &ip->port; - memset(&ms, 0, sizeof(ms)); - - mutex_lock(&port->mutex); - if (!ip->ioaddr) - goto copy; - - tty = tty_port_tty_get(port); - - if (!tty || !tty->termios) - ms.cflag = ip->normal_termios.c_cflag; - else - ms.cflag = tty->termios->c_cflag; - tty_kref_put(tty); - spin_lock_irq(&ip->slock); - status = inb(ip->ioaddr + UART_MSR); - spin_unlock_irq(&ip->slock); - if (status & UART_MSR_DCD) - ms.dcd = 1; - if (status & UART_MSR_DSR) - ms.dsr = 1; - if (status & UART_MSR_CTS) - ms.cts = 1; - copy: - mutex_unlock(&port->mutex); - if (copy_to_user(msu, &ms, sizeof(ms))) - return -EFAULT; - msu++; - } - return 0; - } - case MOXA_ASPP_MON_EXT: { - struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */ - unsigned int cflag, iflag, p; - u8 opmode; - - me = kzalloc(sizeof(*me), GFP_KERNEL); - if (!me) - return -ENOMEM; - - for (i = 0, p = 0; i < MXSER_BOARDS; i++) { - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) { - if (p >= ARRAY_SIZE(me->rx_cnt)) { - i = MXSER_BOARDS; - break; - } - ip = &mxser_boards[i].ports[j]; - port = &ip->port; - - mutex_lock(&port->mutex); - if (!ip->ioaddr) { - mutex_unlock(&port->mutex); - continue; - } - - spin_lock_irq(&ip->slock); - status = mxser_get_msr(ip->ioaddr, 0, p); - - if (status & UART_MSR_TERI) - ip->icount.rng++; - if (status & UART_MSR_DDSR) - ip->icount.dsr++; - if (status & UART_MSR_DDCD) - ip->icount.dcd++; - if (status & UART_MSR_DCTS) - ip->icount.cts++; - - ip->mon_data.modem_status = status; - me->rx_cnt[p] = ip->mon_data.rxcnt; - me->tx_cnt[p] = ip->mon_data.txcnt; - me->up_rxcnt[p] = ip->mon_data.up_rxcnt; - me->up_txcnt[p] = ip->mon_data.up_txcnt; - me->modem_status[p] = - ip->mon_data.modem_status; - spin_unlock_irq(&ip->slock); - - tty = tty_port_tty_get(&ip->port); - - if (!tty || !tty->termios) { - cflag = ip->normal_termios.c_cflag; - iflag = ip->normal_termios.c_iflag; - me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios); - } else { - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; - me->baudrate[p] = tty_get_baud_rate(tty); - } - tty_kref_put(tty); - - me->databits[p] = cflag & CSIZE; - me->stopbits[p] = cflag & CSTOPB; - me->parity[p] = cflag & (PARENB | PARODD | - CMSPAR); - - if (cflag & CRTSCTS) - me->flowctrl[p] |= 0x03; - - if (iflag & (IXON | IXOFF)) - me->flowctrl[p] |= 0x0C; - - if (ip->type == PORT_16550A) - me->fifo[p] = 1; - - opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); - opmode &= OP_MODE_MASK; - me->iftype[p] = opmode; - mutex_unlock(&port->mutex); - } - } - if (copy_to_user(argp, me, sizeof(*me))) - ret = -EFAULT; - kfree(me); - return ret; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, - struct async_icount *cprev) -{ - struct async_icount cnow; - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->slock, flags); - - ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); - - *cprev = cnow; - - return ret; -} - -static int mxser_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct mxser_port *info = tty->driver_data; - struct tty_port *port = &info->port; - struct async_icount cnow; - unsigned long flags; - void __user *argp = (void __user *)arg; - int retval; - - if (tty->index == MXSER_PORTS) - return mxser_ioctl_special(cmd, argp); - - if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) { - int p; - unsigned long opmode; - static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f }; - int shiftbit; - unsigned char val, mask; - - p = tty->index % 4; - if (cmd == MOXA_SET_OP_MODE) { - if (get_user(opmode, (int __user *) argp)) - return -EFAULT; - if (opmode != RS232_MODE && - opmode != RS485_2WIRE_MODE && - opmode != RS422_MODE && - opmode != RS485_4WIRE_MODE) - return -EFAULT; - mask = ModeMask[p]; - shiftbit = p * 2; - spin_lock_irq(&info->slock); - val = inb(info->opmode_ioaddr); - val &= mask; - val |= (opmode << shiftbit); - outb(val, info->opmode_ioaddr); - spin_unlock_irq(&info->slock); - } else { - shiftbit = p * 2; - spin_lock_irq(&info->slock); - opmode = inb(info->opmode_ioaddr) >> shiftbit; - spin_unlock_irq(&info->slock); - opmode &= OP_MODE_MASK; - if (put_user(opmode, (int __user *)argp)) - return -EFAULT; - } - return 0; - } - - if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && - test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - switch (cmd) { - case TIOCGSERIAL: - mutex_lock(&port->mutex); - retval = mxser_get_serial_info(tty, argp); - mutex_unlock(&port->mutex); - return retval; - case TIOCSSERIAL: - mutex_lock(&port->mutex); - retval = mxser_set_serial_info(tty, argp); - mutex_unlock(&port->mutex); - return retval; - case TIOCSERGETLSR: /* Get line status register */ - return mxser_get_lsr_info(info, argp); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* note the counters on entry */ - spin_unlock_irqrestore(&info->slock, flags); - - return wait_event_interruptible(info->port.delta_msr_wait, - mxser_cflags_changed(info, arg, &cnow)); - case MOXA_HighSpeedOn: - return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); - case MOXA_SDS_RSTICOUNTER: - spin_lock_irq(&info->slock); - info->mon_data.rxcnt = 0; - info->mon_data.txcnt = 0; - spin_unlock_irq(&info->slock); - return 0; - - case MOXA_ASPP_OQUEUE:{ - int len, lsr; - - len = mxser_chars_in_buffer(tty); - spin_lock_irq(&info->slock); - lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE; - spin_unlock_irq(&info->slock); - len += (lsr ? 0 : 1); - - return put_user(len, (int __user *)argp); - } - case MOXA_ASPP_MON: { - int mcr, status; - - spin_lock_irq(&info->slock); - status = mxser_get_msr(info->ioaddr, 1, tty->index); - mxser_check_modem_status(tty, info, status); - - mcr = inb(info->ioaddr + UART_MCR); - spin_unlock_irq(&info->slock); - - if (mcr & MOXA_MUST_MCR_XON_FLAG) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD; - - if (mcr & MOXA_MUST_MCR_TX_XON) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; - - if (tty->hw_stopped) - info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; - else - info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; - - if (copy_to_user(argp, &info->mon_data, - sizeof(struct mxser_mon))) - return -EFAULT; - - return 0; - } - case MOXA_ASPP_LSTATUS: { - if (put_user(info->err_shadow, (unsigned char __user *)argp)) - return -EFAULT; - - info->err_shadow = 0; - return 0; - } - case MOXA_SET_BAUD_METHOD: { - int method; - - if (get_user(method, (int __user *)argp)) - return -EFAULT; - mxser_set_baud_method[tty->index] = method; - return put_user(method, (int __user *)argp); - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - -static int mxser_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) - -{ - struct mxser_port *info = tty->driver_data; - struct async_icount cnow; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->slock, flags); - - icount->frame = cnow.frame; - icount->brk = cnow.brk; - icount->overrun = cnow.overrun; - icount->buf_overrun = cnow.buf_overrun; - icount->parity = cnow.parity; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - return 0; -} - -static void mxser_stoprx(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - info->ldisc_stop_rx = 1; - if (I_IXOFF(tty)) { - if (info->board->chip_flag) { - info->IER &= ~MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char = STOP_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - - if (tty->termios->c_cflag & CRTSCTS) { - info->MCR &= ~UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -/* - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - */ -static void mxser_throttle(struct tty_struct *tty) -{ - mxser_stoprx(tty); -} - -static void mxser_unthrottle(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - /* startrx */ - info->ldisc_stop_rx = 0; - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else { - if (info->board->chip_flag) { - info->IER |= MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char = START_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - } - - if (tty->termios->c_cflag & CRTSCTS) { - info->MCR |= UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -/* - * mxser_stop() and mxser_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - */ -static void mxser_stop(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->IER & UART_IER_THRI) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_start(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->xmit_cnt && info->port.xmit_buf) { - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(tty, old_termios); - spin_unlock_irqrestore(&info->slock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - mxser_start(tty); - } - - /* Handle sw stopped */ - if ((old_termios->c_iflag & IXON) && - !(tty->termios->c_iflag & IXON)) { - tty->stopped = 0; - - if (info->board->chip_flag) { - spin_lock_irqsave(&info->slock, flags); - mxser_disable_must_rx_software_flow_control( - info->ioaddr); - spin_unlock_irqrestore(&info->slock, flags); - } - - mxser_start(tty); - } -} - -/* - * mxser_wait_until_sent() --- wait until the transmitter is empty - */ -static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct mxser_port *info = tty->driver_data; - unsigned long orig_jiffies, char_time; - unsigned long flags; - int lsr; - - if (info->type == PORT_UNKNOWN) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - 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. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; - char_time = char_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout = 2 * info->timeout; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...", - timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - spin_lock_irqsave(&info->slock, flags); - while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif - spin_unlock_irqrestore(&info->slock, flags); - schedule_timeout_interruptible(char_time); - spin_lock_irqsave(&info->slock, flags); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - spin_unlock_irqrestore(&info->slock, flags); - set_current_state(TASK_RUNNING); - -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * This routine is called by tty_hangup() when a hangup is signaled. - */ -static void mxser_hangup(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - mxser_flush_buffer(tty); - tty_port_hangup(&info->port); -} - -/* - * mxser_rs_break() --- routine which turns the break handling on or off - */ -static int mxser_rs_break(struct tty_struct *tty, int break_state) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (break_state == -1) - outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, - info->ioaddr + UART_LCR); - else - outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -static void mxser_receive_chars(struct tty_struct *tty, - struct mxser_port *port, int *status) -{ - unsigned char ch, gdl; - int ignored = 0; - int cnt = 0; - int recv_room; - int max = 256; - - recv_room = tty->receive_room; - if (recv_room == 0 && !port->ldisc_stop_rx) - mxser_stoprx(tty); - if (port->board->chip_flag != MOXA_OTHER_UART) { - - if (*status & UART_LSR_SPECIAL) - goto intr_old; - if (port->board->chip_flag == MOXA_MUST_MU860_HWID && - (*status & MOXA_MUST_LSR_RERR)) - goto intr_old; - if (*status & MOXA_MUST_LSR_RERR) - goto intr_old; - - gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); - - if (port->board->chip_flag == MOXA_MUST_MU150_HWID) - gdl &= MOXA_MUST_GDL_MASK; - if (gdl >= recv_room) { - if (!port->ldisc_stop_rx) - mxser_stoprx(tty); - } - while (gdl--) { - ch = inb(port->ioaddr + UART_RX); - tty_insert_flip_char(tty, ch, 0); - cnt++; - } - goto end_intr; - } -intr_old: - - do { - if (max-- < 0) - break; - - ch = inb(port->ioaddr + UART_RX); - if (port->board->chip_flag && (*status & UART_LSR_OE)) - outb(0x23, port->ioaddr + UART_FCR); - *status &= port->read_status_mask; - if (*status & port->ignore_status_mask) { - if (++ignored > 100) - break; - } else { - char flag = 0; - if (*status & UART_LSR_SPECIAL) { - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - port->icount.brk++; - - if (port->port.flags & ASYNC_SAK) - do_SAK(tty); - } else if (*status & UART_LSR_PE) { - flag = TTY_PARITY; - port->icount.parity++; - } else if (*status & UART_LSR_FE) { - flag = TTY_FRAME; - port->icount.frame++; - } else if (*status & UART_LSR_OE) { - flag = TTY_OVERRUN; - port->icount.overrun++; - } else - flag = TTY_BREAK; - } - tty_insert_flip_char(tty, ch, flag); - cnt++; - if (cnt >= recv_room) { - if (!port->ldisc_stop_rx) - mxser_stoprx(tty); - break; - } - - } - - if (port->board->chip_flag) - break; - - *status = inb(port->ioaddr + UART_LSR); - } while (*status & UART_LSR_DR); - -end_intr: - mxvar_log.rxcnt[tty->index] += cnt; - port->mon_data.rxcnt += cnt; - port->mon_data.up_rxcnt += cnt; - - /* - * We are called from an interrupt context with &port->slock - * being held. Drop it temporarily in order to prevent - * recursive locking. - */ - spin_unlock(&port->slock); - tty_flip_buffer_push(tty); - spin_lock(&port->slock); -} - -static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) -{ - int count, cnt; - - if (port->x_char) { - outb(port->x_char, port->ioaddr + UART_TX); - port->x_char = 0; - mxvar_log.txcnt[tty->index]++; - port->mon_data.txcnt++; - port->mon_data.up_txcnt++; - port->icount.tx++; - return; - } - - if (port->port.xmit_buf == NULL) - return; - - if (port->xmit_cnt <= 0 || tty->stopped || - (tty->hw_stopped && - (port->type != PORT_16550A) && - (!port->board->chip_flag))) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + UART_IER); - return; - } - - cnt = port->xmit_cnt; - count = port->xmit_fifo_size; - do { - outb(port->port.xmit_buf[port->xmit_tail++], - port->ioaddr + UART_TX); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt); - - port->mon_data.txcnt += (cnt - port->xmit_cnt); - port->mon_data.up_txcnt += (cnt - port->xmit_cnt); - port->icount.tx += (cnt - port->xmit_cnt); - - if (port->xmit_cnt < WAKEUP_CHARS) - tty_wakeup(tty); - - if (port->xmit_cnt <= 0) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + UART_IER); - } -} - -/* - * This is the serial driver's generic interrupt routine - */ -static irqreturn_t mxser_interrupt(int irq, void *dev_id) -{ - int status, iir, i; - struct mxser_board *brd = NULL; - struct mxser_port *port; - int max, irqbits, bits, msr; - unsigned int int_cnt, pass_counter = 0; - int handled = IRQ_NONE; - struct tty_struct *tty; - - for (i = 0; i < MXSER_BOARDS; i++) - if (dev_id == &mxser_boards[i]) { - brd = dev_id; - break; - } - - if (i == MXSER_BOARDS) - goto irq_stop; - if (brd == NULL) - goto irq_stop; - max = brd->info->nports; - while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { - irqbits = inb(brd->vector) & brd->vector_mask; - if (irqbits == brd->vector_mask) - break; - - handled = IRQ_HANDLED; - for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { - if (irqbits == brd->vector_mask) - break; - if (bits & irqbits) - continue; - port = &brd->ports[i]; - - int_cnt = 0; - spin_lock(&port->slock); - do { - iir = inb(port->ioaddr + UART_IIR); - if (iir & UART_IIR_NO_INT) - break; - iir &= MOXA_MUST_IIR_MASK; - tty = tty_port_tty_get(&port->port); - if (!tty || - (port->port.flags & ASYNC_CLOSING) || - !(port->port.flags & - ASYNC_INITIALIZED)) { - status = inb(port->ioaddr + UART_LSR); - outb(0x27, port->ioaddr + UART_FCR); - inb(port->ioaddr + UART_MSR); - tty_kref_put(tty); - break; - } - - status = inb(port->ioaddr + UART_LSR); - - if (status & UART_LSR_PE) - port->err_shadow |= NPPI_NOTIFY_PARITY; - if (status & UART_LSR_FE) - port->err_shadow |= NPPI_NOTIFY_FRAMING; - if (status & UART_LSR_OE) - port->err_shadow |= - NPPI_NOTIFY_HW_OVERRUN; - if (status & UART_LSR_BI) - port->err_shadow |= NPPI_NOTIFY_BREAK; - - if (port->board->chip_flag) { - if (iir == MOXA_MUST_IIR_GDA || - iir == MOXA_MUST_IIR_RDA || - iir == MOXA_MUST_IIR_RTO || - iir == MOXA_MUST_IIR_LSR) - mxser_receive_chars(tty, port, - &status); - - } else { - status &= port->read_status_mask; - if (status & UART_LSR_DR) - mxser_receive_chars(tty, port, - &status); - } - msr = inb(port->ioaddr + UART_MSR); - if (msr & UART_MSR_ANY_DELTA) - mxser_check_modem_status(tty, port, msr); - - if (port->board->chip_flag) { - if (iir == 0x02 && (status & - UART_LSR_THRE)) - mxser_transmit_chars(tty, port); - } else { - if (status & UART_LSR_THRE) - mxser_transmit_chars(tty, port); - } - tty_kref_put(tty); - } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); - spin_unlock(&port->slock); - } - } - -irq_stop: - return handled; -} - -static const struct tty_operations mxser_ops = { - .open = mxser_open, - .close = mxser_close, - .write = mxser_write, - .put_char = mxser_put_char, - .flush_chars = mxser_flush_chars, - .write_room = mxser_write_room, - .chars_in_buffer = mxser_chars_in_buffer, - .flush_buffer = mxser_flush_buffer, - .ioctl = mxser_ioctl, - .throttle = mxser_throttle, - .unthrottle = mxser_unthrottle, - .set_termios = mxser_set_termios, - .stop = mxser_stop, - .start = mxser_start, - .hangup = mxser_hangup, - .break_ctl = mxser_rs_break, - .wait_until_sent = mxser_wait_until_sent, - .tiocmget = mxser_tiocmget, - .tiocmset = mxser_tiocmset, - .get_icount = mxser_get_icount, -}; - -struct tty_port_operations mxser_port_ops = { - .carrier_raised = mxser_carrier_raised, - .dtr_rts = mxser_dtr_rts, - .activate = mxser_activate, - .shutdown = mxser_shutdown_port, -}; - -/* - * The MOXA Smartio/Industio serial driver boot-time initialization code! - */ - -static void mxser_release_ISA_res(struct mxser_board *brd) -{ - free_irq(brd->irq, brd); - release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - release_region(brd->vector, 1); -} - -static int __devinit mxser_initbrd(struct mxser_board *brd, - struct pci_dev *pdev) -{ - struct mxser_port *info; - unsigned int i; - int retval; - - printk(KERN_INFO "mxser: max. baud rate = %d bps\n", - brd->ports[0].max_baud); - - for (i = 0; i < brd->info->nports; i++) { - info = &brd->ports[i]; - tty_port_init(&info->port); - info->port.ops = &mxser_port_ops; - info->board = brd; - info->stop_rx = 0; - info->ldisc_stop_rx = 0; - - /* Enhance mode enabled here */ - if (brd->chip_flag != MOXA_OTHER_UART) - mxser_enable_must_enchance_mode(info->ioaddr); - - info->port.flags = ASYNC_SHARE_IRQ; - info->type = brd->uart_type; - - process_txrx_fifo(info); - - info->custom_divisor = info->baud_base * 16; - info->port.close_delay = 5 * HZ / 10; - info->port.closing_wait = 30 * HZ; - info->normal_termios = mxvar_sdriver->init_termios; - memset(&info->mon_data, 0, sizeof(struct mxser_mon)); - info->err_shadow = 0; - spin_lock_init(&info->slock); - - /* before set INT ISR, disable all int */ - outb(inb(info->ioaddr + UART_IER) & 0xf0, - info->ioaddr + UART_IER); - } - - retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", - brd); - if (retval) - printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " - "conflict with another device.\n", - brd->info->name, brd->irq); - - return retval; -} - -static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) -{ - int id, i, bits; - unsigned short regs[16], irq; - unsigned char scratch, scratch2; - - brd->chip_flag = MOXA_OTHER_UART; - - id = mxser_read_register(cap, regs); - switch (id) { - case C168_ASIC_ID: - brd->info = &mxser_cards[0]; - break; - case C104_ASIC_ID: - brd->info = &mxser_cards[1]; - break; - case CI104J_ASIC_ID: - brd->info = &mxser_cards[2]; - break; - case C102_ASIC_ID: - brd->info = &mxser_cards[5]; - break; - case CI132_ASIC_ID: - brd->info = &mxser_cards[6]; - break; - case CI134_ASIC_ID: - brd->info = &mxser_cards[7]; - break; - default: - return 0; - } - - irq = 0; - /* some ISA cards have 2 ports, but we want to see them as 4-port (why?) - Flag-hack checks if configuration should be read as 2-port here. */ - if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - if (irq != (regs[9] & 0xFF00)) - goto err_irqconflict; - } else if (brd->info->nports == 4) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if (irq != regs[9]) - goto err_irqconflict; - } else if (brd->info->nports == 8) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if ((irq != regs[9]) || (irq != regs[10])) - goto err_irqconflict; - } - - if (!irq) { - printk(KERN_ERR "mxser: interrupt number unset\n"); - return -EIO; - } - brd->irq = ((int)(irq & 0xF000) >> 12); - for (i = 0; i < 8; i++) - brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8; - if ((regs[12] & 0x80) == 0) { - printk(KERN_ERR "mxser: invalid interrupt vector\n"); - return -EIO; - } - brd->vector = (int)regs[11]; /* interrupt vector */ - if (id == 1) - brd->vector_mask = 0x00FF; - else - brd->vector_mask = 0x000F; - for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { - if (regs[12] & bits) { - brd->ports[i].baud_base = 921600; - brd->ports[i].max_baud = 921600; - } else { - brd->ports[i].baud_base = 115200; - brd->ports[i].max_baud = 115200; - } - } - scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); - outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); - outb(0, cap + UART_EFR); /* EFR is the same as FCR */ - outb(scratch2, cap + UART_LCR); - outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); - scratch = inb(cap + UART_IIR); - - if (scratch & 0xC0) - brd->uart_type = PORT_16550A; - else - brd->uart_type = PORT_16450; - if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports, - "mxser(IO)")) { - printk(KERN_ERR "mxser: can't request ports I/O region: " - "0x%.8lx-0x%.8lx\n", - brd->ports[0].ioaddr, brd->ports[0].ioaddr + - 8 * brd->info->nports - 1); - return -EIO; - } - if (!request_region(brd->vector, 1, "mxser(vector)")) { - release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - printk(KERN_ERR "mxser: can't request interrupt vector region: " - "0x%.8lx-0x%.8lx\n", - brd->ports[0].ioaddr, brd->ports[0].ioaddr + - 8 * brd->info->nports - 1); - return -EIO; - } - return brd->info->nports; - -err_irqconflict: - printk(KERN_ERR "mxser: invalid interrupt number\n"); - return -EIO; -} - -static int __devinit mxser_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ -#ifdef CONFIG_PCI - struct mxser_board *brd; - unsigned int i, j; - unsigned long ioaddress; - int retval = -EINVAL; - - for (i = 0; i < MXSER_BOARDS; i++) - if (mxser_boards[i].info == NULL) - break; - - if (i >= MXSER_BOARDS) { - dev_err(&pdev->dev, "too many boards found (maximum %d), board " - "not configured\n", MXSER_BOARDS); - goto err; - } - - brd = &mxser_boards[i]; - brd->idx = i * MXSER_PORTS_PER_BOARD; - dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n", - mxser_cards[ent->driver_data].name, - pdev->bus->number, PCI_SLOT(pdev->devfn)); - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "PCI enable failed\n"); - goto err; - } - - /* io address */ - ioaddress = pci_resource_start(pdev, 2); - retval = pci_request_region(pdev, 2, "mxser(IO)"); - if (retval) - goto err_dis; - - brd->info = &mxser_cards[ent->driver_data]; - for (i = 0; i < brd->info->nports; i++) - brd->ports[i].ioaddr = ioaddress + 8 * i; - - /* vector */ - ioaddress = pci_resource_start(pdev, 3); - retval = pci_request_region(pdev, 3, "mxser(vector)"); - if (retval) - goto err_zero; - brd->vector = ioaddress; - - /* irq */ - brd->irq = pdev->irq; - - brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr); - brd->uart_type = PORT_16550A; - brd->vector_mask = 0; - - for (i = 0; i < brd->info->nports; i++) { - for (j = 0; j < UART_INFO_NUM; j++) { - if (Gpci_uart_info[j].type == brd->chip_flag) { - brd->ports[i].max_baud = - Gpci_uart_info[j].max_baud; - - /* exception....CP-102 */ - if (brd->info->flags & MXSER_HIGHBAUD) - brd->ports[i].max_baud = 921600; - break; - } - } - } - - if (brd->chip_flag == MOXA_MUST_MU860_HWID) { - for (i = 0; i < brd->info->nports; i++) { - if (i < 4) - brd->ports[i].opmode_ioaddr = ioaddress + 4; - else - brd->ports[i].opmode_ioaddr = ioaddress + 0x0c; - } - outb(0, ioaddress + 4); /* default set to RS232 mode */ - outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ - } - - for (i = 0; i < brd->info->nports; i++) { - brd->vector_mask |= (1 << i); - brd->ports[i].baud_base = 921600; - } - - /* mxser_initbrd will hook ISR. */ - retval = mxser_initbrd(brd, pdev); - if (retval) - goto err_rel3; - - for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev); - - pci_set_drvdata(pdev, brd); - - return 0; -err_rel3: - pci_release_region(pdev, 3); -err_zero: - brd->info = NULL; - pci_release_region(pdev, 2); -err_dis: - pci_disable_device(pdev); -err: - return retval; -#else - return -ENODEV; -#endif -} - -static void __devexit mxser_remove(struct pci_dev *pdev) -{ -#ifdef CONFIG_PCI - struct mxser_board *brd = pci_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < brd->info->nports; i++) - tty_unregister_device(mxvar_sdriver, brd->idx + i); - - free_irq(pdev->irq, brd); - pci_release_region(pdev, 2); - pci_release_region(pdev, 3); - pci_disable_device(pdev); - brd->info = NULL; -#endif -} - -static struct pci_driver mxser_driver = { - .name = "mxser", - .id_table = mxser_pcibrds, - .probe = mxser_probe, - .remove = __devexit_p(mxser_remove) -}; - -static int __init mxser_module_init(void) -{ - struct mxser_board *brd; - unsigned int b, i, m; - int retval; - - mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); - if (!mxvar_sdriver) - return -ENOMEM; - - printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", - MXSER_VERSION); - - /* Initialize the tty_driver structure */ - mxvar_sdriver->owner = THIS_MODULE; - mxvar_sdriver->magic = TTY_DRIVER_MAGIC; - mxvar_sdriver->name = "ttyMI"; - mxvar_sdriver->major = ttymajor; - mxvar_sdriver->minor_start = 0; - mxvar_sdriver->num = MXSER_PORTS + 1; - mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; - mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; - mxvar_sdriver->init_termios = tty_std_termios; - mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(mxvar_sdriver, &mxser_ops); - - retval = tty_register_driver(mxvar_sdriver); - if (retval) { - printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " - "tty driver !\n"); - goto err_put; - } - - /* Start finding ISA boards here */ - for (m = 0, b = 0; b < MXSER_BOARDS; b++) { - if (!ioaddr[b]) - continue; - - brd = &mxser_boards[m]; - retval = mxser_get_ISA_conf(ioaddr[b], brd); - if (retval <= 0) { - brd->info = NULL; - continue; - } - - printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n", - brd->info->name, ioaddr[b]); - - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(brd, NULL) < 0) { - brd->info = NULL; - continue; - } - - brd->idx = m * MXSER_PORTS_PER_BOARD; - for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, NULL); - - m++; - } - - retval = pci_register_driver(&mxser_driver); - if (retval) { - printk(KERN_ERR "mxser: can't register pci driver\n"); - if (!m) { - retval = -ENODEV; - goto err_unr; - } /* else: we have some ISA cards under control */ - } - - return 0; -err_unr: - tty_unregister_driver(mxvar_sdriver); -err_put: - put_tty_driver(mxvar_sdriver); - return retval; -} - -static void __exit mxser_module_exit(void) -{ - unsigned int i, j; - - pci_unregister_driver(&mxser_driver); - - for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ - if (mxser_boards[i].info != NULL) - for (j = 0; j < mxser_boards[i].info->nports; j++) - tty_unregister_device(mxvar_sdriver, - mxser_boards[i].idx + j); - tty_unregister_driver(mxvar_sdriver); - put_tty_driver(mxvar_sdriver); - - for (i = 0; i < MXSER_BOARDS; i++) - if (mxser_boards[i].info != NULL) - mxser_release_ISA_res(&mxser_boards[i]); -} - -module_init(mxser_module_init); -module_exit(mxser_module_exit); diff --git a/drivers/char/mxser.h b/drivers/char/mxser.h deleted file mode 100644 index 41878a69203d..000000000000 --- a/drivers/char/mxser.h +++ /dev/null @@ -1,150 +0,0 @@ -#ifndef _MXSER_H -#define _MXSER_H - -/* - * Semi-public control interfaces - */ - -/* - * MOXA ioctls - */ - -#define MOXA 0x400 -#define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_DIAGNOSE (MOXA + 50) -#define MOXA_CHKPORTENABLE (MOXA + 60) -#define MOXA_HighSpeedOn (MOXA + 61) -#define MOXA_GET_MAJOR (MOXA + 63) -#define MOXA_GETMSTATUS (MOXA + 65) -#define MOXA_SET_OP_MODE (MOXA + 66) -#define MOXA_GET_OP_MODE (MOXA + 67) - -#define RS232_MODE 0 -#define RS485_2WIRE_MODE 1 -#define RS422_MODE 2 -#define RS485_4WIRE_MODE 3 -#define OP_MODE_MASK 3 - -#define MOXA_SDS_RSTICOUNTER (MOXA + 69) -#define MOXA_ASPP_OQUEUE (MOXA + 70) -#define MOXA_ASPP_MON (MOXA + 73) -#define MOXA_ASPP_LSTATUS (MOXA + 74) -#define MOXA_ASPP_MON_EXT (MOXA + 75) -#define MOXA_SET_BAUD_METHOD (MOXA + 76) - -/* --------------------------------------------------- */ - -#define NPPI_NOTIFY_PARITY 0x01 -#define NPPI_NOTIFY_FRAMING 0x02 -#define NPPI_NOTIFY_HW_OVERRUN 0x04 -#define NPPI_NOTIFY_SW_OVERRUN 0x08 -#define NPPI_NOTIFY_BREAK 0x10 - -#define NPPI_NOTIFY_CTSHOLD 0x01 /* Tx hold by CTS low */ -#define NPPI_NOTIFY_DSRHOLD 0x02 /* Tx hold by DSR low */ -#define NPPI_NOTIFY_XOFFHOLD 0x08 /* Tx hold by Xoff received */ -#define NPPI_NOTIFY_XOFFXENT 0x10 /* Xoff Sent */ - -/* follow just for Moxa Must chip define. */ -/* */ -/* when LCR register (offset 0x03) write following value, */ -/* the Must chip will enter enchance mode. And write value */ -/* on EFR (offset 0x02) bit 6,7 to change bank. */ -#define MOXA_MUST_ENTER_ENCHANCE 0xBF - -/* when enhance mode enable, access on general bank register */ -#define MOXA_MUST_GDL_REGISTER 0x07 -#define MOXA_MUST_GDL_MASK 0x7F -#define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 - -#define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ -/* enchance register bank select and enchance mode setting register */ -/* when LCR register equal to 0xBF */ -#define MOXA_MUST_EFR_REGISTER 0x02 -/* enchance mode enable */ -#define MOXA_MUST_EFR_EFRB_ENABLE 0x10 -/* enchance reister bank set 0, 1, 2 */ -#define MOXA_MUST_EFR_BANK0 0x00 -#define MOXA_MUST_EFR_BANK1 0x40 -#define MOXA_MUST_EFR_BANK2 0x80 -#define MOXA_MUST_EFR_BANK3 0xC0 -#define MOXA_MUST_EFR_BANK_MASK 0xC0 - -/* set XON1 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XON1_REGISTER 0x04 - -/* set XON2 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XON2_REGISTER 0x05 - -/* set XOFF1 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XOFF1_REGISTER 0x06 - -/* set XOFF2 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XOFF2_REGISTER 0x07 - -#define MOXA_MUST_RBRTL_REGISTER 0x04 -#define MOXA_MUST_RBRTH_REGISTER 0x05 -#define MOXA_MUST_RBRTI_REGISTER 0x06 -#define MOXA_MUST_THRTL_REGISTER 0x07 -#define MOXA_MUST_ENUM_REGISTER 0x04 -#define MOXA_MUST_HWID_REGISTER 0x05 -#define MOXA_MUST_ECR_REGISTER 0x06 -#define MOXA_MUST_CSR_REGISTER 0x07 - -/* good data mode enable */ -#define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 -/* only good data put into RxFIFO */ -#define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 - -/* enable CTS interrupt */ -#define MOXA_MUST_IER_ECTSI 0x80 -/* enable RTS interrupt */ -#define MOXA_MUST_IER_ERTSI 0x40 -/* enable Xon/Xoff interrupt */ -#define MOXA_MUST_IER_XINT 0x20 -/* enable GDA interrupt */ -#define MOXA_MUST_IER_EGDAI 0x10 - -#define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) - -/* GDA interrupt pending */ -#define MOXA_MUST_IIR_GDA 0x1C -#define MOXA_MUST_IIR_RDA 0x04 -#define MOXA_MUST_IIR_RTO 0x0C -#define MOXA_MUST_IIR_LSR 0x06 - -/* recieved Xon/Xoff or specical interrupt pending */ -#define MOXA_MUST_IIR_XSC 0x10 - -/* RTS/CTS change state interrupt pending */ -#define MOXA_MUST_IIR_RTSCTS 0x20 -#define MOXA_MUST_IIR_MASK 0x3E - -#define MOXA_MUST_MCR_XON_FLAG 0x40 -#define MOXA_MUST_MCR_XON_ANY 0x80 -#define MOXA_MUST_MCR_TX_XON 0x08 - -/* software flow control on chip mask value */ -#define MOXA_MUST_EFR_SF_MASK 0x0F -/* send Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_TX1 0x08 -/* send Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_TX2 0x04 -/* send Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_TX12 0x0C -/* don't send Xon/Xoff */ -#define MOXA_MUST_EFR_SF_TX_NO 0x00 -/* Tx software flow control mask */ -#define MOXA_MUST_EFR_SF_TX_MASK 0x0C -/* don't receive Xon/Xoff */ -#define MOXA_MUST_EFR_SF_RX_NO 0x00 -/* receive Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_RX1 0x02 -/* receive Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_RX2 0x01 -/* receive Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_RX12 0x03 -/* Rx software flow control mask */ -#define MOXA_MUST_EFR_SF_RX_MASK 0x03 - -#endif diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c deleted file mode 100644 index 513ba12064ea..000000000000 --- a/drivers/char/nozomi.c +++ /dev/null @@ -1,1993 +0,0 @@ -/* - * nozomi.c -- HSDPA driver Broadband Wireless Data Card - Globe Trotter - * - * Written by: Ulf Jakobsson, - * Jan Ã…kerfeldt, - * Stefan Thomasson, - * - * Maintained by: Paul Hardwick (p.hardwick@option.com) - * - * Patches: - * Locking code changes for Vodafone by Sphere Systems Ltd, - * Andrew Bird (ajb@spheresystems.co.uk ) - * & Phil Sanderson - * - * Source has been ported from an implementation made by Filip Aben @ Option - * - * -------------------------------------------------------------------------- - * - * Copyright (c) 2005,2006 Option Wireless Sweden AB - * Copyright (c) 2006 Sphere Systems Ltd - * Copyright (c) 2006 Option Wireless n/v - * All rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * -------------------------------------------------------------------------- - */ - -/* Enable this to have a lot of debug printouts */ -#define DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \ - __DATE__ " " __TIME__ ")" - -/* Macros definitions */ - -/* Default debug printout level */ -#define NOZOMI_DEBUG_LEVEL 0x00 - -#define P_BUF_SIZE 128 -#define NFO(_err_flag_, args...) \ -do { \ - char tmp[P_BUF_SIZE]; \ - snprintf(tmp, sizeof(tmp), ##args); \ - printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \ - __func__, tmp); \ -} while (0) - -#define DBG1(args...) D_(0x01, ##args) -#define DBG2(args...) D_(0x02, ##args) -#define DBG3(args...) D_(0x04, ##args) -#define DBG4(args...) D_(0x08, ##args) -#define DBG5(args...) D_(0x10, ##args) -#define DBG6(args...) D_(0x20, ##args) -#define DBG7(args...) D_(0x40, ##args) -#define DBG8(args...) D_(0x80, ##args) - -#ifdef DEBUG -/* Do we need this settable at runtime? */ -static int debug = NOZOMI_DEBUG_LEVEL; - -#define D(lvl, args...) do \ - {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \ - while (0) -#define D_(lvl, args...) D(lvl, ##args) - -/* These printouts are always printed */ - -#else -static int debug; -#define D_(lvl, args...) -#endif - -/* TODO: rewrite to optimize macros... */ - -#define TMP_BUF_MAX 256 - -#define DUMP(buf__,len__) \ - do { \ - char tbuf[TMP_BUF_MAX] = {0};\ - if (len__ > 1) {\ - snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\ - if (tbuf[len__-2] == '\r') {\ - tbuf[len__-2] = 'r';\ - } \ - DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\ - } else {\ - DBG1("SENDING: '%s' (%d)", tbuf, len__);\ - } \ -} while (0) - -/* Defines */ -#define NOZOMI_NAME "nozomi" -#define NOZOMI_NAME_TTY "nozomi_tty" -#define DRIVER_DESC "Nozomi driver" - -#define NTTY_TTY_MAXMINORS 256 -#define NTTY_FIFO_BUFFER_SIZE 8192 - -/* Must be power of 2 */ -#define FIFO_BUFFER_SIZE_UL 8192 - -/* Size of tmp send buffer to card */ -#define SEND_BUF_MAX 1024 -#define RECEIVE_BUF_MAX 4 - - -#define R_IIR 0x0000 /* Interrupt Identity Register */ -#define R_FCR 0x0000 /* Flow Control Register */ -#define R_IER 0x0004 /* Interrupt Enable Register */ - -#define CONFIG_MAGIC 0xEFEFFEFE -#define TOGGLE_VALID 0x0000 - -/* Definition of interrupt tokens */ -#define MDM_DL1 0x0001 -#define MDM_UL1 0x0002 -#define MDM_DL2 0x0004 -#define MDM_UL2 0x0008 -#define DIAG_DL1 0x0010 -#define DIAG_DL2 0x0020 -#define DIAG_UL 0x0040 -#define APP1_DL 0x0080 -#define APP1_UL 0x0100 -#define APP2_DL 0x0200 -#define APP2_UL 0x0400 -#define CTRL_DL 0x0800 -#define CTRL_UL 0x1000 -#define RESET 0x8000 - -#define MDM_DL (MDM_DL1 | MDM_DL2) -#define MDM_UL (MDM_UL1 | MDM_UL2) -#define DIAG_DL (DIAG_DL1 | DIAG_DL2) - -/* modem signal definition */ -#define CTRL_DSR 0x0001 -#define CTRL_DCD 0x0002 -#define CTRL_RI 0x0004 -#define CTRL_CTS 0x0008 - -#define CTRL_DTR 0x0001 -#define CTRL_RTS 0x0002 - -#define MAX_PORT 4 -#define NOZOMI_MAX_PORTS 5 -#define NOZOMI_MAX_CARDS (NTTY_TTY_MAXMINORS / MAX_PORT) - -/* Type definitions */ - -/* - * There are two types of nozomi cards, - * one with 2048 memory and with 8192 memory - */ -enum card_type { - F32_2 = 2048, /* 512 bytes downlink + uplink * 2 -> 2048 */ - F32_8 = 8192, /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */ -}; - -/* Initialization states a card can be in */ -enum card_state { - NOZOMI_STATE_UKNOWN = 0, - NOZOMI_STATE_ENABLED = 1, /* pci device enabled */ - NOZOMI_STATE_ALLOCATED = 2, /* config setup done */ - NOZOMI_STATE_READY = 3, /* flowcontrols received */ -}; - -/* Two different toggle channels exist */ -enum channel_type { - CH_A = 0, - CH_B = 1, -}; - -/* Port definition for the card regarding flow control */ -enum ctrl_port_type { - CTRL_CMD = 0, - CTRL_MDM = 1, - CTRL_DIAG = 2, - CTRL_APP1 = 3, - CTRL_APP2 = 4, - CTRL_ERROR = -1, -}; - -/* Ports that the nozomi has */ -enum port_type { - PORT_MDM = 0, - PORT_DIAG = 1, - PORT_APP1 = 2, - PORT_APP2 = 3, - PORT_CTRL = 4, - PORT_ERROR = -1, -}; - -#ifdef __BIG_ENDIAN -/* Big endian */ - -struct toggles { - unsigned int enabled:5; /* - * Toggle fields are valid if enabled is 0, - * else A-channels must always be used. - */ - unsigned int diag_dl:1; - unsigned int mdm_dl:1; - unsigned int mdm_ul:1; -} __attribute__ ((packed)); - -/* Configuration table to read at startup of card */ -/* Is for now only needed during initialization phase */ -struct config_table { - u32 signature; - u16 product_information; - u16 version; - u8 pad3[3]; - struct toggles toggle; - u8 pad1[4]; - u16 dl_mdm_len1; /* - * If this is 64, it can hold - * 60 bytes + 4 that is length field - */ - u16 dl_start; - - u16 dl_diag_len1; - u16 dl_mdm_len2; /* - * If this is 64, it can hold - * 60 bytes + 4 that is length field - */ - u16 dl_app1_len; - - u16 dl_diag_len2; - u16 dl_ctrl_len; - u16 dl_app2_len; - u8 pad2[16]; - u16 ul_mdm_len1; - u16 ul_start; - u16 ul_diag_len; - u16 ul_mdm_len2; - u16 ul_app1_len; - u16 ul_app2_len; - u16 ul_ctrl_len; -} __attribute__ ((packed)); - -/* This stores all control downlink flags */ -struct ctrl_dl { - u8 port; - unsigned int reserved:4; - unsigned int CTS:1; - unsigned int RI:1; - unsigned int DCD:1; - unsigned int DSR:1; -} __attribute__ ((packed)); - -/* This stores all control uplink flags */ -struct ctrl_ul { - u8 port; - unsigned int reserved:6; - unsigned int RTS:1; - unsigned int DTR:1; -} __attribute__ ((packed)); - -#else -/* Little endian */ - -/* This represents the toggle information */ -struct toggles { - unsigned int mdm_ul:1; - unsigned int mdm_dl:1; - unsigned int diag_dl:1; - unsigned int enabled:5; /* - * Toggle fields are valid if enabled is 0, - * else A-channels must always be used. - */ -} __attribute__ ((packed)); - -/* Configuration table to read at startup of card */ -struct config_table { - u32 signature; - u16 version; - u16 product_information; - struct toggles toggle; - u8 pad1[7]; - u16 dl_start; - u16 dl_mdm_len1; /* - * If this is 64, it can hold - * 60 bytes + 4 that is length field - */ - u16 dl_mdm_len2; - u16 dl_diag_len1; - u16 dl_diag_len2; - u16 dl_app1_len; - u16 dl_app2_len; - u16 dl_ctrl_len; - u8 pad2[16]; - u16 ul_start; - u16 ul_mdm_len2; - u16 ul_mdm_len1; - u16 ul_diag_len; - u16 ul_app1_len; - u16 ul_app2_len; - u16 ul_ctrl_len; -} __attribute__ ((packed)); - -/* This stores all control downlink flags */ -struct ctrl_dl { - unsigned int DSR:1; - unsigned int DCD:1; - unsigned int RI:1; - unsigned int CTS:1; - unsigned int reserverd:4; - u8 port; -} __attribute__ ((packed)); - -/* This stores all control uplink flags */ -struct ctrl_ul { - unsigned int DTR:1; - unsigned int RTS:1; - unsigned int reserved:6; - u8 port; -} __attribute__ ((packed)); -#endif - -/* This holds all information that is needed regarding a port */ -struct port { - struct tty_port port; - u8 update_flow_control; - struct ctrl_ul ctrl_ul; - struct ctrl_dl ctrl_dl; - struct kfifo fifo_ul; - void __iomem *dl_addr[2]; - u32 dl_size[2]; - u8 toggle_dl; - void __iomem *ul_addr[2]; - u32 ul_size[2]; - u8 toggle_ul; - u16 token_dl; - - /* mutex to ensure one access patch to this port */ - struct mutex tty_sem; - wait_queue_head_t tty_wait; - struct async_icount tty_icount; - - struct nozomi *dc; -}; - -/* Private data one for each card in the system */ -struct nozomi { - void __iomem *base_addr; - unsigned long flip; - - /* Pointers to registers */ - void __iomem *reg_iir; - void __iomem *reg_fcr; - void __iomem *reg_ier; - - u16 last_ier; - enum card_type card_type; - struct config_table config_table; /* Configuration table */ - struct pci_dev *pdev; - struct port port[NOZOMI_MAX_PORTS]; - u8 *send_buf; - - spinlock_t spin_mutex; /* secures access to registers and tty */ - - unsigned int index_start; - enum card_state state; - u32 open_ttys; -}; - -/* This is a data packet that is read or written to/from card */ -struct buffer { - u32 size; /* size is the length of the data buffer */ - u8 *data; -} __attribute__ ((packed)); - -/* Global variables */ -static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = { - {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ - {}, -}; - -MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl); - -static struct nozomi *ndevs[NOZOMI_MAX_CARDS]; -static struct tty_driver *ntty_driver; - -static const struct tty_port_operations noz_tty_port_ops; - -/* - * find card by tty_index - */ -static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty) -{ - return tty ? ndevs[tty->index / MAX_PORT] : NULL; -} - -static inline struct port *get_port_by_tty(const struct tty_struct *tty) -{ - struct nozomi *ndev = get_dc_by_tty(tty); - return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL; -} - -/* - * TODO: - * -Optimize - * -Rewrite cleaner - */ - -static void read_mem32(u32 *buf, const void __iomem *mem_addr_start, - u32 size_bytes) -{ - u32 i = 0; - const u32 __iomem *ptr = mem_addr_start; - u16 *buf16; - - if (unlikely(!ptr || !buf)) - goto out; - - /* shortcut for extremely often used cases */ - switch (size_bytes) { - case 2: /* 2 bytes */ - buf16 = (u16 *) buf; - *buf16 = __le16_to_cpu(readw(ptr)); - goto out; - break; - case 4: /* 4 bytes */ - *(buf) = __le32_to_cpu(readl(ptr)); - goto out; - break; - } - - while (i < size_bytes) { - if (size_bytes - i == 2) { - /* Handle 2 bytes in the end */ - buf16 = (u16 *) buf; - *(buf16) = __le16_to_cpu(readw(ptr)); - i += 2; - } else { - /* Read 4 bytes */ - *(buf) = __le32_to_cpu(readl(ptr)); - i += 4; - } - buf++; - ptr++; - } -out: - return; -} - -/* - * TODO: - * -Optimize - * -Rewrite cleaner - */ -static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf, - u32 size_bytes) -{ - u32 i = 0; - u32 __iomem *ptr = mem_addr_start; - const u16 *buf16; - - if (unlikely(!ptr || !buf)) - return 0; - - /* shortcut for extremely often used cases */ - switch (size_bytes) { - case 2: /* 2 bytes */ - buf16 = (const u16 *)buf; - writew(__cpu_to_le16(*buf16), ptr); - return 2; - break; - case 1: /* - * also needs to write 4 bytes in this case - * so falling through.. - */ - case 4: /* 4 bytes */ - writel(__cpu_to_le32(*buf), ptr); - return 4; - break; - } - - while (i < size_bytes) { - if (size_bytes - i == 2) { - /* 2 bytes */ - buf16 = (const u16 *)buf; - writew(__cpu_to_le16(*buf16), ptr); - i += 2; - } else { - /* 4 bytes */ - writel(__cpu_to_le32(*buf), ptr); - i += 4; - } - buf++; - ptr++; - } - return i; -} - -/* Setup pointers to different channels and also setup buffer sizes. */ -static void setup_memory(struct nozomi *dc) -{ - void __iomem *offset = dc->base_addr + dc->config_table.dl_start; - /* The length reported is including the length field of 4 bytes, - * hence subtract with 4. - */ - const u16 buff_offset = 4; - - /* Modem port dl configuration */ - dc->port[PORT_MDM].dl_addr[CH_A] = offset; - dc->port[PORT_MDM].dl_addr[CH_B] = - (offset += dc->config_table.dl_mdm_len1); - dc->port[PORT_MDM].dl_size[CH_A] = - dc->config_table.dl_mdm_len1 - buff_offset; - dc->port[PORT_MDM].dl_size[CH_B] = - dc->config_table.dl_mdm_len2 - buff_offset; - - /* Diag port dl configuration */ - dc->port[PORT_DIAG].dl_addr[CH_A] = - (offset += dc->config_table.dl_mdm_len2); - dc->port[PORT_DIAG].dl_size[CH_A] = - dc->config_table.dl_diag_len1 - buff_offset; - dc->port[PORT_DIAG].dl_addr[CH_B] = - (offset += dc->config_table.dl_diag_len1); - dc->port[PORT_DIAG].dl_size[CH_B] = - dc->config_table.dl_diag_len2 - buff_offset; - - /* App1 port dl configuration */ - dc->port[PORT_APP1].dl_addr[CH_A] = - (offset += dc->config_table.dl_diag_len2); - dc->port[PORT_APP1].dl_size[CH_A] = - dc->config_table.dl_app1_len - buff_offset; - - /* App2 port dl configuration */ - dc->port[PORT_APP2].dl_addr[CH_A] = - (offset += dc->config_table.dl_app1_len); - dc->port[PORT_APP2].dl_size[CH_A] = - dc->config_table.dl_app2_len - buff_offset; - - /* Ctrl dl configuration */ - dc->port[PORT_CTRL].dl_addr[CH_A] = - (offset += dc->config_table.dl_app2_len); - dc->port[PORT_CTRL].dl_size[CH_A] = - dc->config_table.dl_ctrl_len - buff_offset; - - offset = dc->base_addr + dc->config_table.ul_start; - - /* Modem Port ul configuration */ - dc->port[PORT_MDM].ul_addr[CH_A] = offset; - dc->port[PORT_MDM].ul_size[CH_A] = - dc->config_table.ul_mdm_len1 - buff_offset; - dc->port[PORT_MDM].ul_addr[CH_B] = - (offset += dc->config_table.ul_mdm_len1); - dc->port[PORT_MDM].ul_size[CH_B] = - dc->config_table.ul_mdm_len2 - buff_offset; - - /* Diag port ul configuration */ - dc->port[PORT_DIAG].ul_addr[CH_A] = - (offset += dc->config_table.ul_mdm_len2); - dc->port[PORT_DIAG].ul_size[CH_A] = - dc->config_table.ul_diag_len - buff_offset; - - /* App1 port ul configuration */ - dc->port[PORT_APP1].ul_addr[CH_A] = - (offset += dc->config_table.ul_diag_len); - dc->port[PORT_APP1].ul_size[CH_A] = - dc->config_table.ul_app1_len - buff_offset; - - /* App2 port ul configuration */ - dc->port[PORT_APP2].ul_addr[CH_A] = - (offset += dc->config_table.ul_app1_len); - dc->port[PORT_APP2].ul_size[CH_A] = - dc->config_table.ul_app2_len - buff_offset; - - /* Ctrl ul configuration */ - dc->port[PORT_CTRL].ul_addr[CH_A] = - (offset += dc->config_table.ul_app2_len); - dc->port[PORT_CTRL].ul_size[CH_A] = - dc->config_table.ul_ctrl_len - buff_offset; -} - -/* Dump config table under initalization phase */ -#ifdef DEBUG -static void dump_table(const struct nozomi *dc) -{ - DBG3("signature: 0x%08X", dc->config_table.signature); - DBG3("version: 0x%04X", dc->config_table.version); - DBG3("product_information: 0x%04X", \ - dc->config_table.product_information); - DBG3("toggle enabled: %d", dc->config_table.toggle.enabled); - DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul); - DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl); - DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl); - - DBG3("dl_start: 0x%04X", dc->config_table.dl_start); - DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1, - dc->config_table.dl_mdm_len1); - DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2, - dc->config_table.dl_mdm_len2); - DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1, - dc->config_table.dl_diag_len1); - DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2, - dc->config_table.dl_diag_len2); - DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len, - dc->config_table.dl_app1_len); - DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len, - dc->config_table.dl_app2_len); - DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len, - dc->config_table.dl_ctrl_len); - DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start, - dc->config_table.ul_start); - DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1, - dc->config_table.ul_mdm_len1); - DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2, - dc->config_table.ul_mdm_len2); - DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len, - dc->config_table.ul_diag_len); - DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len, - dc->config_table.ul_app1_len); - DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len, - dc->config_table.ul_app2_len); - DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len, - dc->config_table.ul_ctrl_len); -} -#else -static inline void dump_table(const struct nozomi *dc) { } -#endif - -/* - * Read configuration table from card under intalization phase - * Returns 1 if ok, else 0 - */ -static int nozomi_read_config_table(struct nozomi *dc) -{ - read_mem32((u32 *) &dc->config_table, dc->base_addr + 0, - sizeof(struct config_table)); - - if (dc->config_table.signature != CONFIG_MAGIC) { - dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n", - dc->config_table.signature, CONFIG_MAGIC); - return 0; - } - - if ((dc->config_table.version == 0) - || (dc->config_table.toggle.enabled == TOGGLE_VALID)) { - int i; - DBG1("Second phase, configuring card"); - - setup_memory(dc); - - dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul; - dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl; - dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl; - DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d", - dc->port[PORT_MDM].toggle_ul, - dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl); - - dump_table(dc); - - for (i = PORT_MDM; i < MAX_PORT; i++) { - memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl)); - memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul)); - } - - /* Enable control channel */ - dc->last_ier = dc->last_ier | CTRL_DL; - writew(dc->last_ier, dc->reg_ier); - - dc->state = NOZOMI_STATE_ALLOCATED; - dev_info(&dc->pdev->dev, "Initialization OK!\n"); - return 1; - } - - if ((dc->config_table.version > 0) - && (dc->config_table.toggle.enabled != TOGGLE_VALID)) { - u32 offset = 0; - DBG1("First phase: pushing upload buffers, clearing download"); - - dev_info(&dc->pdev->dev, "Version of card: %d\n", - dc->config_table.version); - - /* Here we should disable all I/O over F32. */ - setup_memory(dc); - - /* - * We should send ALL channel pair tokens back along - * with reset token - */ - - /* push upload modem buffers */ - write_mem32(dc->port[PORT_MDM].ul_addr[CH_A], - (u32 *) &offset, 4); - write_mem32(dc->port[PORT_MDM].ul_addr[CH_B], - (u32 *) &offset, 4); - - writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr); - - DBG1("First phase done"); - } - - return 1; -} - -/* Enable uplink interrupts */ -static void enable_transmit_ul(enum port_type port, struct nozomi *dc) -{ - static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL}; - - if (port < NOZOMI_MAX_PORTS) { - dc->last_ier |= mask[port]; - writew(dc->last_ier, dc->reg_ier); - } else { - dev_err(&dc->pdev->dev, "Called with wrong port?\n"); - } -} - -/* Disable uplink interrupts */ -static void disable_transmit_ul(enum port_type port, struct nozomi *dc) -{ - static const u16 mask[] = - {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL}; - - if (port < NOZOMI_MAX_PORTS) { - dc->last_ier &= mask[port]; - writew(dc->last_ier, dc->reg_ier); - } else { - dev_err(&dc->pdev->dev, "Called with wrong port?\n"); - } -} - -/* Enable downlink interrupts */ -static void enable_transmit_dl(enum port_type port, struct nozomi *dc) -{ - static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL}; - - if (port < NOZOMI_MAX_PORTS) { - dc->last_ier |= mask[port]; - writew(dc->last_ier, dc->reg_ier); - } else { - dev_err(&dc->pdev->dev, "Called with wrong port?\n"); - } -} - -/* Disable downlink interrupts */ -static void disable_transmit_dl(enum port_type port, struct nozomi *dc) -{ - static const u16 mask[] = - {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL}; - - if (port < NOZOMI_MAX_PORTS) { - dc->last_ier &= mask[port]; - writew(dc->last_ier, dc->reg_ier); - } else { - dev_err(&dc->pdev->dev, "Called with wrong port?\n"); - } -} - -/* - * Return 1 - send buffer to card and ack. - * Return 0 - don't ack, don't send buffer to card. - */ -static int send_data(enum port_type index, struct nozomi *dc) -{ - u32 size = 0; - struct port *port = &dc->port[index]; - const u8 toggle = port->toggle_ul; - void __iomem *addr = port->ul_addr[toggle]; - const u32 ul_size = port->ul_size[toggle]; - struct tty_struct *tty = tty_port_tty_get(&port->port); - - /* Get data from tty and place in buf for now */ - size = kfifo_out(&port->fifo_ul, dc->send_buf, - ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX); - - if (size == 0) { - DBG4("No more data to send, disable link:"); - tty_kref_put(tty); - return 0; - } - - /* DUMP(buf, size); */ - - /* Write length + data */ - write_mem32(addr, (u32 *) &size, 4); - write_mem32(addr + 4, (u32 *) dc->send_buf, size); - - if (tty) - tty_wakeup(tty); - - tty_kref_put(tty); - return 1; -} - -/* If all data has been read, return 1, else 0 */ -static int receive_data(enum port_type index, struct nozomi *dc) -{ - u8 buf[RECEIVE_BUF_MAX] = { 0 }; - int size; - u32 offset = 4; - struct port *port = &dc->port[index]; - void __iomem *addr = port->dl_addr[port->toggle_dl]; - struct tty_struct *tty = tty_port_tty_get(&port->port); - int i, ret; - - if (unlikely(!tty)) { - DBG1("tty not open for port: %d?", index); - return 1; - } - - read_mem32((u32 *) &size, addr, 4); - /* DBG1( "%d bytes port: %d", size, index); */ - - if (test_bit(TTY_THROTTLED, &tty->flags)) { - DBG1("No room in tty, don't read data, don't ack interrupt, " - "disable interrupt"); - - /* disable interrupt in downlink... */ - disable_transmit_dl(index, dc); - ret = 0; - goto put; - } - - if (unlikely(size == 0)) { - dev_err(&dc->pdev->dev, "size == 0?\n"); - ret = 1; - goto put; - } - - while (size > 0) { - read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX); - - if (size == 1) { - tty_insert_flip_char(tty, buf[0], TTY_NORMAL); - size = 0; - } else if (size < RECEIVE_BUF_MAX) { - size -= tty_insert_flip_string(tty, (char *) buf, size); - } else { - i = tty_insert_flip_string(tty, \ - (char *) buf, RECEIVE_BUF_MAX); - size -= i; - offset += i; - } - } - - set_bit(index, &dc->flip); - ret = 1; -put: - tty_kref_put(tty); - return ret; -} - -/* Debug for interrupts */ -#ifdef DEBUG -static char *interrupt2str(u16 interrupt) -{ - static char buf[TMP_BUF_MAX]; - char *p = buf; - - interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL; - interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "MDM_DL2 ") : NULL; - - interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "MDM_UL1 ") : NULL; - interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "MDM_UL2 ") : NULL; - - interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "DIAG_DL1 ") : NULL; - interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "DIAG_DL2 ") : NULL; - - interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "DIAG_UL ") : NULL; - - interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "APP1_DL ") : NULL; - interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "APP2_DL ") : NULL; - - interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "APP1_UL ") : NULL; - interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "APP2_UL ") : NULL; - - interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "CTRL_DL ") : NULL; - interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "CTRL_UL ") : NULL; - - interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf), - "RESET ") : NULL; - - return buf; -} -#endif - -/* - * Receive flow control - * Return 1 - If ok, else 0 - */ -static int receive_flow_control(struct nozomi *dc) -{ - enum port_type port = PORT_MDM; - struct ctrl_dl ctrl_dl; - struct ctrl_dl old_ctrl; - u16 enable_ier = 0; - - read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2); - - switch (ctrl_dl.port) { - case CTRL_CMD: - DBG1("The Base Band sends this value as a response to a " - "request for IMSI detach sent over the control " - "channel uplink (see section 7.6.1)."); - break; - case CTRL_MDM: - port = PORT_MDM; - enable_ier = MDM_DL; - break; - case CTRL_DIAG: - port = PORT_DIAG; - enable_ier = DIAG_DL; - break; - case CTRL_APP1: - port = PORT_APP1; - enable_ier = APP1_DL; - break; - case CTRL_APP2: - port = PORT_APP2; - enable_ier = APP2_DL; - if (dc->state == NOZOMI_STATE_ALLOCATED) { - /* - * After card initialization the flow control - * received for APP2 is always the last - */ - dc->state = NOZOMI_STATE_READY; - dev_info(&dc->pdev->dev, "Device READY!\n"); - } - break; - default: - dev_err(&dc->pdev->dev, - "ERROR: flow control received for non-existing port\n"); - return 0; - }; - - DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl), - *((u16 *)&ctrl_dl)); - - old_ctrl = dc->port[port].ctrl_dl; - dc->port[port].ctrl_dl = ctrl_dl; - - if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) { - DBG1("Disable interrupt (0x%04X) on port: %d", - enable_ier, port); - disable_transmit_ul(port, dc); - - } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) { - - if (kfifo_len(&dc->port[port].fifo_ul)) { - DBG1("Enable interrupt (0x%04X) on port: %d", - enable_ier, port); - DBG1("Data in buffer [%d], enable transmit! ", - kfifo_len(&dc->port[port].fifo_ul)); - enable_transmit_ul(port, dc); - } else { - DBG1("No data in buffer..."); - } - } - - if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) { - DBG1(" No change in mctrl"); - return 1; - } - /* Update statistics */ - if (old_ctrl.CTS != ctrl_dl.CTS) - dc->port[port].tty_icount.cts++; - if (old_ctrl.DSR != ctrl_dl.DSR) - dc->port[port].tty_icount.dsr++; - if (old_ctrl.RI != ctrl_dl.RI) - dc->port[port].tty_icount.rng++; - if (old_ctrl.DCD != ctrl_dl.DCD) - dc->port[port].tty_icount.dcd++; - - wake_up_interruptible(&dc->port[port].tty_wait); - - DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)", - port, - dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts, - dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr); - - return 1; -} - -static enum ctrl_port_type port2ctrl(enum port_type port, - const struct nozomi *dc) -{ - switch (port) { - case PORT_MDM: - return CTRL_MDM; - case PORT_DIAG: - return CTRL_DIAG; - case PORT_APP1: - return CTRL_APP1; - case PORT_APP2: - return CTRL_APP2; - default: - dev_err(&dc->pdev->dev, - "ERROR: send flow control " \ - "received for non-existing port\n"); - }; - return CTRL_ERROR; -} - -/* - * Send flow control, can only update one channel at a time - * Return 0 - If we have updated all flow control - * Return 1 - If we need to update more flow control, ack current enable more - */ -static int send_flow_control(struct nozomi *dc) -{ - u32 i, more_flow_control_to_be_updated = 0; - u16 *ctrl; - - for (i = PORT_MDM; i < MAX_PORT; i++) { - if (dc->port[i].update_flow_control) { - if (more_flow_control_to_be_updated) { - /* We have more flow control to be updated */ - return 1; - } - dc->port[i].ctrl_ul.port = port2ctrl(i, dc); - ctrl = (u16 *)&dc->port[i].ctrl_ul; - write_mem32(dc->port[PORT_CTRL].ul_addr[0], \ - (u32 *) ctrl, 2); - dc->port[i].update_flow_control = 0; - more_flow_control_to_be_updated = 1; - } - } - return 0; -} - -/* - * Handle downlink data, ports that are handled are modem and diagnostics - * Return 1 - ok - * Return 0 - toggle fields are out of sync - */ -static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle, - u16 read_iir, u16 mask1, u16 mask2) -{ - if (*toggle == 0 && read_iir & mask1) { - if (receive_data(port, dc)) { - writew(mask1, dc->reg_fcr); - *toggle = !(*toggle); - } - - if (read_iir & mask2) { - if (receive_data(port, dc)) { - writew(mask2, dc->reg_fcr); - *toggle = !(*toggle); - } - } - } else if (*toggle == 1 && read_iir & mask2) { - if (receive_data(port, dc)) { - writew(mask2, dc->reg_fcr); - *toggle = !(*toggle); - } - - if (read_iir & mask1) { - if (receive_data(port, dc)) { - writew(mask1, dc->reg_fcr); - *toggle = !(*toggle); - } - } - } else { - dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n", - *toggle); - return 0; - } - return 1; -} - -/* - * Handle uplink data, this is currently for the modem port - * Return 1 - ok - * Return 0 - toggle field are out of sync - */ -static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir) -{ - u8 *toggle = &(dc->port[port].toggle_ul); - - if (*toggle == 0 && read_iir & MDM_UL1) { - dc->last_ier &= ~MDM_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(port, dc)) { - writew(MDM_UL1, dc->reg_fcr); - dc->last_ier = dc->last_ier | MDM_UL; - writew(dc->last_ier, dc->reg_ier); - *toggle = !*toggle; - } - - if (read_iir & MDM_UL2) { - dc->last_ier &= ~MDM_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(port, dc)) { - writew(MDM_UL2, dc->reg_fcr); - dc->last_ier = dc->last_ier | MDM_UL; - writew(dc->last_ier, dc->reg_ier); - *toggle = !*toggle; - } - } - - } else if (*toggle == 1 && read_iir & MDM_UL2) { - dc->last_ier &= ~MDM_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(port, dc)) { - writew(MDM_UL2, dc->reg_fcr); - dc->last_ier = dc->last_ier | MDM_UL; - writew(dc->last_ier, dc->reg_ier); - *toggle = !*toggle; - } - - if (read_iir & MDM_UL1) { - dc->last_ier &= ~MDM_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(port, dc)) { - writew(MDM_UL1, dc->reg_fcr); - dc->last_ier = dc->last_ier | MDM_UL; - writew(dc->last_ier, dc->reg_ier); - *toggle = !*toggle; - } - } - } else { - writew(read_iir & MDM_UL, dc->reg_fcr); - dev_err(&dc->pdev->dev, "port out of sync!\n"); - return 0; - } - return 1; -} - -static irqreturn_t interrupt_handler(int irq, void *dev_id) -{ - struct nozomi *dc = dev_id; - unsigned int a; - u16 read_iir; - - if (!dc) - return IRQ_NONE; - - spin_lock(&dc->spin_mutex); - read_iir = readw(dc->reg_iir); - - /* Card removed */ - if (read_iir == (u16)-1) - goto none; - /* - * Just handle interrupt enabled in IER - * (by masking with dc->last_ier) - */ - read_iir &= dc->last_ier; - - if (read_iir == 0) - goto none; - - - DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir, - dc->last_ier); - - if (read_iir & RESET) { - if (unlikely(!nozomi_read_config_table(dc))) { - dc->last_ier = 0x0; - writew(dc->last_ier, dc->reg_ier); - dev_err(&dc->pdev->dev, "Could not read status from " - "card, we should disable interface\n"); - } else { - writew(RESET, dc->reg_fcr); - } - /* No more useful info if this was the reset interrupt. */ - goto exit_handler; - } - if (read_iir & CTRL_UL) { - DBG1("CTRL_UL"); - dc->last_ier &= ~CTRL_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_flow_control(dc)) { - writew(CTRL_UL, dc->reg_fcr); - dc->last_ier = dc->last_ier | CTRL_UL; - writew(dc->last_ier, dc->reg_ier); - } - } - if (read_iir & CTRL_DL) { - receive_flow_control(dc); - writew(CTRL_DL, dc->reg_fcr); - } - if (read_iir & MDM_DL) { - if (!handle_data_dl(dc, PORT_MDM, - &(dc->port[PORT_MDM].toggle_dl), read_iir, - MDM_DL1, MDM_DL2)) { - dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n"); - goto exit_handler; - } - } - if (read_iir & MDM_UL) { - if (!handle_data_ul(dc, PORT_MDM, read_iir)) { - dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n"); - goto exit_handler; - } - } - if (read_iir & DIAG_DL) { - if (!handle_data_dl(dc, PORT_DIAG, - &(dc->port[PORT_DIAG].toggle_dl), read_iir, - DIAG_DL1, DIAG_DL2)) { - dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n"); - goto exit_handler; - } - } - if (read_iir & DIAG_UL) { - dc->last_ier &= ~DIAG_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(PORT_DIAG, dc)) { - writew(DIAG_UL, dc->reg_fcr); - dc->last_ier = dc->last_ier | DIAG_UL; - writew(dc->last_ier, dc->reg_ier); - } - } - if (read_iir & APP1_DL) { - if (receive_data(PORT_APP1, dc)) - writew(APP1_DL, dc->reg_fcr); - } - if (read_iir & APP1_UL) { - dc->last_ier &= ~APP1_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(PORT_APP1, dc)) { - writew(APP1_UL, dc->reg_fcr); - dc->last_ier = dc->last_ier | APP1_UL; - writew(dc->last_ier, dc->reg_ier); - } - } - if (read_iir & APP2_DL) { - if (receive_data(PORT_APP2, dc)) - writew(APP2_DL, dc->reg_fcr); - } - if (read_iir & APP2_UL) { - dc->last_ier &= ~APP2_UL; - writew(dc->last_ier, dc->reg_ier); - if (send_data(PORT_APP2, dc)) { - writew(APP2_UL, dc->reg_fcr); - dc->last_ier = dc->last_ier | APP2_UL; - writew(dc->last_ier, dc->reg_ier); - } - } - -exit_handler: - spin_unlock(&dc->spin_mutex); - for (a = 0; a < NOZOMI_MAX_PORTS; a++) { - struct tty_struct *tty; - if (test_and_clear_bit(a, &dc->flip)) { - tty = tty_port_tty_get(&dc->port[a].port); - if (tty) - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - } - return IRQ_HANDLED; -none: - spin_unlock(&dc->spin_mutex); - return IRQ_NONE; -} - -static void nozomi_get_card_type(struct nozomi *dc) -{ - int i; - u32 size = 0; - - for (i = 0; i < 6; i++) - size += pci_resource_len(dc->pdev, i); - - /* Assume card type F32_8 if no match */ - dc->card_type = size == 2048 ? F32_2 : F32_8; - - dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type); -} - -static void nozomi_setup_private_data(struct nozomi *dc) -{ - void __iomem *offset = dc->base_addr + dc->card_type / 2; - unsigned int i; - - dc->reg_fcr = (void __iomem *)(offset + R_FCR); - dc->reg_iir = (void __iomem *)(offset + R_IIR); - dc->reg_ier = (void __iomem *)(offset + R_IER); - dc->last_ier = 0; - dc->flip = 0; - - dc->port[PORT_MDM].token_dl = MDM_DL; - dc->port[PORT_DIAG].token_dl = DIAG_DL; - dc->port[PORT_APP1].token_dl = APP1_DL; - dc->port[PORT_APP2].token_dl = APP2_DL; - - for (i = 0; i < MAX_PORT; i++) - init_waitqueue_head(&dc->port[i].tty_wait); -} - -static ssize_t card_type_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev)); - - return sprintf(buf, "%d\n", dc->card_type); -} -static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL); - -static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev)); - - return sprintf(buf, "%u\n", dc->open_ttys); -} -static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL); - -static void make_sysfs_files(struct nozomi *dc) -{ - if (device_create_file(&dc->pdev->dev, &dev_attr_card_type)) - dev_err(&dc->pdev->dev, - "Could not create sysfs file for card_type\n"); - if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys)) - dev_err(&dc->pdev->dev, - "Could not create sysfs file for open_ttys\n"); -} - -static void remove_sysfs_files(struct nozomi *dc) -{ - device_remove_file(&dc->pdev->dev, &dev_attr_card_type); - device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys); -} - -/* Allocate memory for one device */ -static int __devinit nozomi_card_init(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - resource_size_t start; - int ret; - struct nozomi *dc = NULL; - int ndev_idx; - int i; - - dev_dbg(&pdev->dev, "Init, new card found\n"); - - for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++) - if (!ndevs[ndev_idx]) - break; - - if (ndev_idx >= ARRAY_SIZE(ndevs)) { - dev_err(&pdev->dev, "no free tty range for this card left\n"); - ret = -EIO; - goto err; - } - - dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL); - if (unlikely(!dc)) { - dev_err(&pdev->dev, "Could not allocate memory\n"); - ret = -ENOMEM; - goto err_free; - } - - dc->pdev = pdev; - - ret = pci_enable_device(dc->pdev); - if (ret) { - dev_err(&pdev->dev, "Failed to enable PCI Device\n"); - goto err_free; - } - - ret = pci_request_regions(dc->pdev, NOZOMI_NAME); - if (ret) { - dev_err(&pdev->dev, "I/O address 0x%04x already in use\n", - (int) /* nozomi_private.io_addr */ 0); - goto err_disable_device; - } - - start = pci_resource_start(dc->pdev, 0); - if (start == 0) { - dev_err(&pdev->dev, "No I/O address for card detected\n"); - ret = -ENODEV; - goto err_rel_regs; - } - - /* Find out what card type it is */ - nozomi_get_card_type(dc); - - dc->base_addr = ioremap_nocache(start, dc->card_type); - if (!dc->base_addr) { - dev_err(&pdev->dev, "Unable to map card MMIO\n"); - ret = -ENODEV; - goto err_rel_regs; - } - - dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL); - if (!dc->send_buf) { - dev_err(&pdev->dev, "Could not allocate send buffer?\n"); - ret = -ENOMEM; - goto err_free_sbuf; - } - - for (i = PORT_MDM; i < MAX_PORT; i++) { - if (kfifo_alloc(&dc->port[i].fifo_ul, - FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) { - dev_err(&pdev->dev, - "Could not allocate kfifo buffer\n"); - ret = -ENOMEM; - goto err_free_kfifo; - } - } - - spin_lock_init(&dc->spin_mutex); - - nozomi_setup_private_data(dc); - - /* Disable all interrupts */ - dc->last_ier = 0; - writew(dc->last_ier, dc->reg_ier); - - ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED, - NOZOMI_NAME, dc); - if (unlikely(ret)) { - dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq); - goto err_free_kfifo; - } - - DBG1("base_addr: %p", dc->base_addr); - - make_sysfs_files(dc); - - dc->index_start = ndev_idx * MAX_PORT; - ndevs[ndev_idx] = dc; - - pci_set_drvdata(pdev, dc); - - /* Enable RESET interrupt */ - dc->last_ier = RESET; - iowrite16(dc->last_ier, dc->reg_ier); - - dc->state = NOZOMI_STATE_ENABLED; - - for (i = 0; i < MAX_PORT; i++) { - struct device *tty_dev; - struct port *port = &dc->port[i]; - port->dc = dc; - mutex_init(&port->tty_sem); - tty_port_init(&port->port); - port->port.ops = &noz_tty_port_ops; - tty_dev = tty_register_device(ntty_driver, dc->index_start + i, - &pdev->dev); - - if (IS_ERR(tty_dev)) { - ret = PTR_ERR(tty_dev); - dev_err(&pdev->dev, "Could not allocate tty?\n"); - goto err_free_tty; - } - } - - return 0; - -err_free_tty: - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); -err_free_kfifo: - for (i = 0; i < MAX_PORT; i++) - kfifo_free(&dc->port[i].fifo_ul); -err_free_sbuf: - kfree(dc->send_buf); - iounmap(dc->base_addr); -err_rel_regs: - pci_release_regions(pdev); -err_disable_device: - pci_disable_device(pdev); -err_free: - kfree(dc); -err: - return ret; -} - -static void __devexit tty_exit(struct nozomi *dc) -{ - unsigned int i; - - DBG1(" "); - - flush_scheduled_work(); - - for (i = 0; i < MAX_PORT; ++i) { - struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); - if (tty && list_empty(&tty->hangup_work.entry)) - tty_hangup(tty); - tty_kref_put(tty); - } - /* Racy below - surely should wait for scheduled work to be done or - complete off a hangup method ? */ - while (dc->open_ttys) - msleep(1); - for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) - tty_unregister_device(ntty_driver, i); -} - -/* Deallocate memory for one device */ -static void __devexit nozomi_card_exit(struct pci_dev *pdev) -{ - int i; - struct ctrl_ul ctrl; - struct nozomi *dc = pci_get_drvdata(pdev); - - /* Disable all interrupts */ - dc->last_ier = 0; - writew(dc->last_ier, dc->reg_ier); - - tty_exit(dc); - - /* Send 0x0001, command card to resend the reset token. */ - /* This is to get the reset when the module is reloaded. */ - ctrl.port = 0x00; - ctrl.reserved = 0; - ctrl.RTS = 0; - ctrl.DTR = 1; - DBG1("sending flow control 0x%04X", *((u16 *)&ctrl)); - - /* Setup dc->reg addresses to we can use defines here */ - write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2); - writew(CTRL_UL, dc->reg_fcr); /* push the token to the card. */ - - remove_sysfs_files(dc); - - free_irq(pdev->irq, dc); - - for (i = 0; i < MAX_PORT; i++) - kfifo_free(&dc->port[i].fifo_ul); - - kfree(dc->send_buf); - - iounmap(dc->base_addr); - - pci_release_regions(pdev); - - pci_disable_device(pdev); - - ndevs[dc->index_start / MAX_PORT] = NULL; - - kfree(dc); -} - -static void set_rts(const struct tty_struct *tty, int rts) -{ - struct port *port = get_port_by_tty(tty); - - port->ctrl_ul.RTS = rts; - port->update_flow_control = 1; - enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty)); -} - -static void set_dtr(const struct tty_struct *tty, int dtr) -{ - struct port *port = get_port_by_tty(tty); - - DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr); - - port->ctrl_ul.DTR = dtr; - port->update_flow_control = 1; - enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty)); -} - -/* - * ---------------------------------------------------------------------------- - * TTY code - * ---------------------------------------------------------------------------- - */ - -static int ntty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct port *port = get_port_by_tty(tty); - struct nozomi *dc = get_dc_by_tty(tty); - int ret; - if (!port || !dc || dc->state != NOZOMI_STATE_READY) - return -ENODEV; - ret = tty_init_termios(tty); - if (ret == 0) { - tty_driver_kref_get(driver); - tty->count++; - tty->driver_data = port; - driver->ttys[tty->index] = tty; - } - return ret; -} - -static void ntty_cleanup(struct tty_struct *tty) -{ - tty->driver_data = NULL; -} - -static int ntty_activate(struct tty_port *tport, struct tty_struct *tty) -{ - struct port *port = container_of(tport, struct port, port); - struct nozomi *dc = port->dc; - unsigned long flags; - - DBG1("open: %d", port->token_dl); - spin_lock_irqsave(&dc->spin_mutex, flags); - dc->last_ier = dc->last_ier | port->token_dl; - writew(dc->last_ier, dc->reg_ier); - dc->open_ttys++; - spin_unlock_irqrestore(&dc->spin_mutex, flags); - printk("noz: activated %d: %p\n", tty->index, tport); - return 0; -} - -static int ntty_open(struct tty_struct *tty, struct file *filp) -{ - struct port *port = tty->driver_data; - return tty_port_open(&port->port, tty, filp); -} - -static void ntty_shutdown(struct tty_port *tport) -{ - struct port *port = container_of(tport, struct port, port); - struct nozomi *dc = port->dc; - unsigned long flags; - - DBG1("close: %d", port->token_dl); - spin_lock_irqsave(&dc->spin_mutex, flags); - dc->last_ier &= ~(port->token_dl); - writew(dc->last_ier, dc->reg_ier); - dc->open_ttys--; - spin_unlock_irqrestore(&dc->spin_mutex, flags); - printk("noz: shutdown %p\n", tport); -} - -static void ntty_close(struct tty_struct *tty, struct file *filp) -{ - struct port *port = tty->driver_data; - if (port) - tty_port_close(&port->port, tty, filp); -} - -static void ntty_hangup(struct tty_struct *tty) -{ - struct port *port = tty->driver_data; - tty_port_hangup(&port->port); -} - -/* - * called when the userspace process writes to the tty (/dev/noz*). - * Data is inserted into a fifo, which is then read and transfered to the modem. - */ -static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, - int count) -{ - int rval = -EINVAL; - struct nozomi *dc = get_dc_by_tty(tty); - struct port *port = tty->driver_data; - unsigned long flags; - - /* DBG1( "WRITEx: %d, index = %d", count, index); */ - - if (!dc || !port) - return -ENODEV; - - mutex_lock(&port->tty_sem); - - if (unlikely(!port->port.count)) { - DBG1(" "); - goto exit; - } - - rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count); - - /* notify card */ - if (unlikely(dc == NULL)) { - DBG1("No device context?"); - goto exit; - } - - spin_lock_irqsave(&dc->spin_mutex, flags); - /* CTS is only valid on the modem channel */ - if (port == &(dc->port[PORT_MDM])) { - if (port->ctrl_dl.CTS) { - DBG4("Enable interrupt"); - enable_transmit_ul(tty->index % MAX_PORT, dc); - } else { - dev_err(&dc->pdev->dev, - "CTS not active on modem port?\n"); - } - } else { - enable_transmit_ul(tty->index % MAX_PORT, dc); - } - spin_unlock_irqrestore(&dc->spin_mutex, flags); - -exit: - mutex_unlock(&port->tty_sem); - return rval; -} - -/* - * Calculate how much is left in device - * This method is called by the upper tty layer. - * #according to sources N_TTY.c it expects a value >= 0 and - * does not check for negative values. - * - * If the port is unplugged report lots of room and let the bits - * dribble away so we don't block anything. - */ -static int ntty_write_room(struct tty_struct *tty) -{ - struct port *port = tty->driver_data; - int room = 4096; - const struct nozomi *dc = get_dc_by_tty(tty); - - if (dc) { - mutex_lock(&port->tty_sem); - if (port->port.count) - room = kfifo_avail(&port->fifo_ul); - mutex_unlock(&port->tty_sem); - } - return room; -} - -/* Gets io control parameters */ -static int ntty_tiocmget(struct tty_struct *tty) -{ - const struct port *port = tty->driver_data; - const struct ctrl_dl *ctrl_dl = &port->ctrl_dl; - const struct ctrl_ul *ctrl_ul = &port->ctrl_ul; - - /* Note: these could change under us but it is not clear this - matters if so */ - return (ctrl_ul->RTS ? TIOCM_RTS : 0) | - (ctrl_ul->DTR ? TIOCM_DTR : 0) | - (ctrl_dl->DCD ? TIOCM_CAR : 0) | - (ctrl_dl->RI ? TIOCM_RNG : 0) | - (ctrl_dl->DSR ? TIOCM_DSR : 0) | - (ctrl_dl->CTS ? TIOCM_CTS : 0); -} - -/* Sets io controls parameters */ -static int ntty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct nozomi *dc = get_dc_by_tty(tty); - unsigned long flags; - - spin_lock_irqsave(&dc->spin_mutex, flags); - if (set & TIOCM_RTS) - set_rts(tty, 1); - else if (clear & TIOCM_RTS) - set_rts(tty, 0); - - if (set & TIOCM_DTR) - set_dtr(tty, 1); - else if (clear & TIOCM_DTR) - set_dtr(tty, 0); - spin_unlock_irqrestore(&dc->spin_mutex, flags); - - return 0; -} - -static int ntty_cflags_changed(struct port *port, unsigned long flags, - struct async_icount *cprev) -{ - const struct async_icount cnow = port->tty_icount; - int ret; - - ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) || - ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || - ((flags & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || - ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts)); - - *cprev = cnow; - - return ret; -} - -static int ntty_tiocgicount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct port *port = tty->driver_data; - const struct async_icount cnow = port->tty_icount; - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - return 0; -} - -static int ntty_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct port *port = tty->driver_data; - int rval = -ENOIOCTLCMD; - - DBG1("******** IOCTL, cmd: %d", cmd); - - switch (cmd) { - case TIOCMIWAIT: { - struct async_icount cprev = port->tty_icount; - - rval = wait_event_interruptible(port->tty_wait, - ntty_cflags_changed(port, arg, &cprev)); - break; - } - default: - DBG1("ERR: 0x%08X, %d", cmd, cmd); - break; - }; - - return rval; -} - -/* - * Called by the upper tty layer when tty buffers are ready - * to receive data again after a call to throttle. - */ -static void ntty_unthrottle(struct tty_struct *tty) -{ - struct nozomi *dc = get_dc_by_tty(tty); - unsigned long flags; - - DBG1("UNTHROTTLE"); - spin_lock_irqsave(&dc->spin_mutex, flags); - enable_transmit_dl(tty->index % MAX_PORT, dc); - set_rts(tty, 1); - - spin_unlock_irqrestore(&dc->spin_mutex, flags); -} - -/* - * Called by the upper tty layer when the tty buffers are almost full. - * The driver should stop send more data. - */ -static void ntty_throttle(struct tty_struct *tty) -{ - struct nozomi *dc = get_dc_by_tty(tty); - unsigned long flags; - - DBG1("THROTTLE"); - spin_lock_irqsave(&dc->spin_mutex, flags); - set_rts(tty, 0); - spin_unlock_irqrestore(&dc->spin_mutex, flags); -} - -/* Returns number of chars in buffer, called by tty layer */ -static s32 ntty_chars_in_buffer(struct tty_struct *tty) -{ - struct port *port = tty->driver_data; - struct nozomi *dc = get_dc_by_tty(tty); - s32 rval = 0; - - if (unlikely(!dc || !port)) { - goto exit_in_buffer; - } - - if (unlikely(!port->port.count)) { - dev_err(&dc->pdev->dev, "No tty open?\n"); - goto exit_in_buffer; - } - - rval = kfifo_len(&port->fifo_ul); - -exit_in_buffer: - return rval; -} - -static const struct tty_port_operations noz_tty_port_ops = { - .activate = ntty_activate, - .shutdown = ntty_shutdown, -}; - -static const struct tty_operations tty_ops = { - .ioctl = ntty_ioctl, - .open = ntty_open, - .close = ntty_close, - .hangup = ntty_hangup, - .write = ntty_write, - .write_room = ntty_write_room, - .unthrottle = ntty_unthrottle, - .throttle = ntty_throttle, - .chars_in_buffer = ntty_chars_in_buffer, - .tiocmget = ntty_tiocmget, - .tiocmset = ntty_tiocmset, - .get_icount = ntty_tiocgicount, - .install = ntty_install, - .cleanup = ntty_cleanup, -}; - -/* Module initialization */ -static struct pci_driver nozomi_driver = { - .name = NOZOMI_NAME, - .id_table = nozomi_pci_tbl, - .probe = nozomi_card_init, - .remove = __devexit_p(nozomi_card_exit), -}; - -static __init int nozomi_init(void) -{ - int ret; - - printk(KERN_INFO "Initializing %s\n", VERSION_STRING); - - ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS); - if (!ntty_driver) - return -ENOMEM; - - ntty_driver->owner = THIS_MODULE; - ntty_driver->driver_name = NOZOMI_NAME_TTY; - ntty_driver->name = "noz"; - ntty_driver->major = 0; - ntty_driver->type = TTY_DRIVER_TYPE_SERIAL; - ntty_driver->subtype = SERIAL_TYPE_NORMAL; - ntty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - ntty_driver->init_termios = tty_std_termios; - ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \ - HUPCL | CLOCAL; - ntty_driver->init_termios.c_ispeed = 115200; - ntty_driver->init_termios.c_ospeed = 115200; - tty_set_operations(ntty_driver, &tty_ops); - - ret = tty_register_driver(ntty_driver); - if (ret) { - printk(KERN_ERR "Nozomi: failed to register ntty driver\n"); - goto free_tty; - } - - ret = pci_register_driver(&nozomi_driver); - if (ret) { - printk(KERN_ERR "Nozomi: can't register pci driver\n"); - goto unr_tty; - } - - return 0; -unr_tty: - tty_unregister_driver(ntty_driver); -free_tty: - put_tty_driver(ntty_driver); - return ret; -} - -static __exit void nozomi_exit(void) -{ - printk(KERN_INFO "Unloading %s\n", DRIVER_DESC); - pci_unregister_driver(&nozomi_driver); - tty_unregister_driver(ntty_driver); - put_tty_driver(ntty_driver); -} - -module_init(nozomi_init); -module_exit(nozomi_exit); - -module_param(debug, int, S_IRUGO | S_IWUSR); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c deleted file mode 100644 index 3780da8ad12d..000000000000 --- a/drivers/char/rocket.c +++ /dev/null @@ -1,3199 +0,0 @@ -/* - * RocketPort device driver for Linux - * - * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000. - * - * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* - * Kernel Synchronization: - * - * This driver has 2 kernel control paths - exception handlers (calls into the driver - * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts - * are not used. - * - * Critical data: - * - rp_table[], accessed through passed "info" pointers, is a global (static) array of - * serial port state information and the xmit_buf circular buffer. Protected by - * a per port spinlock. - * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there - * is data to be transmitted. Protected by atomic bit operations. - * - rp_num_ports, int indicating number of open ports, protected by atomic operations. - * - * rp_write() and rp_write_char() functions use a per port semaphore to protect against - * simultaneous access to the same port by more than one process. - */ - -/****** Defines ******/ -#define ROCKET_PARANOIA_CHECK -#define ROCKET_DISABLE_SIMUSAGE - -#undef ROCKET_SOFT_FLOW -#undef ROCKET_DEBUG_OPEN -#undef ROCKET_DEBUG_INTR -#undef ROCKET_DEBUG_WRITE -#undef ROCKET_DEBUG_FLOW -#undef ROCKET_DEBUG_THROTTLE -#undef ROCKET_DEBUG_WAIT_UNTIL_SENT -#undef ROCKET_DEBUG_RECEIVE -#undef ROCKET_DEBUG_HANGUP -#undef REV_PCI_ORDER -#undef ROCKET_DEBUG_IO - -#define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */ - -/****** Kernel includes ******/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/****** RocketPort includes ******/ - -#include "rocket_int.h" -#include "rocket.h" - -#define ROCKET_VERSION "2.09" -#define ROCKET_DATE "12-June-2003" - -/****** RocketPort Local Variables ******/ - -static void rp_do_poll(unsigned long dummy); - -static struct tty_driver *rocket_driver; - -static struct rocket_version driver_version = { - ROCKET_VERSION, ROCKET_DATE -}; - -static struct r_port *rp_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */ -static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */ - /* eg. Bit 0 indicates port 0 has xmit data, ... */ -static atomic_t rp_num_ports_open; /* Number of serial ports open */ -static DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0); - -static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */ -static unsigned long board2; -static unsigned long board3; -static unsigned long board4; -static unsigned long controller; -static int support_low_speed; -static unsigned long modem1; -static unsigned long modem2; -static unsigned long modem3; -static unsigned long modem4; -static unsigned long pc104_1[8]; -static unsigned long pc104_2[8]; -static unsigned long pc104_3[8]; -static unsigned long pc104_4[8]; -static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 }; - -static int rp_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */ -static unsigned long rcktpt_io_addr[NUM_BOARDS]; -static int rcktpt_type[NUM_BOARDS]; -static int is_PCI[NUM_BOARDS]; -static rocketModel_t rocketModel[NUM_BOARDS]; -static int max_board; -static const struct tty_port_operations rocket_port_ops; - -/* - * The following arrays define the interrupt bits corresponding to each AIOP. - * These bits are different between the ISA and regular PCI boards and the - * Universal PCI boards. - */ - -static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { - AIOP_INTR_BIT_0, - AIOP_INTR_BIT_1, - AIOP_INTR_BIT_2, - AIOP_INTR_BIT_3 -}; - -static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { - UPCI_AIOP_INTR_BIT_0, - UPCI_AIOP_INTR_BIT_1, - UPCI_AIOP_INTR_BIT_2, - UPCI_AIOP_INTR_BIT_3 -}; - -static Byte_t RData[RDATASIZE] = { - 0x00, 0x09, 0xf6, 0x82, - 0x02, 0x09, 0x86, 0xfb, - 0x04, 0x09, 0x00, 0x0a, - 0x06, 0x09, 0x01, 0x0a, - 0x08, 0x09, 0x8a, 0x13, - 0x0a, 0x09, 0xc5, 0x11, - 0x0c, 0x09, 0x86, 0x85, - 0x0e, 0x09, 0x20, 0x0a, - 0x10, 0x09, 0x21, 0x0a, - 0x12, 0x09, 0x41, 0xff, - 0x14, 0x09, 0x82, 0x00, - 0x16, 0x09, 0x82, 0x7b, - 0x18, 0x09, 0x8a, 0x7d, - 0x1a, 0x09, 0x88, 0x81, - 0x1c, 0x09, 0x86, 0x7a, - 0x1e, 0x09, 0x84, 0x81, - 0x20, 0x09, 0x82, 0x7c, - 0x22, 0x09, 0x0a, 0x0a -}; - -static Byte_t RRegData[RREGDATASIZE] = { - 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ - 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ - 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ - 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ - 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ - 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ - 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ - 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ - 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ - 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ - 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ - 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ - 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ -}; - -static CONTROLLER_T sController[CTL_SIZE] = { - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, - {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, - {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}} -}; - -static Byte_t sBitMapClrTbl[8] = { - 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f -}; - -static Byte_t sBitMapSetTbl[8] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 -}; - -static int sClockPrescale = 0x14; - -/* - * Line number is the ttySIx number (x), the Minor number. We - * assign them sequentially, starting at zero. The following - * array keeps track of the line number assigned to a given board/aiop/channel. - */ -static unsigned char lineNumbers[MAX_RP_PORTS]; -static unsigned long nextLineNumber; - -/***** RocketPort Static Prototypes *********/ -static int __init init_ISA(int i); -static void rp_wait_until_sent(struct tty_struct *tty, int timeout); -static void rp_flush_buffer(struct tty_struct *tty); -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); -static unsigned char GetLineNumber(int ctrl, int aiop, int ch); -static unsigned char SetLineNumber(int ctrl, int aiop, int ch); -static void rp_start(struct tty_struct *tty); -static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, - int ChanNum); -static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode); -static void sFlushRxFIFO(CHANNEL_T * ChP); -static void sFlushTxFIFO(CHANNEL_T * ChP); -static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags); -static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); -static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); -static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); -static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd); -static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, - ByteIO_t * AiopIOList, int AiopIOListSize, - int IRQNum, Byte_t Frequency, int PeriodicOnly); -static int sReadAiopID(ByteIO_t io); -static int sReadAiopNumChan(WordIO_t io); - -MODULE_AUTHOR("Theodore Ts'o"); -MODULE_DESCRIPTION("Comtrol RocketPort driver"); -module_param(board1, ulong, 0); -MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); -module_param(board2, ulong, 0); -MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); -module_param(board3, ulong, 0); -MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); -module_param(board4, ulong, 0); -MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); -module_param(controller, ulong, 0); -MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); -module_param(support_low_speed, bool, 0); -MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); -module_param(modem1, ulong, 0); -MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem"); -module_param(modem2, ulong, 0); -MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem"); -module_param(modem3, ulong, 0); -MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem"); -module_param(modem4, ulong, 0); -MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem"); -module_param_array(pc104_1, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,..."); -module_param_array(pc104_2, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,..."); -module_param_array(pc104_3, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,..."); -module_param_array(pc104_4, ulong, NULL, 0); -MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,..."); - -static int rp_init(void); -static void rp_cleanup_module(void); - -module_init(rp_init); -module_exit(rp_cleanup_module); - - -MODULE_LICENSE("Dual BSD/GPL"); - -/*************************************************************************/ -/* Module code starts here */ - -static inline int rocket_paranoia_check(struct r_port *info, - const char *routine) -{ -#ifdef ROCKET_PARANOIA_CHECK - if (!info) - return 1; - if (info->magic != RPORT_MAGIC) { - printk(KERN_WARNING "Warning: bad magic number for rocketport " - "struct in %s\n", routine); - return 1; - } -#endif - return 0; -} - - -/* Serial port receive data function. Called (from timer poll) when an AIOPIC signals - * that receive data is present on a serial port. Pulls data from FIFO, moves it into the - * tty layer. - */ -static void rp_do_receive(struct r_port *info, - struct tty_struct *tty, - CHANNEL_t * cp, unsigned int ChanStatus) -{ - unsigned int CharNStat; - int ToRecv, wRecv, space; - unsigned char *cbuf; - - ToRecv = sGetRxCnt(cp); -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv); -#endif - if (ToRecv == 0) - return; - - /* - * if status indicates there are errored characters in the - * FIFO, then enter status mode (a word in FIFO holds - * character and status). - */ - if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { - if (!(ChanStatus & STATMODE)) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Entering STATMODE...\n"); -#endif - ChanStatus |= STATMODE; - sEnRxStatusMode(cp); - } - } - - /* - * if we previously entered status mode, then read down the - * FIFO one word at a time, pulling apart the character and - * the status. Update error counters depending on status - */ - if (ChanStatus & STATMODE) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Ignore %x, read %x...\n", - info->ignore_status_mask, info->read_status_mask); -#endif - while (ToRecv) { - char flag; - - CharNStat = sInW(sGetTxRxDataIO(cp)); -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "%x...\n", CharNStat); -#endif - if (CharNStat & STMBREAKH) - CharNStat &= ~(STMFRAMEH | STMPARITYH); - if (CharNStat & info->ignore_status_mask) { - ToRecv--; - continue; - } - CharNStat &= info->read_status_mask; - if (CharNStat & STMBREAKH) - flag = TTY_BREAK; - else if (CharNStat & STMPARITYH) - flag = TTY_PARITY; - else if (CharNStat & STMFRAMEH) - flag = TTY_FRAME; - else if (CharNStat & STMRCVROVRH) - flag = TTY_OVERRUN; - else - flag = TTY_NORMAL; - tty_insert_flip_char(tty, CharNStat & 0xff, flag); - ToRecv--; - } - - /* - * after we've emptied the FIFO in status mode, turn - * status mode back off - */ - if (sGetRxCnt(cp) == 0) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Status mode off.\n"); -#endif - sDisRxStatusMode(cp); - } - } else { - /* - * we aren't in status mode, so read down the FIFO two - * characters at time by doing repeated word IO - * transfer. - */ - space = tty_prepare_flip_string(tty, &cbuf, ToRecv); - if (space < ToRecv) { -#ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); -#endif - if (space <= 0) - return; - ToRecv = space; - } - wRecv = ToRecv >> 1; - if (wRecv) - sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv); - if (ToRecv & 1) - cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); - } - /* Push the data up to the tty layer */ - tty_flip_buffer_push(tty); -} - -/* - * Serial port transmit data function. Called from the timer polling loop as a - * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready - * to be sent out the serial port. Data is buffered in rp_table[line].xmit_buf, it is - * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks. - */ -static void rp_do_transmit(struct r_port *info) -{ - int c; - CHANNEL_t *cp = &info->channel; - struct tty_struct *tty; - unsigned long flags; - -#ifdef ROCKET_DEBUG_INTR - printk(KERN_DEBUG "%s\n", __func__); -#endif - if (!info) - return; - tty = tty_port_tty_get(&info->port); - - if (tty == NULL) { - printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - return; - } - - spin_lock_irqsave(&info->slock, flags); - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - /* Loop sending data to FIFO until done or FIFO full */ - while (1) { - if (tty->stopped || tty->hw_stopped) - break; - c = min(info->xmit_fifo_room, info->xmit_cnt); - c = min(c, XMIT_BUF_SIZE - info->xmit_tail); - if (c <= 0 || info->xmit_fifo_room <= 0) - break; - sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); - if (c & 1) - sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]); - info->xmit_tail += c; - info->xmit_tail &= XMIT_BUF_SIZE - 1; - info->xmit_cnt -= c; - info->xmit_fifo_room -= c; -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "tx %d chars...\n", c); -#endif - } - - if (info->xmit_cnt == 0) - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - if (info->xmit_cnt < WAKEUP_CHARS) { - tty_wakeup(tty); -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } - - spin_unlock_irqrestore(&info->slock, flags); - tty_kref_put(tty); - -#ifdef ROCKET_DEBUG_INTR - printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, - info->xmit_tail, info->xmit_fifo_room); -#endif -} - -/* - * Called when a serial port signals it has read data in it's RX FIFO. - * It checks what interrupts are pending and services them, including - * receiving serial data. - */ -static void rp_handle_port(struct r_port *info) -{ - CHANNEL_t *cp; - struct tty_struct *tty; - unsigned int IntMask, ChanStatus; - - if (!info) - return; - - if ((info->port.flags & ASYNC_INITIALIZED) == 0) { - printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " - "info->flags & NOT_INIT\n"); - return; - } - tty = tty_port_tty_get(&info->port); - if (!tty) { - printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " - "tty==NULL\n"); - return; - } - cp = &info->channel; - - IntMask = sGetChanIntID(cp) & info->intmask; -#ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_interrupt %02x...\n", IntMask); -#endif - ChanStatus = sGetChanStatus(cp); - if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ - rp_do_receive(info, tty, cp, ChanStatus); - } - if (IntMask & DELTA_CD) { /* CD change */ -#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, - (ChanStatus & CD_ACT) ? "on" : "off"); -#endif - if (!(ChanStatus & CD_ACT) && info->cd_status) { -#ifdef ROCKET_DEBUG_HANGUP - printk(KERN_INFO "CD drop, calling hangup.\n"); -#endif - tty_hangup(tty); - } - info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; - wake_up_interruptible(&info->port.open_wait); - } -#ifdef ROCKET_DEBUG_INTR - if (IntMask & DELTA_CTS) { /* CTS change */ - printk(KERN_INFO "CTS change...\n"); - } - if (IntMask & DELTA_DSR) { /* DSR change */ - printk(KERN_INFO "DSR change...\n"); - } -#endif - tty_kref_put(tty); -} - -/* - * The top level polling routine. Repeats every 1/100 HZ (10ms). - */ -static void rp_do_poll(unsigned long dummy) -{ - CONTROLLER_t *ctlp; - int ctrl, aiop, ch, line; - unsigned int xmitmask, i; - unsigned int CtlMask; - unsigned char AiopMask; - Word_t bit; - - /* Walk through all the boards (ctrl's) */ - for (ctrl = 0; ctrl < max_board; ctrl++) { - if (rcktpt_io_addr[ctrl] <= 0) - continue; - - /* Get a ptr to the board's control struct */ - ctlp = sCtlNumToCtlPtr(ctrl); - - /* Get the interrupt status from the board */ -#ifdef CONFIG_PCI - if (ctlp->BusType == isPCI) - CtlMask = sPCIGetControllerIntStatus(ctlp); - else -#endif - CtlMask = sGetControllerIntStatus(ctlp); - - /* Check if any AIOP read bits are set */ - for (aiop = 0; CtlMask; aiop++) { - bit = ctlp->AiopIntrBits[aiop]; - if (CtlMask & bit) { - CtlMask &= ~bit; - AiopMask = sGetAiopIntStatus(ctlp, aiop); - - /* Check if any port read bits are set */ - for (ch = 0; AiopMask; AiopMask >>= 1, ch++) { - if (AiopMask & 1) { - - /* Get the line number (/dev/ttyRx number). */ - /* Read the data from the port. */ - line = GetLineNumber(ctrl, aiop, ch); - rp_handle_port(rp_table[line]); - } - } - } - } - - xmitmask = xmit_flags[ctrl]; - - /* - * xmit_flags contains bit-significant flags, indicating there is data - * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port - * 1, ... (32 total possible). The variable i has the aiop and ch - * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc). - */ - if (xmitmask) { - for (i = 0; i < rocketModel[ctrl].numPorts; i++) { - if (xmitmask & (1 << i)) { - aiop = (i & 0x18) >> 3; - ch = i & 0x07; - line = GetLineNumber(ctrl, aiop, ch); - rp_do_transmit(rp_table[line]); - } - } - } - } - - /* - * Reset the timer so we get called at the next clock tick (10ms). - */ - if (atomic_read(&rp_num_ports_open)) - mod_timer(&rocket_timer, jiffies + POLL_PERIOD); -} - -/* - * Initializes the r_port structure for a port, as well as enabling the port on - * the board. - * Inputs: board, aiop, chan numbers - */ -static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) -{ - unsigned rocketMode; - struct r_port *info; - int line; - CONTROLLER_T *ctlp; - - /* Get the next available line number */ - line = SetLineNumber(board, aiop, chan); - - ctlp = sCtlNumToCtlPtr(board); - - /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ - info = kzalloc(sizeof (struct r_port), GFP_KERNEL); - if (!info) { - printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", - line); - return; - } - - info->magic = RPORT_MAGIC; - info->line = line; - info->ctlp = ctlp; - info->board = board; - info->aiop = aiop; - info->chan = chan; - tty_port_init(&info->port); - info->port.ops = &rocket_port_ops; - init_completion(&info->close_wait); - info->flags &= ~ROCKET_MODE_MASK; - switch (pc104[board][line]) { - case 422: - info->flags |= ROCKET_MODE_RS422; - break; - case 485: - info->flags |= ROCKET_MODE_RS485; - break; - case 232: - default: - info->flags |= ROCKET_MODE_RS232; - break; - } - - info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; - if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { - printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", - board, aiop, chan); - kfree(info); - return; - } - - rocketMode = info->flags & ROCKET_MODE_MASK; - - if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) - sEnRTSToggle(&info->channel); - else - sDisRTSToggle(&info->channel); - - if (ctlp->boardType == ROCKET_TYPE_PC104) { - switch (rocketMode) { - case ROCKET_MODE_RS485: - sSetInterfaceMode(&info->channel, InterfaceModeRS485); - break; - case ROCKET_MODE_RS422: - sSetInterfaceMode(&info->channel, InterfaceModeRS422); - break; - case ROCKET_MODE_RS232: - default: - if (info->flags & ROCKET_RTS_TOGGLE) - sSetInterfaceMode(&info->channel, InterfaceModeRS232T); - else - sSetInterfaceMode(&info->channel, InterfaceModeRS232); - break; - } - } - spin_lock_init(&info->slock); - mutex_init(&info->write_mtx); - rp_table[line] = info; - tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev : - NULL); -} - -/* - * Configures a rocketport port according to its termio settings. Called from - * user mode into the driver (exception handler). *info CD manipulation is spinlock protected. - */ -static void configure_r_port(struct tty_struct *tty, struct r_port *info, - struct ktermios *old_termios) -{ - unsigned cflag; - unsigned long flags; - unsigned rocketMode; - int bits, baud, divisor; - CHANNEL_t *cp; - struct ktermios *t = tty->termios; - - cp = &info->channel; - cflag = t->c_cflag; - - /* Byte size and parity */ - if ((cflag & CSIZE) == CS8) { - sSetData8(cp); - bits = 10; - } else { - sSetData7(cp); - bits = 9; - } - if (cflag & CSTOPB) { - sSetStop2(cp); - bits++; - } else { - sSetStop1(cp); - } - - if (cflag & PARENB) { - sEnParity(cp); - bits++; - if (cflag & PARODD) { - sSetOddParity(cp); - } else { - sSetEvenParity(cp); - } - } else { - sDisParity(cp); - } - - /* baud rate */ - baud = tty_get_baud_rate(tty); - if (!baud) - baud = 9600; - divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1; - if ((divisor >= 8192 || divisor < 0) && old_termios) { - baud = tty_termios_baud_rate(old_termios); - if (!baud) - baud = 9600; - divisor = (rp_baud_base[info->board] / baud) - 1; - } - if (divisor >= 8192 || divisor < 0) { - baud = 9600; - divisor = (rp_baud_base[info->board] / baud) - 1; - } - info->cps = baud / bits; - sSetBaud(cp, divisor); - - /* FIXME: Should really back compute a baud rate from the divisor */ - tty_encode_baud_rate(tty, baud, baud); - - if (cflag & CRTSCTS) { - info->intmask |= DELTA_CTS; - sEnCTSFlowCtl(cp); - } else { - info->intmask &= ~DELTA_CTS; - sDisCTSFlowCtl(cp); - } - if (cflag & CLOCAL) { - info->intmask &= ~DELTA_CD; - } else { - spin_lock_irqsave(&info->slock, flags); - if (sGetChanStatus(cp) & CD_ACT) - info->cd_status = 1; - else - info->cd_status = 0; - info->intmask |= DELTA_CD; - spin_unlock_irqrestore(&info->slock, flags); - } - - /* - * Handle software flow control in the board - */ -#ifdef ROCKET_SOFT_FLOW - if (I_IXON(tty)) { - sEnTxSoftFlowCtl(cp); - if (I_IXANY(tty)) { - sEnIXANY(cp); - } else { - sDisIXANY(cp); - } - sSetTxXONChar(cp, START_CHAR(tty)); - sSetTxXOFFChar(cp, STOP_CHAR(tty)); - } else { - sDisTxSoftFlowCtl(cp); - sDisIXANY(cp); - sClrTxXOFF(cp); - } -#endif - - /* - * Set up ignore/read mask words - */ - info->read_status_mask = STMRCVROVRH | 0xFF; - if (I_INPCK(tty)) - info->read_status_mask |= STMFRAMEH | STMPARITYH; - if (I_BRKINT(tty) || I_PARMRK(tty)) - info->read_status_mask |= STMBREAKH; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(tty)) - info->ignore_status_mask |= STMFRAMEH | STMPARITYH; - if (I_IGNBRK(tty)) { - info->ignore_status_mask |= STMBREAKH; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too. (For real raw support). - */ - if (I_IGNPAR(tty)) - info->ignore_status_mask |= STMRCVROVRH; - } - - rocketMode = info->flags & ROCKET_MODE_MASK; - - if ((info->flags & ROCKET_RTS_TOGGLE) - || (rocketMode == ROCKET_MODE_RS485)) - sEnRTSToggle(cp); - else - sDisRTSToggle(cp); - - sSetRTS(&info->channel); - - if (cp->CtlP->boardType == ROCKET_TYPE_PC104) { - switch (rocketMode) { - case ROCKET_MODE_RS485: - sSetInterfaceMode(cp, InterfaceModeRS485); - break; - case ROCKET_MODE_RS422: - sSetInterfaceMode(cp, InterfaceModeRS422); - break; - case ROCKET_MODE_RS232: - default: - if (info->flags & ROCKET_RTS_TOGGLE) - sSetInterfaceMode(cp, InterfaceModeRS232T); - else - sSetInterfaceMode(cp, InterfaceModeRS232); - break; - } - } -} - -static int carrier_raised(struct tty_port *port) -{ - struct r_port *info = container_of(port, struct r_port, port); - return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; -} - -static void dtr_rts(struct tty_port *port, int on) -{ - struct r_port *info = container_of(port, struct r_port, port); - if (on) { - sSetDTR(&info->channel); - sSetRTS(&info->channel); - } else { - sClrDTR(&info->channel); - sClrRTS(&info->channel); - } -} - -/* - * Exception handler that opens a serial port. Creates xmit_buf storage, fills in - * port's r_port struct. Initializes the port hardware. - */ -static int rp_open(struct tty_struct *tty, struct file *filp) -{ - struct r_port *info; - struct tty_port *port; - int line = 0, retval; - CHANNEL_t *cp; - unsigned long page; - - line = tty->index; - if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL)) - return -ENXIO; - port = &info->port; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - if (port->flags & ASYNC_CLOSING) { - retval = wait_for_completion_interruptible(&info->close_wait); - free_page(page); - if (retval) - return retval; - return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); - } - - /* - * We must not sleep from here until the port is marked fully in use. - */ - if (info->xmit_buf) - free_page(page); - else - info->xmit_buf = (unsigned char *) page; - - tty->driver_data = info; - tty_port_tty_set(port, tty); - - if (port->count++ == 0) { - atomic_inc(&rp_num_ports_open); - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod++ = %d...\n", - atomic_read(&rp_num_ports_open)); -#endif - } -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count); -#endif - - /* - * Info->count is now 1; so it's safe to sleep now. - */ - if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { - cp = &info->channel; - sSetRxTrigger(cp, TRIG_1); - if (sGetChanStatus(cp) & CD_ACT) - info->cd_status = 1; - else - info->cd_status = 0; - sDisRxStatusMode(cp); - sFlushRxFIFO(cp); - sFlushTxFIFO(cp); - - sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sSetRxTrigger(cp, TRIG_1); - - sGetChanStatus(cp); - sDisRxStatusMode(cp); - sClrTxXOFF(cp); - - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - - sEnRxFIFO(cp); - sEnTransmit(cp); - - set_bit(ASYNCB_INITIALIZED, &info->port.flags); - - /* - * Set up the tty->alt_speed kludge - */ - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) - tty->alt_speed = 57600; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) - tty->alt_speed = 115200; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) - tty->alt_speed = 230400; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) - tty->alt_speed = 460800; - - configure_r_port(tty, info, NULL); - if (tty->termios->c_cflag & CBAUD) { - sSetDTR(cp); - sSetRTS(cp); - } - } - /* Starts (or resets) the maint polling loop */ - mod_timer(&rocket_timer, jiffies + POLL_PERIOD); - - retval = tty_port_block_til_ready(port, tty, filp); - if (retval) { -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval); -#endif - return retval; - } - return 0; -} - -/* - * Exception handler that closes a serial port. info->port.count is considered critical. - */ -static void rp_close(struct tty_struct *tty, struct file *filp) -{ - struct r_port *info = tty->driver_data; - struct tty_port *port = &info->port; - int timeout; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_close")) - return; - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count); -#endif - - if (tty_port_close_start(port, tty, filp) == 0) - return; - - mutex_lock(&port->mutex); - cp = &info->channel; - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps; - if (timeout == 0) - timeout = 1; - rp_wait_until_sent(tty, timeout); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - sDisTransmit(cp); - sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - sClrTxXOFF(cp); - sFlushRxFIFO(cp); - sFlushTxFIFO(cp); - sClrRTS(cp); - if (C_HUPCL(tty)) - sClrDTR(cp); - - rp_flush_buffer(tty); - - tty_ldisc_flush(tty); - - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - - /* We can't yet use tty_port_close_end as the buffer handling in this - driver is a bit different to the usual */ - - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - } - wake_up_interruptible(&port->open_wait); - } else { - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = NULL; - } - } - spin_lock_irq(&port->lock); - info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); - tty->closing = 0; - spin_unlock_irq(&port->lock); - mutex_unlock(&port->mutex); - tty_port_tty_set(port, NULL); - - wake_up_interruptible(&port->close_wait); - complete_all(&info->close_wait); - atomic_dec(&rp_num_ports_open); - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod-- = %d...\n", - atomic_read(&rp_num_ports_open)); - printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line); -#endif - -} - -static void rp_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned cflag; - - if (rocket_paranoia_check(info, "rp_set_termios")) - return; - - cflag = tty->termios->c_cflag; - - /* - * This driver doesn't support CS5 or CS6 - */ - if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) - tty->termios->c_cflag = - ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); - /* Or CMSPAR */ - tty->termios->c_cflag &= ~CMSPAR; - - configure_r_port(tty, info, old_termios); - - cp = &info->channel; - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { - sClrDTR(cp); - sClrRTS(cp); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { - if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) - sSetRTS(cp); - sSetDTR(cp); - } - - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rp_start(tty); - } -} - -static int rp_break(struct tty_struct *tty, int break_state) -{ - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_break")) - return -EINVAL; - - spin_lock_irqsave(&info->slock, flags); - if (break_state == -1) - sSendBreak(&info->channel); - else - sClrBreak(&info->channel); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -/* - * sGetChanRI used to be a macro in rocket_int.h. When the functionality for - * the UPCI boards was added, it was decided to make this a function because - * the macro was getting too complicated. All cases except the first one - * (UPCIRingInd) are taken directly from the original macro. - */ -static int sGetChanRI(CHANNEL_T * ChP) -{ - CONTROLLER_t *CtlP = ChP->CtlP; - int ChanNum = ChP->ChanNum; - int RingInd = 0; - - if (CtlP->UPCIRingInd) - RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]); - else if (CtlP->AltChanRingIndicator) - RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT; - else if (CtlP->boardType == ROCKET_TYPE_PC104) - RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]); - - return RingInd; -} - -/********************************************************************************************/ -/* Here are the routines used by rp_ioctl. These are all called from exception handlers. */ - -/* - * Returns the state of the serial modem control lines. These next 2 functions - * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. - */ -static int rp_tiocmget(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - unsigned int control, result, ChanStatus; - - ChanStatus = sGetChanStatusLo(&info->channel); - control = info->channel.TxControl[3]; - result = ((control & SET_RTS) ? TIOCM_RTS : 0) | - ((control & SET_DTR) ? TIOCM_DTR : 0) | - ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | - (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) | - ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | - ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0); - - return result; -} - -/* - * Sets the modem control lines - */ -static int rp_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct r_port *info = tty->driver_data; - - if (set & TIOCM_RTS) - info->channel.TxControl[3] |= SET_RTS; - if (set & TIOCM_DTR) - info->channel.TxControl[3] |= SET_DTR; - if (clear & TIOCM_RTS) - info->channel.TxControl[3] &= ~SET_RTS; - if (clear & TIOCM_DTR) - info->channel.TxControl[3] &= ~SET_DTR; - - out32(info->channel.IndexAddr, info->channel.TxControl); - return 0; -} - -static int get_config(struct r_port *info, struct rocket_config __user *retinfo) -{ - struct rocket_config tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof (tmp)); - mutex_lock(&info->port.mutex); - tmp.line = info->line; - tmp.flags = info->flags; - tmp.close_delay = info->port.close_delay; - tmp.closing_wait = info->port.closing_wait; - tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; - mutex_unlock(&info->port.mutex); - - if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) - return -EFAULT; - return 0; -} - -static int set_config(struct tty_struct *tty, struct r_port *info, - struct rocket_config __user *new_info) -{ - struct rocket_config new_serial; - - if (copy_from_user(&new_serial, new_info, sizeof (new_serial))) - return -EFAULT; - - mutex_lock(&info->port.mutex); - if (!capable(CAP_SYS_ADMIN)) - { - if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) { - mutex_unlock(&info->port.mutex); - return -EPERM; - } - info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); - configure_r_port(tty, info, NULL); - mutex_unlock(&info->port.mutex); - return 0; - } - - info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); - info->port.close_delay = new_serial.close_delay; - info->port.closing_wait = new_serial.closing_wait; - - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) - tty->alt_speed = 57600; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) - tty->alt_speed = 115200; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) - tty->alt_speed = 230400; - if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) - tty->alt_speed = 460800; - mutex_unlock(&info->port.mutex); - - configure_r_port(tty, info, NULL); - return 0; -} - -/* - * This function fills in a rocket_ports struct with information - * about what boards/ports are in the system. This info is passed - * to user space. See setrocket.c where the info is used to create - * the /dev/ttyRx ports. - */ -static int get_ports(struct r_port *info, struct rocket_ports __user *retports) -{ - struct rocket_ports tmp; - int board; - - if (!retports) - return -EFAULT; - memset(&tmp, 0, sizeof (tmp)); - tmp.tty_major = rocket_driver->major; - - for (board = 0; board < 4; board++) { - tmp.rocketModel[board].model = rocketModel[board].model; - strcpy(tmp.rocketModel[board].modelString, rocketModel[board].modelString); - tmp.rocketModel[board].numPorts = rocketModel[board].numPorts; - tmp.rocketModel[board].loadrm2 = rocketModel[board].loadrm2; - tmp.rocketModel[board].startingPortNumber = rocketModel[board].startingPortNumber; - } - if (copy_to_user(retports, &tmp, sizeof (*retports))) - return -EFAULT; - return 0; -} - -static int reset_rm2(struct r_port *info, void __user *arg) -{ - int reset; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&reset, arg, sizeof (int))) - return -EFAULT; - if (reset) - reset = 1; - - if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII && - rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII) - return -EINVAL; - - if (info->ctlp->BusType == isISA) - sModemReset(info->ctlp, info->chan, reset); - else - sPCIModemReset(info->ctlp, info->chan, reset); - - return 0; -} - -static int get_version(struct r_port *info, struct rocket_version __user *retvers) -{ - if (copy_to_user(retvers, &driver_version, sizeof (*retvers))) - return -EFAULT; - return 0; -} - -/* IOCTL call handler into the driver */ -static int rp_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct r_port *info = tty->driver_data; - void __user *argp = (void __user *)arg; - int ret = 0; - - if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl")) - return -ENXIO; - - switch (cmd) { - case RCKP_GET_STRUCT: - if (copy_to_user(argp, info, sizeof (struct r_port))) - ret = -EFAULT; - break; - case RCKP_GET_CONFIG: - ret = get_config(info, argp); - break; - case RCKP_SET_CONFIG: - ret = set_config(tty, info, argp); - break; - case RCKP_GET_PORTS: - ret = get_ports(info, argp); - break; - case RCKP_RESET_RM2: - ret = reset_rm2(info, argp); - break; - case RCKP_GET_VERSION: - ret = get_version(info, argp); - break; - default: - ret = -ENOIOCTLCMD; - } - return ret; -} - -static void rp_send_xchar(struct tty_struct *tty, char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_send_xchar")) - return; - - cp = &info->channel; - if (sGetTxCnt(cp)) - sWriteTxPrioByte(cp, ch); - else - sWriteTxByte(sGetTxRxDataIO(cp), ch); -} - -static void rp_throttle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "throttle %s: %d....\n", tty->name, - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (rocket_paranoia_check(info, "rp_throttle")) - return; - - cp = &info->channel; - if (I_IXOFF(tty)) - rp_send_xchar(tty, STOP_CHAR(tty)); - - sClrRTS(&info->channel); -} - -static void rp_unthrottle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "unthrottle %s: %d....\n", tty->name, - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (rocket_paranoia_check(info, "rp_throttle")) - return; - - cp = &info->channel; - if (I_IXOFF(tty)) - rp_send_xchar(tty, START_CHAR(tty)); - - sSetRTS(&info->channel); -} - -/* - * ------------------------------------------------------------ - * rp_stop() and rp_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rp_stop(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "stop %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - if (sGetTxCnt(&info->channel)) - sDisTransmit(&info->channel); -} - -static void rp_start(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "start %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - sEnTransmit(&info->channel); - set_bit((info->aiop * 8) + info->chan, - (void *) &xmit_flags[info->board]); -} - -/* - * rp_wait_until_sent() --- wait until the transmitter is empty - */ -static void rp_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long orig_jiffies; - int check_time, exit_time; - int txcnt; - - if (rocket_paranoia_check(info, "rp_wait_until_sent")) - return; - - cp = &info->channel; - - orig_jiffies = jiffies; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout, - jiffies); - printk(KERN_INFO "cps=%d...\n", info->cps); -#endif - while (1) { - txcnt = sGetTxCnt(cp); - if (!txcnt) { - if (sGetChanStatusLo(cp) & TXSHRMT) - break; - check_time = (HZ / info->cps) / 5; - } else { - check_time = HZ * txcnt / info->cps; - } - if (timeout) { - exit_time = orig_jiffies + timeout - jiffies; - if (exit_time <= 0) - break; - if (exit_time < check_time) - check_time = exit_time; - } - if (check_time == 0) - check_time = 1; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, - jiffies, check_time); -#endif - msleep_interruptible(jiffies_to_msecs(check_time)); - if (signal_pending(current)) - break; - } - __set_current_state(TASK_RUNNING); -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); -#endif -} - -/* - * rp_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rp_hangup(struct tty_struct *tty) -{ - CHANNEL_t *cp; - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_hangup")) - return; - -#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line); -#endif - rp_flush_buffer(tty); - spin_lock_irqsave(&info->port.lock, flags); - if (info->port.flags & ASYNC_CLOSING) { - spin_unlock_irqrestore(&info->port.lock, flags); - return; - } - if (info->port.count) - atomic_dec(&rp_num_ports_open); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - spin_unlock_irqrestore(&info->port.lock, flags); - - tty_port_hangup(&info->port); - - cp = &info->channel; - sDisRxFIFO(cp); - sDisTransmit(cp); - sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - sClrTxXOFF(cp); - clear_bit(ASYNCB_INITIALIZED, &info->port.flags); - - wake_up_interruptible(&info->port.open_wait); -} - -/* - * Exception handler - write char routine. The RocketPort driver uses a - * double-buffering strategy, with the twist that if the in-memory CPU - * buffer is empty, and there's space in the transmit FIFO, the - * writing routines will write directly to transmit FIFO. - * Write buffer and counters protected by spinlocks - */ -static int rp_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_put_char")) - return 0; - - /* - * Grab the port write mutex, locking out other processes that try to - * write to this port - */ - mutex_lock(&info->write_mtx); - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_put_char %c...\n", ch); -#endif - - spin_lock_irqsave(&info->slock, flags); - cp = &info->channel; - - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= XMIT_BUF_SIZE - 1; - info->xmit_cnt++; - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - } else { - sOutB(sGetTxRxDataIO(cp), ch); - info->xmit_fifo_room--; - } - spin_unlock_irqrestore(&info->slock, flags); - mutex_unlock(&info->write_mtx); - return 1; -} - -/* - * Exception handler - write routine, called when user app writes to the device. - * A per port write mutex is used to protect from another process writing to - * this port at the same time. This other process could be running on the other CPU - * or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). - * Spinlocks protect the info xmit members. - */ -static int rp_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - const unsigned char *b; - int c, retval = 0; - unsigned long flags; - - if (count <= 0 || rocket_paranoia_check(info, "rp_write")) - return 0; - - if (mutex_lock_interruptible(&info->write_mtx)) - return -ERESTARTSYS; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write %d chars...\n", count); -#endif - cp = &info->channel; - - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - /* - * If the write queue for the port is empty, and there is FIFO space, stuff bytes - * into FIFO. Use the write queue for temp storage. - */ - if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { - c = min(count, info->xmit_fifo_room); - b = buf; - - /* Push data into FIFO, 2 bytes at a time */ - sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2); - - /* If there is a byte remaining, write it */ - if (c & 1) - sOutB(sGetTxRxDataIO(cp), b[c - 1]); - - retval += c; - buf += c; - count -= c; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_fifo_room -= c; - spin_unlock_irqrestore(&info->slock, flags); - } - - /* If count is zero, we wrote it all and are done */ - if (!count) - goto end; - - /* Write remaining data into the port's xmit_buf */ - while (1) { - /* Hung up ? */ - if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags)) - goto end; - c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); - c = min(c, XMIT_BUF_SIZE - info->xmit_head); - if (c <= 0) - break; - - b = buf; - memcpy(info->xmit_buf + info->xmit_head, b, c); - - spin_lock_irqsave(&info->slock, flags); - info->xmit_head = - (info->xmit_head + c) & (XMIT_BUF_SIZE - 1); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->slock, flags); - - buf += c; - count -= c; - retval += c; - } - - if ((retval > 0) && !tty->stopped && !tty->hw_stopped) - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - -end: - if (info->xmit_cnt < WAKEUP_CHARS) { - tty_wakeup(tty); -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } - mutex_unlock(&info->write_mtx); - return retval; -} - -/* - * Return the number of characters that can be sent. We estimate - * only using the in-memory transmit buffer only, and ignore the - * potential space in the transmit FIFO. - */ -static int rp_write_room(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - int ret; - - if (rocket_paranoia_check(info, "rp_write_room")) - return 0; - - ret = XMIT_BUF_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write_room returns %d...\n", ret); -#endif - return ret; -} - -/* - * Return the number of characters in the buffer. Again, this only - * counts those characters in the in-memory transmit buffer. - */ -static int rp_chars_in_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_chars_in_buffer")) - return 0; - - cp = &info->channel; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); -#endif - return info->xmit_cnt; -} - -/* - * Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the - * r_port struct for the port. Note that spinlock are used to protect info members, - * do not call this function if the spinlock is already held. - */ -static void rp_flush_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_flush_buffer")) - return; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&info->slock, flags); - -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - tty_wakeup(tty); - - cp = &info->channel; - sFlushTxFIFO(cp); -} - -#ifdef CONFIG_PCI - -static struct pci_device_id __devinitdata __used rocket_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, - { } -}; -MODULE_DEVICE_TABLE(pci, rocket_pci_ids); - -/* - * Called when a PCI card is found. Retrieves and stores model information, - * init's aiopic and serial port hardware. - * Inputs: i is the board number (0-n) - */ -static __init int register_PCI(int i, struct pci_dev *dev) -{ - int num_aiops, aiop, max_num_aiops, num_chan, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - char *str, *board_type; - CONTROLLER_t *ctlp; - - int fast_clock = 0; - int altChanRingIndicator = 0; - int ports_per_aiop = 8; - WordIO_t ConfigIO = 0; - ByteIO_t UPCIRingInd = 0; - - if (!dev || pci_enable_device(dev)) - return 0; - - rcktpt_io_addr[i] = pci_resource_start(dev, 0); - - rcktpt_type[i] = ROCKET_TYPE_NORMAL; - rocketModel[i].loadrm2 = 0; - rocketModel[i].startingPortNumber = nextLineNumber; - - /* Depending on the model, set up some config variables */ - switch (dev->device) { - case PCI_DEVICE_ID_RP4QUAD: - str = "Quadcable"; - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4QUAD; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8OCTA: - str = "Octacable"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8OCTA: - str = "Octacable"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8INTF: - str = "8"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8INTF: - str = "8"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8J: - str = "8J"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8J; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP4J: - str = "4J"; - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4J; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8SNI: - str = "8 (DB78 Custom)"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8SNI; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP16SNI: - str = "16 (DB78 Custom)"; - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16SNI; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP16INTF: - str = "16"; - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_URP16INTF: - str = "16"; - max_num_aiops = 2; - rocketModel[i].model = MODEL_UPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_CRP16INTF: - str = "16"; - max_num_aiops = 2; - rocketModel[i].model = MODEL_CPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP32INTF: - str = "32"; - max_num_aiops = 4; - rocketModel[i].model = MODEL_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_URP32INTF: - str = "32"; - max_num_aiops = 4; - rocketModel[i].model = MODEL_UPCI_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_RPP4: - str = "Plus Quadcable"; - max_num_aiops = 1; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP4; - strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RPP8: - str = "Plus Octacable"; - max_num_aiops = 2; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP8; - strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP2_232: - str = "Plus 2 (RS-232)"; - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_232; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP2_422: - str = "Plus 2 (RS-422)"; - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_422; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP6M: - - max_num_aiops = 1; - ports_per_aiop = 6; - str = "6-port"; - - /* If revision is 1, the rocketmodem flash must be loaded. - * If it is 2 it is a "socketed" version. */ - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP6M; - strcpy(rocketModel[i].modelString, "RocketModem 6 port"); - rocketModel[i].numPorts = 6; - break; - case PCI_DEVICE_ID_RP4M: - max_num_aiops = 1; - ports_per_aiop = 4; - str = "4-port"; - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP4M; - strcpy(rocketModel[i].modelString, "RocketModem 4 port"); - rocketModel[i].numPorts = 4; - break; - default: - str = "(unknown/unsupported)"; - max_num_aiops = 0; - break; - } - - /* - * Check for UPCI boards. - */ - - switch (dev->device) { - case PCI_DEVICE_ID_URP32INTF: - case PCI_DEVICE_ID_URP8INTF: - case PCI_DEVICE_ID_URP16INTF: - case PCI_DEVICE_ID_CRP16INTF: - case PCI_DEVICE_ID_URP8OCTA: - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - ConfigIO = pci_resource_start(dev, 1); - if (dev->device == PCI_DEVICE_ID_URP8OCTA) { - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - - /* - * Check for octa or quad cable. - */ - if (! - (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & - PCI_GPIO_CTRL_8PORT)) { - str = "Quadcable"; - ports_per_aiop = 4; - rocketModel[i].numPorts = 4; - } - } - break; - case PCI_DEVICE_ID_UPCI_RM3_8PORT: - str = "8 ports"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_8PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); - rocketModel[i].numPorts = 8; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - case PCI_DEVICE_ID_UPCI_RM3_4PORT: - str = "4 ports"; - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_4PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); - rocketModel[i].numPorts = 4; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - default: - break; - } - - switch (rcktpt_type[i]) { - case ROCKET_TYPE_MODEM: - board_type = "RocketModem"; - break; - case ROCKET_TYPE_MODEMII: - board_type = "RocketModem II"; - break; - case ROCKET_TYPE_MODEMIII: - board_type = "RocketModem III"; - break; - default: - board_type = "RocketPort"; - break; - } - - if (fast_clock) { - sClockPrescale = 0x12; /* mod 2 (divide by 3) */ - rp_baud_base[i] = 921600; - } else { - /* - * If support_low_speed is set, use the slow clock - * prescale, which supports 50 bps - */ - if (support_low_speed) { - /* mod 9 (divide by 10) prescale */ - sClockPrescale = 0x19; - rp_baud_base[i] = 230400; - } else { - /* mod 4 (devide by 5) prescale */ - sClockPrescale = 0x14; - rp_baud_base[i] = 460800; - } - } - - for (aiop = 0; aiop < max_num_aiops; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40); - ctlp = sCtlNumToCtlPtr(i); - num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd); - for (aiop = 0; aiop < max_num_aiops; aiop++) - ctlp->AiopNumChan[aiop] = ports_per_aiop; - - dev_info(&dev->dev, "comtrol PCI controller #%d found at " - "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", - i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); - - if (num_aiops <= 0) { - rcktpt_io_addr[i] = 0; - return (0); - } - is_PCI[i] = 1; - - /* Reset the AIOPIC, init the serial ports */ - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - num_chan = ports_per_aiop; - for (chan = 0; chan < num_chan; chan++) - init_r_port(i, aiop, chan, dev); - } - - /* Rocket modems must be reset */ - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMII) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) { - num_chan = ports_per_aiop; - for (chan = 0; chan < num_chan; chan++) - sPCIModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < num_chan; chan++) - sPCIModemReset(ctlp, chan, 0); - msleep(500); - rmSpeakerReset(ctlp, rocketModel[i].model); - } - return (1); -} - -/* - * Probes for PCI cards, inits them if found - * Input: board_found = number of ISA boards already found, or the - * starting board number - * Returns: Number of PCI boards found - */ -static int __init init_PCI(int boards_found) -{ - struct pci_dev *dev = NULL; - int count = 0; - - /* Work through the PCI device list, pulling out ours */ - while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) { - if (register_PCI(count + boards_found, dev)) - count++; - } - return (count); -} - -#endif /* CONFIG_PCI */ - -/* - * Probes for ISA cards - * Input: i = the board number to look for - * Returns: 1 if board found, 0 else - */ -static int __init init_ISA(int i) -{ - int num_aiops, num_chan = 0, total_num_chan = 0; - int aiop, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - CONTROLLER_t *ctlp; - char *type_string; - - /* If io_addr is zero, no board configured */ - if (rcktpt_io_addr[i] == 0) - return (0); - - /* Reserve the IO region */ - if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { - printk(KERN_ERR "Unable to reserve IO region for configured " - "ISA RocketPort at address 0x%lx, board not " - "installed...\n", rcktpt_io_addr[i]); - rcktpt_io_addr[i] = 0; - return (0); - } - - ctlp = sCtlNumToCtlPtr(i); - - ctlp->boardType = rcktpt_type[i]; - - switch (rcktpt_type[i]) { - case ROCKET_TYPE_PC104: - type_string = "(PC104)"; - break; - case ROCKET_TYPE_MODEM: - type_string = "(RocketModem)"; - break; - case ROCKET_TYPE_MODEMII: - type_string = "(RocketModem II)"; - break; - default: - type_string = ""; - break; - } - - /* - * If support_low_speed is set, use the slow clock prescale, - * which supports 50 bps - */ - if (support_low_speed) { - sClockPrescale = 0x19; /* mod 9 (divide by 10) prescale */ - rp_baud_base[i] = 230400; - } else { - sClockPrescale = 0x14; /* mod 4 (devide by 5) prescale */ - rp_baud_base[i] = 460800; - } - - for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400); - - num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); - - if (ctlp->boardType == ROCKET_TYPE_PC104) { - sEnAiop(ctlp, 2); /* only one AIOPIC, but these */ - sEnAiop(ctlp, 3); /* CSels used for other stuff */ - } - - /* If something went wrong initing the AIOP's release the ISA IO memory */ - if (num_aiops <= 0) { - release_region(rcktpt_io_addr[i], 64); - rcktpt_io_addr[i] = 0; - return (0); - } - - rocketModel[i].startingPortNumber = nextLineNumber; - - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - sEnAiop(ctlp, aiop); - num_chan = sGetAiopNumChan(ctlp, aiop); - total_num_chan += num_chan; - for (chan = 0; chan < num_chan; chan++) - init_r_port(i, aiop, chan, NULL); - } - is_PCI[i] = 0; - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) { - num_chan = sGetAiopNumChan(ctlp, 0); - total_num_chan = num_chan; - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 0); - msleep(500); - strcpy(rocketModel[i].modelString, "RocketModem ISA"); - } else { - strcpy(rocketModel[i].modelString, "RocketPort ISA"); - } - rocketModel[i].numPorts = total_num_chan; - rocketModel[i].model = MODEL_ISA; - - printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", - i, rcktpt_io_addr[i], num_aiops, type_string); - - printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", - rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + - rocketModel[i].numPorts - 1); - - return (1); -} - -static const struct tty_operations rocket_ops = { - .open = rp_open, - .close = rp_close, - .write = rp_write, - .put_char = rp_put_char, - .write_room = rp_write_room, - .chars_in_buffer = rp_chars_in_buffer, - .flush_buffer = rp_flush_buffer, - .ioctl = rp_ioctl, - .throttle = rp_throttle, - .unthrottle = rp_unthrottle, - .set_termios = rp_set_termios, - .stop = rp_stop, - .start = rp_start, - .hangup = rp_hangup, - .break_ctl = rp_break, - .send_xchar = rp_send_xchar, - .wait_until_sent = rp_wait_until_sent, - .tiocmget = rp_tiocmget, - .tiocmset = rp_tiocmset, -}; - -static const struct tty_port_operations rocket_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* - * The module "startup" routine; it's run when the module is loaded. - */ -static int __init rp_init(void) -{ - int ret = -ENOMEM, pci_boards_found, isa_boards_found, i; - - printk(KERN_INFO "RocketPort device driver module, version %s, %s\n", - ROCKET_VERSION, ROCKET_DATE); - - rocket_driver = alloc_tty_driver(MAX_RP_PORTS); - if (!rocket_driver) - goto err; - - /* - * If board 1 is non-zero, there is at least one ISA configured. If controller is - * zero, use the default controller IO address of board1 + 0x40. - */ - if (board1) { - if (controller == 0) - controller = board1 + 0x40; - } else { - controller = 0; /* Used as a flag, meaning no ISA boards */ - } - - /* If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */ - if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) { - printk(KERN_ERR "Unable to reserve IO region for first " - "configured ISA RocketPort controller 0x%lx. " - "Driver exiting\n", controller); - ret = -EBUSY; - goto err_tty; - } - - /* Store ISA variable retrieved from command line or .conf file. */ - rcktpt_io_addr[0] = board1; - rcktpt_io_addr[1] = board2; - rcktpt_io_addr[2] = board3; - rcktpt_io_addr[3] = board4; - - rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0]; - rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1]; - rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2]; - rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3]; - - /* - * Set up the tty driver structure and then register this - * driver with the tty layer. - */ - - rocket_driver->owner = THIS_MODULE; - rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV; - rocket_driver->name = "ttyR"; - rocket_driver->driver_name = "Comtrol RocketPort"; - rocket_driver->major = TTY_ROCKET_MAJOR; - rocket_driver->minor_start = 0; - rocket_driver->type = TTY_DRIVER_TYPE_SERIAL; - rocket_driver->subtype = SERIAL_TYPE_NORMAL; - rocket_driver->init_termios = tty_std_termios; - rocket_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - rocket_driver->init_termios.c_ispeed = 9600; - rocket_driver->init_termios.c_ospeed = 9600; -#ifdef ROCKET_SOFT_FLOW - rocket_driver->flags |= TTY_DRIVER_REAL_RAW; -#endif - tty_set_operations(rocket_driver, &rocket_ops); - - ret = tty_register_driver(rocket_driver); - if (ret < 0) { - printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); - goto err_controller; - } - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major); -#endif - - /* - * OK, let's probe each of the controllers looking for boards. Any boards found - * will be initialized here. - */ - isa_boards_found = 0; - pci_boards_found = 0; - - for (i = 0; i < NUM_BOARDS; i++) { - if (init_ISA(i)) - isa_boards_found++; - } - -#ifdef CONFIG_PCI - if (isa_boards_found < NUM_BOARDS) - pci_boards_found = init_PCI(isa_boards_found); -#endif - - max_board = pci_boards_found + isa_boards_found; - - if (max_board == 0) { - printk(KERN_ERR "No rocketport ports found; unloading driver\n"); - ret = -ENXIO; - goto err_ttyu; - } - - return 0; -err_ttyu: - tty_unregister_driver(rocket_driver); -err_controller: - if (controller) - release_region(controller, 4); -err_tty: - put_tty_driver(rocket_driver); -err: - return ret; -} - - -static void rp_cleanup_module(void) -{ - int retval; - int i; - - del_timer_sync(&rocket_timer); - - retval = tty_unregister_driver(rocket_driver); - if (retval) - printk(KERN_ERR "Error %d while trying to unregister " - "rocketport driver\n", -retval); - - for (i = 0; i < MAX_RP_PORTS; i++) - if (rp_table[i]) { - tty_unregister_device(rocket_driver, i); - kfree(rp_table[i]); - } - - put_tty_driver(rocket_driver); - - for (i = 0; i < NUM_BOARDS; i++) { - if (rcktpt_io_addr[i] <= 0 || is_PCI[i]) - continue; - release_region(rcktpt_io_addr[i], 64); - } - if (controller) - release_region(controller, 4); -} - -/*************************************************************************** -Function: sInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t MudbacIO; Mudbac base I/O address. - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, - ByteIO_t * AiopIOList, int AiopIOListSize, - int IRQNum, Byte_t Frequency, int PeriodicOnly) -{ - int i; - ByteIO_t io; - int done; - - CtlP->AiopIntrBits = aiop_intr_bits; - CtlP->AltChanRingIndicator = 0; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isISA; - CtlP->MBaseIO = MudbacIO; - CtlP->MReg1IO = MudbacIO + 1; - CtlP->MReg2IO = MudbacIO + 2; - CtlP->MReg3IO = MudbacIO + 3; -#if 1 - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ -#else - if (sIRQMap[IRQNum] == 0) { /* interrupts globally disabled */ - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ - } else { - CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ - CtlP->MReg3 = Frequency; /* set frequency */ - if (PeriodicOnly) { /* periodic interrupt only */ - CtlP->MReg3 |= PERIODIC_ONLY; - } - } -#endif - sOutB(CtlP->MReg2IO, CtlP->MReg2); - sOutB(CtlP->MReg3IO, CtlP->MReg3); - sControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = done = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */ - sOutB(MudbacIO, (Byte_t) (io >> 6)); /* set up AIOP I/O in MUDBAC */ - if (done) - continue; - sEnAiop(CtlP, i); /* enable the AIOP */ - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - done = 1; /* done looking for AIOPs */ - else { - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - sDisAiop(CtlP, i); /* disable AIOP */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** -Function: sPCIInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd) -{ - int i; - ByteIO_t io; - - CtlP->AltChanRingIndicator = altChanRingIndicator; - CtlP->UPCIRingInd = UPCIRingInd; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isPCI; /* controller release 1 */ - - if (ConfigIO) { - CtlP->isUPCI = 1; - CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; - CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; - CtlP->AiopIntrBits = upci_aiop_intr_bits; - } else { - CtlP->isUPCI = 0; - CtlP->PCIIO = - (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); - CtlP->AiopIntrBits = aiop_intr_bits; - } - - sPCIControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - break; /* done looking for AIOPs */ - - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** -Function: sReadAiopID -Purpose: Read the AIOP idenfication number directly from an AIOP. -Call: sReadAiopID(io) - ByteIO_t io: AIOP base I/O address -Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X - is replace by an identifying number. - Flag AIOPID_NULL if no valid AIOP is found -Warnings: No context switches are allowed while executing this function. - -*/ -static int sReadAiopID(ByteIO_t io) -{ - Byte_t AiopID; /* ID byte from AIOP */ - - sOutB(io + _CMD_REG, RESET_ALL); /* reset AIOP */ - sOutB(io + _CMD_REG, 0x0); - AiopID = sInW(io + _CHN_STAT0) & 0x07; - if (AiopID == 0x06) - return (1); - else /* AIOP does not exist */ - return (-1); -} - -/*************************************************************************** -Function: sReadAiopNumChan -Purpose: Read the number of channels available in an AIOP directly from - an AIOP. -Call: sReadAiopNumChan(io) - WordIO_t io: AIOP base I/O address -Return: int: The number of channels available -Comments: The number of channels is determined by write/reads from identical - offsets within the SRAM address spaces for channels 0 and 4. - If the channel 4 space is mirrored to channel 0 it is a 4 channel - AIOP, otherwise it is an 8 channel. -Warnings: No context switches are allowed while executing this function. -*/ -static int sReadAiopNumChan(WordIO_t io) -{ - Word_t x; - static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 }; - - /* write to chan 0 SRAM */ - out32((DWordIO_t) io + _INDX_ADDR, R); - sOutW(io + _INDX_ADDR, 0); /* read from SRAM, chan 0 */ - x = sInW(io + _INDX_DATA); - sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */ - if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ - return (8); - else - return (4); -} - -/*************************************************************************** -Function: sInitChan -Purpose: Initialization of a channel and channel structure -Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) - CONTROLLER_T *CtlP; Ptr to controller structure - CHANNEL_T *ChP; Ptr to channel structure - int AiopNum; AIOP number within controller - int ChanNum; Channel number within AIOP -Return: int: 1 if initialization succeeded, 0 if it fails because channel - number exceeds number of channels available in AIOP. -Comments: This function must be called before a channel can be used. -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. -*/ -static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, - int ChanNum) -{ - int i; - WordIO_t AiopIO; - WordIO_t ChIOOff; - Byte_t *ChR; - Word_t ChOff; - static Byte_t R[4]; - int brd9600; - - if (ChanNum >= CtlP->AiopNumChan[AiopNum]) - return 0; /* exceeds num chans in AIOP */ - - /* Channel, AIOP, and controller identifiers */ - ChP->CtlP = CtlP; - ChP->ChanID = CtlP->AiopID[AiopNum]; - ChP->AiopNum = AiopNum; - ChP->ChanNum = ChanNum; - - /* Global direct addresses */ - AiopIO = CtlP->AiopIO[AiopNum]; - ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG; - ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN; - ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK; - ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR; - ChP->IndexData = AiopIO + _INDX_DATA; - - /* Channel direct addresses */ - ChIOOff = AiopIO + ChP->ChanNum * 2; - ChP->TxRxData = ChIOOff + _TD0; - ChP->ChanStat = ChIOOff + _CHN_STAT0; - ChP->TxRxCount = ChIOOff + _FIFO_CNT0; - ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0; - - /* Initialize the channel from the RData array */ - for (i = 0; i < RDATASIZE; i += 4) { - R[0] = RData[i]; - R[1] = RData[i + 1] + 0x10 * ChanNum; - R[2] = RData[i + 2]; - R[3] = RData[i + 3]; - out32(ChP->IndexAddr, R); - } - - ChR = ChP->R; - for (i = 0; i < RREGDATASIZE; i += 4) { - ChR[i] = RRegData[i]; - ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum; - ChR[i + 2] = RRegData[i + 2]; - ChR[i + 3] = RRegData[i + 3]; - } - - /* Indexed registers */ - ChOff = (Word_t) ChanNum *0x1000; - - if (sClockPrescale == 0x14) - brd9600 = 47; - else - brd9600 = 23; - - ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD); - ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8); - ChP->BaudDiv[2] = (Byte_t) brd9600; - ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8); - out32(ChP->IndexAddr, ChP->BaudDiv); - - ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL); - ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8); - ChP->TxControl[2] = 0; - ChP->TxControl[3] = 0; - out32(ChP->IndexAddr, ChP->TxControl); - - ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL); - ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8); - ChP->RxControl[2] = 0; - ChP->RxControl[3] = 0; - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS); - ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8); - ChP->TxEnables[2] = 0; - ChP->TxEnables[3] = 0; - out32(ChP->IndexAddr, ChP->TxEnables); - - ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1); - ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8); - ChP->TxCompare[2] = 0; - ChP->TxCompare[3] = 0; - out32(ChP->IndexAddr, ChP->TxCompare); - - ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1); - ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8); - ChP->TxReplace1[2] = 0; - ChP->TxReplace1[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace1); - - ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2); - ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8); - ChP->TxReplace2[2] = 0; - ChP->TxReplace2[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace2); - - ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; - ChP->TxFIFO = ChOff + _TX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; - ChP->RxFIFO = ChOff + _RX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - ChP->TxPrioCnt = ChOff + _TXP_CNT; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt); - sOutB(ChP->IndexData, 0); - ChP->TxPrioPtr = ChOff + _TXP_PNTR; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr); - sOutB(ChP->IndexData, 0); - ChP->TxPrioBuf = ChOff + _TXP_BUF; - sEnRxProcessor(ChP); /* start the Rx processor */ - - return 1; -} - -/*************************************************************************** -Function: sStopRxProcessor -Purpose: Stop the receive processor from processing a channel. -Call: sStopRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure - -Comments: The receive processor can be started again with sStartRxProcessor(). - This function causes the receive processor to skip over the - stopped channel. It does not stop it from processing other channels. - -Warnings: No context switches are allowed while executing this function. - - Do not leave the receive processor stopped for more than one - character time. - - After calling this function a delay of 4 uS is required to ensure - that the receive processor is no longer processing this channel. -*/ -static void sStopRxProcessor(CHANNEL_T * ChP) -{ - Byte_t R[4]; - - R[0] = ChP->R[0]; - R[1] = ChP->R[1]; - R[2] = 0x0a; - R[3] = ChP->R[3]; - out32(ChP->IndexAddr, R); -} - -/*************************************************************************** -Function: sFlushRxFIFO -Purpose: Flush the Rx FIFO -Call: sFlushRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushRxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int RxFIFOEnabled; /* 1 if Rx FIFO enabled */ - - if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ - return; /* don't need to flush */ - - RxFIFOEnabled = 0; - if (ChP->R[0x32] == 0x08) { /* Rx FIFO is enabled */ - RxFIFOEnabled = 1; - sDisRxFIFO(ChP); /* disable it */ - for (i = 0; i < 2000 / 200; i++) /* delay 2 uS to allow proc to disable FIFO */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - } - sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - if (RxFIFOEnabled) - sEnRxFIFO(ChP); /* enable Rx FIFO */ -} - -/*************************************************************************** -Function: sFlushTxFIFO -Purpose: Flush the Tx FIFO -Call: sFlushTxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushTxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int TxEnabled; /* 1 if transmitter enabled */ - - if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ - return; /* don't need to flush */ - - TxEnabled = 0; - if (ChP->TxControl[3] & TX_ENABLE) { - TxEnabled = 1; - sDisTransmit(ChP); /* disable transmitter */ - } - sStopRxProcessor(ChP); /* stop Rx processor */ - for (i = 0; i < 4000 / 200; i++) /* delay 4 uS to allow proc to stop */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - if (TxEnabled) - sEnTransmit(ChP); /* enable transmitter */ - sStartRxProcessor(ChP); /* restart Rx processor */ -} - -/*************************************************************************** -Function: sWriteTxPrioByte -Purpose: Write a byte of priority transmit data to a channel -Call: sWriteTxPrioByte(ChP,Data) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Data; The transmit data byte - -Return: int: 1 if the bytes is successfully written, otherwise 0. - -Comments: The priority byte is transmitted before any data in the Tx FIFO. - -Warnings: No context switches are allowed while executing this function. -*/ -static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data) -{ - Byte_t DWBuf[4]; /* buffer for double word writes */ - Word_t *WordPtr; /* must be far because Win SS != DS */ - register DWordIO_t IndexAddr; - - if (sGetTxCnt(ChP) > 1) { /* write it to Tx priority buffer */ - IndexAddr = ChP->IndexAddr; - sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt); /* get priority buffer status */ - if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */ - return (0); /* nothing sent */ - - WordPtr = (Word_t *) (&DWBuf[0]); - *WordPtr = ChP->TxPrioBuf; /* data byte address */ - - DWBuf[2] = Data; /* data byte value */ - out32(IndexAddr, DWBuf); /* write it out */ - - *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ - - DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ - DWBuf[3] = 0; /* priority buffer pointer */ - out32(IndexAddr, DWBuf); /* write it out */ - } else { /* write it to Tx FIFO */ - - sWriteTxByte(sGetTxRxDataIO(ChP), Data); - } - return (1); /* 1 byte sent */ -} - -/*************************************************************************** -Function: sEnInterrupts -Purpose: Enable one or more interrupts for a channel -Call: sEnInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt enable flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Allow channel interrupt signal to the AIOP's - Interrupt Channel Register. -Return: void -Comments: If an interrupt enable flag is set in Flags, that interrupt will be - enabled. If an interrupt enable flag is not set in Flags, that - interrupt will not be changed. Interrupts can be disabled with - function sDisInterrupts(). - - This function sets the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This allows - this channel's bit to be set in the AIOP's Interrupt Channel Register. - - Interrupts must also be globally enabled before channel interrupts - will be passed on to the host. This is done with function - sEnGlobalInt(). - - In some cases it may be desirable to disable interrupts globally but - enable channel interrupts. This would allow the global interrupt - status register to be used to determine which AIOPs need service. -*/ -static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] |= - ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN); - - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -/*************************************************************************** -Function: sDisInterrupts -Purpose: Disable one or more interrupts for a channel -Call: sDisInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Disable channel interrupt signal to the - AIOP's Interrupt Channel Register. -Return: void -Comments: If an interrupt flag is set in Flags, that interrupt will be - disabled. If an interrupt flag is not set in Flags, that - interrupt will not be changed. Interrupts can be enabled with - function sEnInterrupts(). - - This function clears the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This blocks - this channel's bit from being set in the AIOP's Interrupt Channel - Register. -*/ -static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] &= - ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - out32(ChP->IndexAddr, ChP->RxControl); - ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN); - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode) -{ - sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * ISA bus version - */ -static void sModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - Byte_t val; - - addr = CtlP->AiopIO[0] + 0x400; - val = sInB(CtlP->MReg3IO); - /* if AIOP[1] is not enabled, enable it */ - if ((val & 2) == 0) { - val = sInB(CtlP->MReg2IO); - sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03)); - sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6)); - } - - sEnAiop(CtlP, 1); - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ - sDisAiop(CtlP, 1); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * PCI bus version - */ -static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - - addr = CtlP->AiopIO[0] + 0x40; /* 2nd AIOP */ - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ -} - -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - -/* Returns the line number given the controller (board), aiop and channel number */ -static unsigned char GetLineNumber(int ctrl, int aiop, int ch) -{ - return lineNumbers[(ctrl << 5) | (aiop << 3) | ch]; -} - -/* - * Stores the line number associated with a given controller (board), aiop - * and channel number. - * Returns: The line number assigned - */ -static unsigned char SetLineNumber(int ctrl, int aiop, int ch) -{ - lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++; - return (nextLineNumber - 1); -} diff --git a/drivers/char/rocket.h b/drivers/char/rocket.h deleted file mode 100644 index ec863f35f1a9..000000000000 --- a/drivers/char/rocket.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * rocket.h --- the exported interface of the rocket driver to its configuration program. - * - * Written by Theodore Ts'o, Copyright 1997. - * Copyright 1997 Comtrol Corporation. - * - */ - -/* Model Information Struct */ -typedef struct { - unsigned long model; - char modelString[80]; - unsigned long numPorts; - int loadrm2; - int startingPortNumber; -} rocketModel_t; - -struct rocket_config { - int line; - int flags; - int closing_wait; - int close_delay; - int port; - int reserved[32]; -}; - -struct rocket_ports { - int tty_major; - int callout_major; - rocketModel_t rocketModel[8]; -}; - -struct rocket_version { - char rocket_version[32]; - char rocket_date[32]; - char reserved[64]; -}; - -/* - * Rocketport flags - */ -/*#define ROCKET_CALLOUT_NOHUP 0x00000001 */ -#define ROCKET_FORCE_CD 0x00000002 -#define ROCKET_HUP_NOTIFY 0x00000004 -#define ROCKET_SPLIT_TERMIOS 0x00000008 -#define ROCKET_SPD_MASK 0x00000070 -#define ROCKET_SPD_HI 0x00000010 /* Use 56000 instead of 38400 bps */ -#define ROCKET_SPD_VHI 0x00000020 /* Use 115200 instead of 38400 bps */ -#define ROCKET_SPD_SHI 0x00000030 /* Use 230400 instead of 38400 bps */ -#define ROCKET_SPD_WARP 0x00000040 /* Use 460800 instead of 38400 bps */ -#define ROCKET_SAK 0x00000080 -#define ROCKET_SESSION_LOCKOUT 0x00000100 -#define ROCKET_PGRP_LOCKOUT 0x00000200 -#define ROCKET_RTS_TOGGLE 0x00000400 -#define ROCKET_MODE_MASK 0x00003000 -#define ROCKET_MODE_RS232 0x00000000 -#define ROCKET_MODE_RS485 0x00001000 -#define ROCKET_MODE_RS422 0x00002000 -#define ROCKET_FLAGS 0x00003FFF - -#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged - * users can set or reset */ - -/* - * For closing_wait and closing_wait2 - */ -#define ROCKET_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE -#define ROCKET_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF - -/* - * Rocketport ioctls -- "RP" - */ -#define RCKP_GET_STRUCT 0x00525001 -#define RCKP_GET_CONFIG 0x00525002 -#define RCKP_SET_CONFIG 0x00525003 -#define RCKP_GET_PORTS 0x00525004 -#define RCKP_RESET_RM2 0x00525005 -#define RCKP_GET_VERSION 0x00525006 - -/* Rocketport Models */ -#define MODEL_RP32INTF 0x0001 /* RP 32 port w/external I/F */ -#define MODEL_RP8INTF 0x0002 /* RP 8 port w/external I/F */ -#define MODEL_RP16INTF 0x0003 /* RP 16 port w/external I/F */ -#define MODEL_RP8OCTA 0x0005 /* RP 8 port w/octa cable */ -#define MODEL_RP4QUAD 0x0004 /* RP 4 port w/quad cable */ -#define MODEL_RP8J 0x0006 /* RP 8 port w/RJ11 connectors */ -#define MODEL_RP4J 0x0007 /* RP 4 port w/RJ45 connectors */ -#define MODEL_RP8SNI 0x0008 /* RP 8 port w/ DB78 SNI connector */ -#define MODEL_RP16SNI 0x0009 /* RP 16 port w/ DB78 SNI connector */ -#define MODEL_RPP4 0x000A /* RP Plus 4 port */ -#define MODEL_RPP8 0x000B /* RP Plus 8 port */ -#define MODEL_RP2_232 0x000E /* RP Plus 2 port RS232 */ -#define MODEL_RP2_422 0x000F /* RP Plus 2 port RS232 */ - -/* Rocketmodem II Models */ -#define MODEL_RP6M 0x000C /* RM 6 port */ -#define MODEL_RP4M 0x000D /* RM 4 port */ - -/* Universal PCI boards */ -#define MODEL_UPCI_RP32INTF 0x0801 /* RP UPCI 32 port w/external I/F */ -#define MODEL_UPCI_RP8INTF 0x0802 /* RP UPCI 8 port w/external I/F */ -#define MODEL_UPCI_RP16INTF 0x0803 /* RP UPCI 16 port w/external I/F */ -#define MODEL_UPCI_RP8OCTA 0x0805 /* RP UPCI 8 port w/octa cable */ -#define MODEL_UPCI_RM3_8PORT 0x080C /* RP UPCI Rocketmodem III 8 port */ -#define MODEL_UPCI_RM3_4PORT 0x080C /* RP UPCI Rocketmodem III 4 port */ - -/* Compact PCI 16 port */ -#define MODEL_CPCI_RP16INTF 0x0903 /* RP Compact PCI 16 port w/external I/F */ - -/* All ISA boards */ -#define MODEL_ISA 0x1000 diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h deleted file mode 100644 index 67e0f1e778a2..000000000000 --- a/drivers/char/rocket_int.h +++ /dev/null @@ -1,1214 +0,0 @@ -/* - * rocket_int.h --- internal header file for rocket.c - * - * Written by Theodore Ts'o, Copyright 1997. - * Copyright 1997 Comtrol Corporation. - * - */ - -/* - * Definition of the types in rcktpt_type - */ -#define ROCKET_TYPE_NORMAL 0 -#define ROCKET_TYPE_MODEM 1 -#define ROCKET_TYPE_MODEMII 2 -#define ROCKET_TYPE_MODEMIII 3 -#define ROCKET_TYPE_PC104 4 - -#include - -#include -#include - -typedef unsigned char Byte_t; -typedef unsigned int ByteIO_t; - -typedef unsigned int Word_t; -typedef unsigned int WordIO_t; - -typedef unsigned int DWordIO_t; - -/* - * Note! Normally the Linux I/O macros already take care of - * byte-swapping the I/O instructions. However, all accesses using - * sOutDW aren't really 32-bit accesses, but should be handled in byte - * order. Hence the use of the cpu_to_le32() macro to byte-swap - * things to no-op the byte swapping done by the big-endian outl() - * instruction. - */ - -static inline void sOutB(unsigned short port, unsigned char value) -{ -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value); -#endif - outb_p(value, port); -} - -static inline void sOutW(unsigned short port, unsigned short value) -{ -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value); -#endif - outw_p(value, port); -} - -static inline void out32(unsigned short port, Byte_t *p) -{ - u32 value = get_unaligned_le32(p); -#ifdef ROCKET_DEBUG_IO - printk(KERN_DEBUG "out32(%x, %lx)...\n", port, value); -#endif - outl_p(value, port); -} - -static inline unsigned char sInB(unsigned short port) -{ - return inb_p(port); -} - -static inline unsigned short sInW(unsigned short port) -{ - return inw_p(port); -} - -/* This is used to move arrays of bytes so byte swapping isn't appropriate. */ -#define sOutStrW(port, addr, count) if (count) outsw(port, addr, count) -#define sInStrW(port, addr, count) if (count) insw(port, addr, count) - -#define CTL_SIZE 8 -#define AIOP_CTL_SIZE 4 -#define CHAN_AIOP_SIZE 8 -#define MAX_PORTS_PER_AIOP 8 -#define MAX_AIOPS_PER_BOARD 4 -#define MAX_PORTS_PER_BOARD 32 - -/* Bus type ID */ -#define isISA 0 -#define isPCI 1 -#define isMC 2 - -/* Controller ID numbers */ -#define CTLID_NULL -1 /* no controller exists */ -#define CTLID_0001 0x0001 /* controller release 1 */ - -/* AIOP ID numbers, identifies AIOP type implementing channel */ -#define AIOPID_NULL -1 /* no AIOP or channel exists */ -#define AIOPID_0001 0x0001 /* AIOP release 1 */ - -/************************************************************************ - Global Register Offsets - Direct Access - Fixed values -************************************************************************/ - -#define _CMD_REG 0x38 /* Command Register 8 Write */ -#define _INT_CHAN 0x39 /* Interrupt Channel Register 8 Read */ -#define _INT_MASK 0x3A /* Interrupt Mask Register 8 Read / Write */ -#define _UNUSED 0x3B /* Unused 8 */ -#define _INDX_ADDR 0x3C /* Index Register Address 16 Write */ -#define _INDX_DATA 0x3E /* Index Register Data 8/16 Read / Write */ - -/************************************************************************ - Channel Register Offsets for 1st channel in AIOP - Direct Access -************************************************************************/ -#define _TD0 0x00 /* Transmit Data 16 Write */ -#define _RD0 0x00 /* Receive Data 16 Read */ -#define _CHN_STAT0 0x20 /* Channel Status 8/16 Read / Write */ -#define _FIFO_CNT0 0x10 /* Transmit/Receive FIFO Count 16 Read */ -#define _INT_ID0 0x30 /* Interrupt Identification 8 Read */ - -/************************************************************************ - Tx Control Register Offsets - Indexed - External - Fixed -************************************************************************/ -#define _TX_ENBLS 0x980 /* Tx Processor Enables Register 8 Read / Write */ -#define _TXCMP1 0x988 /* Transmit Compare Value #1 8 Read / Write */ -#define _TXCMP2 0x989 /* Transmit Compare Value #2 8 Read / Write */ -#define _TXREP1B1 0x98A /* Tx Replace Value #1 - Byte 1 8 Read / Write */ -#define _TXREP1B2 0x98B /* Tx Replace Value #1 - Byte 2 8 Read / Write */ -#define _TXREP2 0x98C /* Transmit Replace Value #2 8 Read / Write */ - -/************************************************************************ -Memory Controller Register Offsets - Indexed - External - Fixed -************************************************************************/ -#define _RX_FIFO 0x000 /* Rx FIFO */ -#define _TX_FIFO 0x800 /* Tx FIFO */ -#define _RXF_OUTP 0x990 /* Rx FIFO OUT pointer 16 Read / Write */ -#define _RXF_INP 0x992 /* Rx FIFO IN pointer 16 Read / Write */ -#define _TXF_OUTP 0x994 /* Tx FIFO OUT pointer 8 Read / Write */ -#define _TXF_INP 0x995 /* Tx FIFO IN pointer 8 Read / Write */ -#define _TXP_CNT 0x996 /* Tx Priority Count 8 Read / Write */ -#define _TXP_PNTR 0x997 /* Tx Priority Pointer 8 Read / Write */ - -#define PRI_PEND 0x80 /* Priority data pending (bit7, Tx pri cnt) */ -#define TXFIFO_SIZE 255 /* size of Tx FIFO */ -#define RXFIFO_SIZE 1023 /* size of Rx FIFO */ - -/************************************************************************ -Tx Priority Buffer - Indexed - External - Fixed -************************************************************************/ -#define _TXP_BUF 0x9C0 /* Tx Priority Buffer 32 Bytes Read / Write */ -#define TXP_SIZE 0x20 /* 32 bytes */ - -/************************************************************************ -Channel Register Offsets - Indexed - Internal - Fixed -************************************************************************/ - -#define _TX_CTRL 0xFF0 /* Transmit Control 16 Write */ -#define _RX_CTRL 0xFF2 /* Receive Control 8 Write */ -#define _BAUD 0xFF4 /* Baud Rate 16 Write */ -#define _CLK_PRE 0xFF6 /* Clock Prescaler 8 Write */ - -#define STMBREAK 0x08 /* BREAK */ -#define STMFRAME 0x04 /* framing error */ -#define STMRCVROVR 0x02 /* receiver over run error */ -#define STMPARITY 0x01 /* parity error */ -#define STMERROR (STMBREAK | STMFRAME | STMPARITY) -#define STMBREAKH 0x800 /* BREAK */ -#define STMFRAMEH 0x400 /* framing error */ -#define STMRCVROVRH 0x200 /* receiver over run error */ -#define STMPARITYH 0x100 /* parity error */ -#define STMERRORH (STMBREAKH | STMFRAMEH | STMPARITYH) - -#define CTS_ACT 0x20 /* CTS input asserted */ -#define DSR_ACT 0x10 /* DSR input asserted */ -#define CD_ACT 0x08 /* CD input asserted */ -#define TXFIFOMT 0x04 /* Tx FIFO is empty */ -#define TXSHRMT 0x02 /* Tx shift register is empty */ -#define RDA 0x01 /* Rx data available */ -#define DRAINED (TXFIFOMT | TXSHRMT) /* indicates Tx is drained */ - -#define STATMODE 0x8000 /* status mode enable bit */ -#define RXFOVERFL 0x2000 /* receive FIFO overflow */ -#define RX2MATCH 0x1000 /* receive compare byte 2 match */ -#define RX1MATCH 0x0800 /* receive compare byte 1 match */ -#define RXBREAK 0x0400 /* received BREAK */ -#define RXFRAME 0x0200 /* received framing error */ -#define RXPARITY 0x0100 /* received parity error */ -#define STATERROR (RXBREAK | RXFRAME | RXPARITY) - -#define CTSFC_EN 0x80 /* CTS flow control enable bit */ -#define RTSTOG_EN 0x40 /* RTS toggle enable bit */ -#define TXINT_EN 0x10 /* transmit interrupt enable */ -#define STOP2 0x08 /* enable 2 stop bits (0 = 1 stop) */ -#define PARITY_EN 0x04 /* enable parity (0 = no parity) */ -#define EVEN_PAR 0x02 /* even parity (0 = odd parity) */ -#define DATA8BIT 0x01 /* 8 bit data (0 = 7 bit data) */ - -#define SETBREAK 0x10 /* send break condition (must clear) */ -#define LOCALLOOP 0x08 /* local loopback set for test */ -#define SET_DTR 0x04 /* assert DTR */ -#define SET_RTS 0x02 /* assert RTS */ -#define TX_ENABLE 0x01 /* enable transmitter */ - -#define RTSFC_EN 0x40 /* RTS flow control enable */ -#define RXPROC_EN 0x20 /* receive processor enable */ -#define TRIG_NO 0x00 /* Rx FIFO trigger level 0 (no trigger) */ -#define TRIG_1 0x08 /* trigger level 1 char */ -#define TRIG_1_2 0x10 /* trigger level 1/2 */ -#define TRIG_7_8 0x18 /* trigger level 7/8 */ -#define TRIG_MASK 0x18 /* trigger level mask */ -#define SRCINT_EN 0x04 /* special Rx condition interrupt enable */ -#define RXINT_EN 0x02 /* Rx interrupt enable */ -#define MCINT_EN 0x01 /* modem change interrupt enable */ - -#define RXF_TRIG 0x20 /* Rx FIFO trigger level interrupt */ -#define TXFIFO_MT 0x10 /* Tx FIFO empty interrupt */ -#define SRC_INT 0x08 /* special receive condition interrupt */ -#define DELTA_CD 0x04 /* CD change interrupt */ -#define DELTA_CTS 0x02 /* CTS change interrupt */ -#define DELTA_DSR 0x01 /* DSR change interrupt */ - -#define REP1W2_EN 0x10 /* replace byte 1 with 2 bytes enable */ -#define IGN2_EN 0x08 /* ignore byte 2 enable */ -#define IGN1_EN 0x04 /* ignore byte 1 enable */ -#define COMP2_EN 0x02 /* compare byte 2 enable */ -#define COMP1_EN 0x01 /* compare byte 1 enable */ - -#define RESET_ALL 0x80 /* reset AIOP (all channels) */ -#define TXOVERIDE 0x40 /* Transmit software off override */ -#define RESETUART 0x20 /* reset channel's UART */ -#define RESTXFCNT 0x10 /* reset channel's Tx FIFO count register */ -#define RESRXFCNT 0x08 /* reset channel's Rx FIFO count register */ - -#define INTSTAT0 0x01 /* AIOP 0 interrupt status */ -#define INTSTAT1 0x02 /* AIOP 1 interrupt status */ -#define INTSTAT2 0x04 /* AIOP 2 interrupt status */ -#define INTSTAT3 0x08 /* AIOP 3 interrupt status */ - -#define INTR_EN 0x08 /* allow interrupts to host */ -#define INT_STROB 0x04 /* strobe and clear interrupt line (EOI) */ - -/************************************************************************** - MUDBAC remapped for PCI -**************************************************************************/ - -#define _CFG_INT_PCI 0x40 -#define _PCI_INT_FUNC 0x3A - -#define PCI_STROB 0x2000 /* bit 13 of int aiop register */ -#define INTR_EN_PCI 0x0010 /* allow interrupts to host */ - -/* - * Definitions for Universal PCI board registers - */ -#define _PCI_9030_INT_CTRL 0x4c /* Offsets from BAR1 */ -#define _PCI_9030_GPIO_CTRL 0x54 -#define PCI_INT_CTRL_AIOP 0x0001 -#define PCI_GPIO_CTRL_8PORT 0x4000 -#define _PCI_9030_RING_IND 0xc0 /* Offsets from BAR1 */ - -#define CHAN3_EN 0x08 /* enable AIOP 3 */ -#define CHAN2_EN 0x04 /* enable AIOP 2 */ -#define CHAN1_EN 0x02 /* enable AIOP 1 */ -#define CHAN0_EN 0x01 /* enable AIOP 0 */ -#define FREQ_DIS 0x00 -#define FREQ_274HZ 0x60 -#define FREQ_137HZ 0x50 -#define FREQ_69HZ 0x40 -#define FREQ_34HZ 0x30 -#define FREQ_17HZ 0x20 -#define FREQ_9HZ 0x10 -#define PERIODIC_ONLY 0x80 /* only PERIODIC interrupt */ - -#define CHANINT_EN 0x0100 /* flags to enable/disable channel ints */ - -#define RDATASIZE 72 -#define RREGDATASIZE 52 - -/* - * AIOP interrupt bits for ISA/PCI boards and UPCI boards. - */ -#define AIOP_INTR_BIT_0 0x0001 -#define AIOP_INTR_BIT_1 0x0002 -#define AIOP_INTR_BIT_2 0x0004 -#define AIOP_INTR_BIT_3 0x0008 - -#define AIOP_INTR_BITS ( \ - AIOP_INTR_BIT_0 \ - | AIOP_INTR_BIT_1 \ - | AIOP_INTR_BIT_2 \ - | AIOP_INTR_BIT_3) - -#define UPCI_AIOP_INTR_BIT_0 0x0004 -#define UPCI_AIOP_INTR_BIT_1 0x0020 -#define UPCI_AIOP_INTR_BIT_2 0x0100 -#define UPCI_AIOP_INTR_BIT_3 0x0800 - -#define UPCI_AIOP_INTR_BITS ( \ - UPCI_AIOP_INTR_BIT_0 \ - | UPCI_AIOP_INTR_BIT_1 \ - | UPCI_AIOP_INTR_BIT_2 \ - | UPCI_AIOP_INTR_BIT_3) - -/* Controller level information structure */ -typedef struct { - int CtlID; - int CtlNum; - int BusType; - int boardType; - int isUPCI; - WordIO_t PCIIO; - WordIO_t PCIIO2; - ByteIO_t MBaseIO; - ByteIO_t MReg1IO; - ByteIO_t MReg2IO; - ByteIO_t MReg3IO; - Byte_t MReg2; - Byte_t MReg3; - int NumAiop; - int AltChanRingIndicator; - ByteIO_t UPCIRingInd; - WordIO_t AiopIO[AIOP_CTL_SIZE]; - ByteIO_t AiopIntChanIO[AIOP_CTL_SIZE]; - int AiopID[AIOP_CTL_SIZE]; - int AiopNumChan[AIOP_CTL_SIZE]; - Word_t *AiopIntrBits; -} CONTROLLER_T; - -typedef CONTROLLER_T CONTROLLER_t; - -/* Channel level information structure */ -typedef struct { - CONTROLLER_T *CtlP; - int AiopNum; - int ChanID; - int ChanNum; - int rtsToggle; - - ByteIO_t Cmd; - ByteIO_t IntChan; - ByteIO_t IntMask; - DWordIO_t IndexAddr; - WordIO_t IndexData; - - WordIO_t TxRxData; - WordIO_t ChanStat; - WordIO_t TxRxCount; - ByteIO_t IntID; - - Word_t TxFIFO; - Word_t TxFIFOPtrs; - Word_t RxFIFO; - Word_t RxFIFOPtrs; - Word_t TxPrioCnt; - Word_t TxPrioPtr; - Word_t TxPrioBuf; - - Byte_t R[RREGDATASIZE]; - - Byte_t BaudDiv[4]; - Byte_t TxControl[4]; - Byte_t RxControl[4]; - Byte_t TxEnables[4]; - Byte_t TxCompare[4]; - Byte_t TxReplace1[4]; - Byte_t TxReplace2[4]; -} CHANNEL_T; - -typedef CHANNEL_T CHANNEL_t; -typedef CHANNEL_T *CHANPTR_T; - -#define InterfaceModeRS232 0x00 -#define InterfaceModeRS422 0x08 -#define InterfaceModeRS485 0x10 -#define InterfaceModeRS232T 0x18 - -/*************************************************************************** -Function: sClrBreak -Purpose: Stop sending a transmit BREAK signal -Call: sClrBreak(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrBreak(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~SETBREAK; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrDTR -Purpose: Clr the DTR output -Call: sClrDTR(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrDTR(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~SET_DTR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrRTS -Purpose: Clr the RTS output -Call: sClrRTS(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrRTS(ChP) \ -do { \ - if ((ChP)->rtsToggle) break; \ - (ChP)->TxControl[3] &= ~SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sClrTxXOFF -Purpose: Clear any existing transmit software flow control off condition -Call: sClrTxXOFF(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sClrTxXOFF(ChP) \ -do { \ - sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \ - sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \ -} while (0) - -/*************************************************************************** -Function: sCtlNumToCtlPtr -Purpose: Convert a controller number to controller structure pointer -Call: sCtlNumToCtlPtr(CtlNum) - int CtlNum; Controller number -Return: CONTROLLER_T *: Ptr to controller structure -*/ -#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM] - -/*************************************************************************** -Function: sControllerEOI -Purpose: Strobe the MUDBAC's End Of Interrupt bit. -Call: sControllerEOI(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -*/ -#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB) - -/*************************************************************************** -Function: sPCIControllerEOI -Purpose: Strobe the PCI End Of Interrupt bit. - For the UPCI boards, toggle the AIOP interrupt enable bit - (this was taken from the Windows driver). -Call: sPCIControllerEOI(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -*/ -#define sPCIControllerEOI(CTLP) \ -do { \ - if ((CTLP)->isUPCI) { \ - Word_t w = sInW((CTLP)->PCIIO); \ - sOutW((CTLP)->PCIIO, (w ^ PCI_INT_CTRL_AIOP)); \ - sOutW((CTLP)->PCIIO, w); \ - } \ - else { \ - sOutW((CTLP)->PCIIO, PCI_STROB); \ - } \ -} while (0) - -/*************************************************************************** -Function: sDisAiop -Purpose: Disable I/O access to an AIOP -Call: sDisAiop(CltP) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; Number of AIOP on controller -*/ -#define sDisAiop(CTLP,AIOPNUM) \ -do { \ - (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \ - sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ -} while (0) - -/*************************************************************************** -Function: sDisCTSFlowCtl -Purpose: Disable output flow control using CTS -Call: sDisCTSFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisCTSFlowCtl(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~CTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisIXANY -Purpose: Disable IXANY Software Flow Control -Call: sDisIXANY(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisIXANY(ChP) \ -do { \ - (ChP)->R[0x0e] = 0x86; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ -} while (0) - -/*************************************************************************** -Function: DisParity -Purpose: Disable parity -Call: sDisParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). -*/ -#define sDisParity(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~PARITY_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisRTSToggle -Purpose: Disable RTS toggle -Call: sDisRTSToggle(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisRTSToggle(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~RTSTOG_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ - (ChP)->rtsToggle = 0; \ -} while (0) - -/*************************************************************************** -Function: sDisRxFIFO -Purpose: Disable Rx FIFO -Call: sDisRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisRxFIFO(ChP) \ -do { \ - (ChP)->R[0x32] = 0x0a; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ -} while (0) - -/*************************************************************************** -Function: sDisRxStatusMode -Purpose: Disable the Rx status mode -Call: sDisRxStatusMode(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This takes the channel out of the receive status mode. All - subsequent reads of receive data using sReadRxWord() will return - two data bytes. -*/ -#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0) - -/*************************************************************************** -Function: sDisTransmit -Purpose: Disable transmit -Call: sDisTransmit(ChP) - CHANNEL_T *ChP; Ptr to channel structure - This disables movement of Tx data from the Tx FIFO into the 1 byte - Tx buffer. Therefore there could be up to a 2 byte latency - between the time sDisTransmit() is called and the transmit buffer - and transmit shift register going completely empty. -*/ -#define sDisTransmit(ChP) \ -do { \ - (ChP)->TxControl[3] &= ~TX_ENABLE; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sDisTxSoftFlowCtl -Purpose: Disable Tx Software Flow Control -Call: sDisTxSoftFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sDisTxSoftFlowCtl(ChP) \ -do { \ - (ChP)->R[0x06] = 0x8a; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sEnAiop -Purpose: Enable I/O access to an AIOP -Call: sEnAiop(CltP) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; Number of AIOP on controller -*/ -#define sEnAiop(CTLP,AIOPNUM) \ -do { \ - (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \ - sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ -} while (0) - -/*************************************************************************** -Function: sEnCTSFlowCtl -Purpose: Enable output flow control using CTS -Call: sEnCTSFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnCTSFlowCtl(ChP) \ -do { \ - (ChP)->TxControl[2] |= CTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnIXANY -Purpose: Enable IXANY Software Flow Control -Call: sEnIXANY(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnIXANY(ChP) \ -do { \ - (ChP)->R[0x0e] = 0x21; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ -} while (0) - -/*************************************************************************** -Function: EnParity -Purpose: Enable parity -Call: sEnParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: Before enabling parity odd or even parity should be chosen using - functions sSetOddParity() or sSetEvenParity(). -*/ -#define sEnParity(ChP) \ -do { \ - (ChP)->TxControl[2] |= PARITY_EN; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnRTSToggle -Purpose: Enable RTS toggle -Call: sEnRTSToggle(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function will disable RTS flow control and clear the RTS - line to allow operation of RTS toggle. -*/ -#define sEnRTSToggle(ChP) \ -do { \ - (ChP)->RxControl[2] &= ~RTSFC_EN; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ - (ChP)->TxControl[2] |= RTSTOG_EN; \ - (ChP)->TxControl[3] &= ~SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ - (ChP)->rtsToggle = 1; \ -} while (0) - -/*************************************************************************** -Function: sEnRxFIFO -Purpose: Enable Rx FIFO -Call: sEnRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnRxFIFO(ChP) \ -do { \ - (ChP)->R[0x32] = 0x08; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ -} while (0) - -/*************************************************************************** -Function: sEnRxProcessor -Purpose: Enable the receive processor -Call: sEnRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function is used to start the receive processor. When - the channel is in the reset state the receive processor is not - running. This is done to prevent the receive processor from - executing invalid microcode instructions prior to the - downloading of the microcode. - -Warnings: This function must be called after valid microcode has been - downloaded to the AIOP, and it must not be called before the - microcode has been downloaded. -*/ -#define sEnRxProcessor(ChP) \ -do { \ - (ChP)->RxControl[2] |= RXPROC_EN; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnRxStatusMode -Purpose: Enable the Rx status mode -Call: sEnRxStatusMode(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This places the channel in the receive status mode. All subsequent - reads of receive data using sReadRxWord() will return a data byte - in the low word and a status byte in the high word. - -*/ -#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE) - -/*************************************************************************** -Function: sEnTransmit -Purpose: Enable transmit -Call: sEnTransmit(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnTransmit(ChP) \ -do { \ - (ChP)->TxControl[3] |= TX_ENABLE; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sEnTxSoftFlowCtl -Purpose: Enable Tx Software Flow Control -Call: sEnTxSoftFlowCtl(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sEnTxSoftFlowCtl(ChP) \ -do { \ - (ChP)->R[0x06] = 0xc5; \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sGetAiopIntStatus -Purpose: Get the AIOP interrupt status -Call: sGetAiopIntStatus(CtlP,AiopNum) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; AIOP number -Return: Byte_t: The AIOP interrupt status. Bits 0 through 7 - represent channels 0 through 7 respectively. If a - bit is set that channel is interrupting. -*/ -#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM]) - -/*************************************************************************** -Function: sGetAiopNumChan -Purpose: Get the number of channels supported by an AIOP -Call: sGetAiopNumChan(CtlP,AiopNum) - CONTROLLER_T *CtlP; Ptr to controller structure - int AiopNum; AIOP number -Return: int: The number of channels supported by the AIOP -*/ -#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM] - -/*************************************************************************** -Function: sGetChanIntID -Purpose: Get a channel's interrupt identification byte -Call: sGetChanIntID(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The channel interrupt ID. Can be any - combination of the following flags: - RXF_TRIG: Rx FIFO trigger level interrupt - TXFIFO_MT: Tx FIFO empty interrupt - SRC_INT: Special receive condition interrupt - DELTA_CD: CD change interrupt - DELTA_CTS: CTS change interrupt - DELTA_DSR: DSR change interrupt -*/ -#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR)) - -/*************************************************************************** -Function: sGetChanNum -Purpose: Get the number of a channel within an AIOP -Call: sGetChanNum(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: int: Channel number within AIOP, or NULLCHAN if channel does - not exist. -*/ -#define sGetChanNum(ChP) (ChP)->ChanNum - -/*************************************************************************** -Function: sGetChanStatus -Purpose: Get the channel status -Call: sGetChanStatus(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Word_t: The channel status. Can be any combination of - the following flags: - LOW BYTE FLAGS - CTS_ACT: CTS input asserted - DSR_ACT: DSR input asserted - CD_ACT: CD input asserted - TXFIFOMT: Tx FIFO is empty - TXSHRMT: Tx shift register is empty - RDA: Rx data available - - HIGH BYTE FLAGS - STATMODE: status mode enable bit - RXFOVERFL: receive FIFO overflow - RX2MATCH: receive compare byte 2 match - RX1MATCH: receive compare byte 1 match - RXBREAK: received BREAK - RXFRAME: received framing error - RXPARITY: received parity error -Warnings: This function will clear the high byte flags in the Channel - Status Register. -*/ -#define sGetChanStatus(ChP) sInW((ChP)->ChanStat) - -/*************************************************************************** -Function: sGetChanStatusLo -Purpose: Get the low byte only of the channel status -Call: sGetChanStatusLo(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The channel status low byte. Can be any combination - of the following flags: - CTS_ACT: CTS input asserted - DSR_ACT: DSR input asserted - CD_ACT: CD input asserted - TXFIFOMT: Tx FIFO is empty - TXSHRMT: Tx shift register is empty - RDA: Rx data available -*/ -#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat) - -/********************************************************************** - * Get RI status of channel - * Defined as a function in rocket.c -aes - */ -#if 0 -#define sGetChanRI(ChP) ((ChP)->CtlP->AltChanRingIndicator ? \ - (sInB((ByteIO_t)((ChP)->ChanStat+8)) & DSR_ACT) : \ - (((ChP)->CtlP->boardType == ROCKET_TYPE_PC104) ? \ - (!(sInB((ChP)->CtlP->AiopIO[3]) & sBitMapSetTbl[(ChP)->ChanNum])) : \ - 0)) -#endif - -/*************************************************************************** -Function: sGetControllerIntStatus -Purpose: Get the controller interrupt status -Call: sGetControllerIntStatus(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -Return: Byte_t: The controller interrupt status in the lower 4 - bits. Bits 0 through 3 represent AIOP's 0 - through 3 respectively. If a bit is set that - AIOP is interrupting. Bits 4 through 7 will - always be cleared. -*/ -#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f) - -/*************************************************************************** -Function: sPCIGetControllerIntStatus -Purpose: Get the controller interrupt status -Call: sPCIGetControllerIntStatus(CtlP) - CONTROLLER_T *CtlP; Ptr to controller structure -Return: unsigned char: The controller interrupt status in the lower 4 - bits and bit 4. Bits 0 through 3 represent AIOP's 0 - through 3 respectively. Bit 4 is set if the int - was generated from periodic. If a bit is set the - AIOP is interrupting. -*/ -#define sPCIGetControllerIntStatus(CTLP) \ - ((CTLP)->isUPCI ? \ - (sInW((CTLP)->PCIIO2) & UPCI_AIOP_INTR_BITS) : \ - ((sInW((CTLP)->PCIIO) >> 8) & AIOP_INTR_BITS)) - -/*************************************************************************** - -Function: sGetRxCnt -Purpose: Get the number of data bytes in the Rx FIFO -Call: sGetRxCnt(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: int: The number of data bytes in the Rx FIFO. -Comments: Byte read of count register is required to obtain Rx count. - -*/ -#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount) - -/*************************************************************************** -Function: sGetTxCnt -Purpose: Get the number of data bytes in the Tx FIFO -Call: sGetTxCnt(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: Byte_t: The number of data bytes in the Tx FIFO. -Comments: Byte read of count register is required to obtain Tx count. - -*/ -#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount) - -/***************************************************************************** -Function: sGetTxRxDataIO -Purpose: Get the I/O address of a channel's TxRx Data register -Call: sGetTxRxDataIO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: WordIO_t: I/O address of a channel's TxRx Data register -*/ -#define sGetTxRxDataIO(ChP) (ChP)->TxRxData - -/*************************************************************************** -Function: sInitChanDefaults -Purpose: Initialize a channel structure to it's default state. -Call: sInitChanDefaults(ChP) - CHANNEL_T *ChP; Ptr to the channel structure -Comments: This function must be called once for every channel structure - that exists before any other SSCI calls can be made. - -*/ -#define sInitChanDefaults(ChP) \ -do { \ - (ChP)->CtlP = NULLCTLPTR; \ - (ChP)->AiopNum = NULLAIOP; \ - (ChP)->ChanID = AIOPID_NULL; \ - (ChP)->ChanNum = NULLCHAN; \ -} while (0) - -/*************************************************************************** -Function: sResetAiopByNum -Purpose: Reset the AIOP by number -Call: sResetAiopByNum(CTLP,AIOPNUM) - CONTROLLER_T CTLP; Ptr to controller structure - AIOPNUM; AIOP index -*/ -#define sResetAiopByNum(CTLP,AIOPNUM) \ -do { \ - sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \ - sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \ -} while (0) - -/*************************************************************************** -Function: sSendBreak -Purpose: Send a transmit BREAK signal -Call: sSendBreak(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSendBreak(ChP) \ -do { \ - (ChP)->TxControl[3] |= SETBREAK; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetBaud -Purpose: Set baud rate -Call: sSetBaud(ChP,Divisor) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Divisor; 16 bit baud rate divisor for channel -*/ -#define sSetBaud(ChP,DIVISOR) \ -do { \ - (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \ - (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \ - out32((ChP)->IndexAddr,(ChP)->BaudDiv); \ -} while (0) - -/*************************************************************************** -Function: sSetData7 -Purpose: Set data bits to 7 -Call: sSetData7(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetData7(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~DATA8BIT; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetData8 -Purpose: Set data bits to 8 -Call: sSetData8(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetData8(ChP) \ -do { \ - (ChP)->TxControl[2] |= DATA8BIT; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetDTR -Purpose: Set the DTR output -Call: sSetDTR(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetDTR(ChP) \ -do { \ - (ChP)->TxControl[3] |= SET_DTR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetEvenParity -Purpose: Set even parity -Call: sSetEvenParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: This function has no effect unless parity is enabled with function - sEnParity(). -*/ -#define sSetEvenParity(ChP) \ -do { \ - (ChP)->TxControl[2] |= EVEN_PAR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetOddParity -Purpose: Set odd parity -Call: sSetOddParity(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: Function sSetParity() can be used in place of functions sEnParity(), - sDisParity(), sSetOddParity(), and sSetEvenParity(). - -Warnings: This function has no effect unless parity is enabled with function - sEnParity(). -*/ -#define sSetOddParity(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~EVEN_PAR; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetRTS -Purpose: Set the RTS output -Call: sSetRTS(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetRTS(ChP) \ -do { \ - if ((ChP)->rtsToggle) break; \ - (ChP)->TxControl[3] |= SET_RTS; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetRxTrigger -Purpose: Set the Rx FIFO trigger level -Call: sSetRxProcessor(ChP,Level) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Level; Number of characters in Rx FIFO at which the - interrupt will be generated. Can be any of the following flags: - - TRIG_NO: no trigger - TRIG_1: 1 character in FIFO - TRIG_1_2: FIFO 1/2 full - TRIG_7_8: FIFO 7/8 full -Comments: An interrupt will be generated when the trigger level is reached - only if function sEnInterrupt() has been called with flag - RXINT_EN set. The RXF_TRIG flag in the Interrupt Idenfification - register will be set whenever the trigger level is reached - regardless of the setting of RXINT_EN. - -*/ -#define sSetRxTrigger(ChP,LEVEL) \ -do { \ - (ChP)->RxControl[2] &= ~TRIG_MASK; \ - (ChP)->RxControl[2] |= LEVEL; \ - out32((ChP)->IndexAddr,(ChP)->RxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetStop1 -Purpose: Set stop bits to 1 -Call: sSetStop1(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetStop1(ChP) \ -do { \ - (ChP)->TxControl[2] &= ~STOP2; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetStop2 -Purpose: Set stop bits to 2 -Call: sSetStop2(ChP) - CHANNEL_T *ChP; Ptr to channel structure -*/ -#define sSetStop2(ChP) \ -do { \ - (ChP)->TxControl[2] |= STOP2; \ - out32((ChP)->IndexAddr,(ChP)->TxControl); \ -} while (0) - -/*************************************************************************** -Function: sSetTxXOFFChar -Purpose: Set the Tx XOFF flow control character -Call: sSetTxXOFFChar(ChP,Ch) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Ch; The value to set the Tx XOFF character to -*/ -#define sSetTxXOFFChar(ChP,CH) \ -do { \ - (ChP)->R[0x07] = (CH); \ - out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ -} while (0) - -/*************************************************************************** -Function: sSetTxXONChar -Purpose: Set the Tx XON flow control character -Call: sSetTxXONChar(ChP,Ch) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Ch; The value to set the Tx XON character to -*/ -#define sSetTxXONChar(ChP,CH) \ -do { \ - (ChP)->R[0x0b] = (CH); \ - out32((ChP)->IndexAddr,&(ChP)->R[0x08]); \ -} while (0) - -/*************************************************************************** -Function: sStartRxProcessor -Purpose: Start a channel's receive processor -Call: sStartRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Comments: This function is used to start a Rx processor after it was - stopped with sStopRxProcessor() or sStopSWInFlowCtl(). It - will restart both the Rx processor and software input flow control. - -*/ -#define sStartRxProcessor(ChP) out32((ChP)->IndexAddr,&(ChP)->R[0]) - -/*************************************************************************** -Function: sWriteTxByte -Purpose: Write a transmit data byte to a channel. - ByteIO_t io: Channel transmit register I/O address. This can - be obtained with sGetTxRxDataIO(). - Byte_t Data; The transmit data byte. -Warnings: This function writes the data byte without checking to see if - sMaxTxSize is exceeded in the Tx FIFO. -*/ -#define sWriteTxByte(IO,DATA) sOutB(IO,DATA) - -/* - * Begin Linux specific definitions for the Rocketport driver - * - * This code is Copyright Theodore Ts'o, 1995-1997 - */ - -struct r_port { - int magic; - struct tty_port port; - int line; - int flags; /* Don't yet match the ASY_ flags!! */ - unsigned int board:3; - unsigned int aiop:2; - unsigned int chan:3; - CONTROLLER_t *ctlp; - CHANNEL_t channel; - int intmask; - int xmit_fifo_room; /* room in xmit fifo */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - int cd_status; - int ignore_status_mask; - int read_status_mask; - int cps; - - struct completion close_wait; /* Not yet matching the core */ - spinlock_t slock; - struct mutex write_mtx; -}; - -#define RPORT_MAGIC 0x525001 - -#define NUM_BOARDS 8 -#define MAX_RP_PORTS (32*NUM_BOARDS) - -/* - * The size of the xmit buffer is 1 page, or 4096 bytes - */ -#define XMIT_BUF_SIZE 4096 - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* - * Assigned major numbers for the Comtrol Rocketport - */ -#define TTY_ROCKET_MAJOR 46 -#define CUA_ROCKET_MAJOR 47 - -#ifdef PCI_VENDOR_ID_RP -#undef PCI_VENDOR_ID_RP -#undef PCI_DEVICE_ID_RP8OCTA -#undef PCI_DEVICE_ID_RP8INTF -#undef PCI_DEVICE_ID_RP16INTF -#undef PCI_DEVICE_ID_RP32INTF -#undef PCI_DEVICE_ID_URP8OCTA -#undef PCI_DEVICE_ID_URP8INTF -#undef PCI_DEVICE_ID_URP16INTF -#undef PCI_DEVICE_ID_CRP16INTF -#undef PCI_DEVICE_ID_URP32INTF -#endif - -/* Comtrol PCI Vendor ID */ -#define PCI_VENDOR_ID_RP 0x11fe - -/* Comtrol Device ID's */ -#define PCI_DEVICE_ID_RP32INTF 0x0001 /* Rocketport 32 port w/external I/F */ -#define PCI_DEVICE_ID_RP8INTF 0x0002 /* Rocketport 8 port w/external I/F */ -#define PCI_DEVICE_ID_RP16INTF 0x0003 /* Rocketport 16 port w/external I/F */ -#define PCI_DEVICE_ID_RP4QUAD 0x0004 /* Rocketport 4 port w/quad cable */ -#define PCI_DEVICE_ID_RP8OCTA 0x0005 /* Rocketport 8 port w/octa cable */ -#define PCI_DEVICE_ID_RP8J 0x0006 /* Rocketport 8 port w/RJ11 connectors */ -#define PCI_DEVICE_ID_RP4J 0x0007 /* Rocketport 4 port w/RJ11 connectors */ -#define PCI_DEVICE_ID_RP8SNI 0x0008 /* Rocketport 8 port w/ DB78 SNI (Siemens) connector */ -#define PCI_DEVICE_ID_RP16SNI 0x0009 /* Rocketport 16 port w/ DB78 SNI (Siemens) connector */ -#define PCI_DEVICE_ID_RPP4 0x000A /* Rocketport Plus 4 port */ -#define PCI_DEVICE_ID_RPP8 0x000B /* Rocketport Plus 8 port */ -#define PCI_DEVICE_ID_RP6M 0x000C /* RocketModem 6 port */ -#define PCI_DEVICE_ID_RP4M 0x000D /* RocketModem 4 port */ -#define PCI_DEVICE_ID_RP2_232 0x000E /* Rocketport Plus 2 port RS232 */ -#define PCI_DEVICE_ID_RP2_422 0x000F /* Rocketport Plus 2 port RS422 */ - -/* Universal PCI boards */ -#define PCI_DEVICE_ID_URP32INTF 0x0801 /* Rocketport UPCI 32 port w/external I/F */ -#define PCI_DEVICE_ID_URP8INTF 0x0802 /* Rocketport UPCI 8 port w/external I/F */ -#define PCI_DEVICE_ID_URP16INTF 0x0803 /* Rocketport UPCI 16 port w/external I/F */ -#define PCI_DEVICE_ID_URP8OCTA 0x0805 /* Rocketport UPCI 8 port w/octa cable */ -#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C /* Rocketmodem III 8 port */ -#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D /* Rocketmodem III 4 port */ - -/* Compact PCI device */ -#define PCI_DEVICE_ID_CRP16INTF 0x0903 /* Rocketport Compact PCI 16 port w/external I/F */ - diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c deleted file mode 100644 index 18888d005a0a..000000000000 --- a/drivers/char/synclink.c +++ /dev/null @@ -1,8119 +0,0 @@ -/* - * linux/drivers/char/synclink.c - * - * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $ - * - * Device driver for Microgate SyncLink ISA and PCI - * high speed multiprotocol serial adapters. - * - * written by Paul Fulghum for Microgate Corporation - * paulkf@microgate.com - * - * Microgate and SyncLink are trademarks of Microgate Corporation - * - * Derived from serial.c written by Theodore Ts'o and Linus Torvalds - * - * Original release 01/11/99 - * - * This code is released under the GNU General Public License (GPL) - * - * This driver is primarily intended for use in synchronous - * HDLC mode. Asynchronous mode is also provided. - * - * When operating in synchronous mode, each call to mgsl_write() - * contains exactly one complete HDLC frame. Calling mgsl_put_char - * will start assembling an HDLC frame that will not be sent until - * mgsl_flush_chars or mgsl_write is called. - * - * Synchronous receive data is reported as complete frames. To accomplish - * this, the TTY flip buffer is bypassed (too small to hold largest - * frame and may fragment frames) and the line discipline - * receive entry point is called directly. - * - * This driver has been tested with a slightly modified ppp.c driver - * for synchronous PPP. - * - * 2000/02/16 - * Added interface for syncppp.c driver (an alternate synchronous PPP - * implementation that also supports Cisco HDLC). Each device instance - * registers as a tty device AND a network device (if dosyncppp option - * is set for the device). The functionality is determined by which - * device interface is opened. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(__i386__) -# define BREAKPOINT() asm(" int $3"); -#else -# define BREAKPOINT() { } -#endif - -#define MAX_ISA_DEVICES 10 -#define MAX_PCI_DEVICES 10 -#define MAX_TOTAL_DEVICES 20 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_MODULE)) -#define SYNCLINK_GENERIC_HDLC 1 -#else -#define SYNCLINK_GENERIC_HDLC 0 -#endif - -#define GET_USER(error,value,addr) error = get_user(value,addr) -#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 -#define PUT_USER(error,value,addr) error = put_user(value,addr) -#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 - -#include - -#define RCLRVALUE 0xffff - -static MGSL_PARAMS default_params = { - MGSL_MODE_HDLC, /* unsigned long mode */ - 0, /* unsigned char loopback; */ - HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ - HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ - 0, /* unsigned long clock_speed; */ - 0xff, /* unsigned char addr_filter; */ - HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ - HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ - HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ - 9600, /* unsigned long data_rate; */ - 8, /* unsigned char data_bits; */ - 1, /* unsigned char stop_bits; */ - ASYNC_PARITY_NONE /* unsigned char parity; */ -}; - -#define SHARED_MEM_ADDRESS_SIZE 0x40000 -#define BUFFERLISTSIZE 4096 -#define DMABUFFERSIZE 4096 -#define MAXRXFRAMES 7 - -typedef struct _DMABUFFERENTRY -{ - u32 phys_addr; /* 32-bit flat physical address of data buffer */ - volatile u16 count; /* buffer size/data count */ - volatile u16 status; /* Control/status field */ - volatile u16 rcc; /* character count field */ - u16 reserved; /* padding required by 16C32 */ - u32 link; /* 32-bit flat link to next buffer entry */ - char *virt_addr; /* virtual address of data buffer */ - u32 phys_entry; /* physical address of this buffer entry */ - dma_addr_t dma_addr; -} DMABUFFERENTRY, *DMAPBUFFERENTRY; - -/* The queue of BH actions to be performed */ - -#define BH_RECEIVE 1 -#define BH_TRANSMIT 2 -#define BH_STATUS 4 - -#define IO_PIN_SHUTDOWN_LIMIT 100 - -struct _input_signal_events { - int ri_up; - int ri_down; - int dsr_up; - int dsr_down; - int dcd_up; - int dcd_down; - int cts_up; - int cts_down; -}; - -/* transmit holding buffer definitions*/ -#define MAX_TX_HOLDING_BUFFERS 5 -struct tx_holding_buffer { - int buffer_size; - unsigned char * buffer; -}; - - -/* - * Device instance data structure - */ - -struct mgsl_struct { - int magic; - struct tty_port port; - int line; - int hw_version; - - struct mgsl_icount icount; - - int timeout; - int x_char; /* xon/xoff character */ - u16 read_status_mask; - u16 ignore_status_mask; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - - wait_queue_head_t status_event_wait_q; - wait_queue_head_t event_wait_q; - struct timer_list tx_timer; /* HDLC transmit timeout timer */ - struct mgsl_struct *next_device; /* device list link */ - - spinlock_t irq_spinlock; /* spinlock for synchronizing with ISR */ - struct work_struct task; /* task structure for scheduling bh */ - - u32 EventMask; /* event trigger mask */ - u32 RecordedEvents; /* pending events */ - - u32 max_frame_size; /* as set by device config */ - - u32 pending_bh; - - bool bh_running; /* Protection from multiple */ - int isr_overflow; - bool bh_requested; - - int dcd_chkcount; /* check counts to prevent */ - int cts_chkcount; /* too many IRQs if a signal */ - int dsr_chkcount; /* is floating */ - int ri_chkcount; - - char *buffer_list; /* virtual address of Rx & Tx buffer lists */ - u32 buffer_list_phys; - dma_addr_t buffer_list_dma_addr; - - unsigned int rx_buffer_count; /* count of total allocated Rx buffers */ - DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */ - unsigned int current_rx_buffer; - - int num_tx_dma_buffers; /* number of tx dma frames required */ - int tx_dma_buffers_used; - unsigned int tx_buffer_count; /* count of total allocated Tx buffers */ - DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */ - int start_tx_dma_buffer; /* tx dma buffer to start tx dma operation */ - int current_tx_buffer; /* next tx dma buffer to be loaded */ - - unsigned char *intermediate_rxbuffer; - - int num_tx_holding_buffers; /* number of tx holding buffer allocated */ - int get_tx_holding_index; /* next tx holding buffer for adapter to load */ - int put_tx_holding_index; /* next tx holding buffer to store user request */ - int tx_holding_count; /* number of tx holding buffers waiting */ - struct tx_holding_buffer tx_holding_buffers[MAX_TX_HOLDING_BUFFERS]; - - bool rx_enabled; - bool rx_overflow; - bool rx_rcc_underrun; - - bool tx_enabled; - bool tx_active; - u32 idle_mode; - - u16 cmr_value; - u16 tcsr_value; - - char device_name[25]; /* device instance name */ - - unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ - unsigned char bus; /* expansion bus number (zero based) */ - unsigned char function; /* PCI device number */ - - unsigned int io_base; /* base I/O address of adapter */ - unsigned int io_addr_size; /* size of the I/O address range */ - bool io_addr_requested; /* true if I/O address requested */ - - unsigned int irq_level; /* interrupt level */ - unsigned long irq_flags; - bool irq_requested; /* true if IRQ requested */ - - unsigned int dma_level; /* DMA channel */ - bool dma_requested; /* true if dma channel requested */ - - u16 mbre_bit; - u16 loopback_bits; - u16 usc_idle_mode; - - MGSL_PARAMS params; /* communications parameters */ - - unsigned char serial_signals; /* current serial signal states */ - - bool irq_occurred; /* for diagnostics use */ - unsigned int init_error; /* Initialization startup error (DIAGS) */ - int fDiagnosticsmode; /* Driver in Diagnostic mode? (DIAGS) */ - - u32 last_mem_alloc; - unsigned char* memory_base; /* shared memory address (PCI only) */ - u32 phys_memory_base; - bool shared_mem_requested; - - unsigned char* lcr_base; /* local config registers (PCI only) */ - u32 phys_lcr_base; - u32 lcr_offset; - bool lcr_mem_requested; - - u32 misc_ctrl_value; - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; - bool drop_rts_on_tx_done; - - bool loopmode_insert_requested; - bool loopmode_send_done_requested; - - struct _input_signal_events input_signal_events; - - /* generic HDLC device parts */ - int netcount; - spinlock_t netlock; - -#if SYNCLINK_GENERIC_HDLC - struct net_device *netdev; -#endif -}; - -#define MGSL_MAGIC 0x5401 - -/* - * The size of the serial xmit buffer is 1 page, or 4096 bytes - */ -#ifndef SERIAL_XMIT_SIZE -#define SERIAL_XMIT_SIZE 4096 -#endif - -/* - * These macros define the offsets used in calculating the - * I/O address of the specified USC registers. - */ - - -#define DCPIN 2 /* Bit 1 of I/O address */ -#define SDPIN 4 /* Bit 2 of I/O address */ - -#define DCAR 0 /* DMA command/address register */ -#define CCAR SDPIN /* channel command/address register */ -#define DATAREG DCPIN + SDPIN /* serial data register */ -#define MSBONLY 0x41 -#define LSBONLY 0x40 - -/* - * These macros define the register address (ordinal number) - * used for writing address/value pairs to the USC. - */ - -#define CMR 0x02 /* Channel mode Register */ -#define CCSR 0x04 /* Channel Command/status Register */ -#define CCR 0x06 /* Channel Control Register */ -#define PSR 0x08 /* Port status Register */ -#define PCR 0x0a /* Port Control Register */ -#define TMDR 0x0c /* Test mode Data Register */ -#define TMCR 0x0e /* Test mode Control Register */ -#define CMCR 0x10 /* Clock mode Control Register */ -#define HCR 0x12 /* Hardware Configuration Register */ -#define IVR 0x14 /* Interrupt Vector Register */ -#define IOCR 0x16 /* Input/Output Control Register */ -#define ICR 0x18 /* Interrupt Control Register */ -#define DCCR 0x1a /* Daisy Chain Control Register */ -#define MISR 0x1c /* Misc Interrupt status Register */ -#define SICR 0x1e /* status Interrupt Control Register */ -#define RDR 0x20 /* Receive Data Register */ -#define RMR 0x22 /* Receive mode Register */ -#define RCSR 0x24 /* Receive Command/status Register */ -#define RICR 0x26 /* Receive Interrupt Control Register */ -#define RSR 0x28 /* Receive Sync Register */ -#define RCLR 0x2a /* Receive count Limit Register */ -#define RCCR 0x2c /* Receive Character count Register */ -#define TC0R 0x2e /* Time Constant 0 Register */ -#define TDR 0x30 /* Transmit Data Register */ -#define TMR 0x32 /* Transmit mode Register */ -#define TCSR 0x34 /* Transmit Command/status Register */ -#define TICR 0x36 /* Transmit Interrupt Control Register */ -#define TSR 0x38 /* Transmit Sync Register */ -#define TCLR 0x3a /* Transmit count Limit Register */ -#define TCCR 0x3c /* Transmit Character count Register */ -#define TC1R 0x3e /* Time Constant 1 Register */ - - -/* - * MACRO DEFINITIONS FOR DMA REGISTERS - */ - -#define DCR 0x06 /* DMA Control Register (shared) */ -#define DACR 0x08 /* DMA Array count Register (shared) */ -#define BDCR 0x12 /* Burst/Dwell Control Register (shared) */ -#define DIVR 0x14 /* DMA Interrupt Vector Register (shared) */ -#define DICR 0x18 /* DMA Interrupt Control Register (shared) */ -#define CDIR 0x1a /* Clear DMA Interrupt Register (shared) */ -#define SDIR 0x1c /* Set DMA Interrupt Register (shared) */ - -#define TDMR 0x02 /* Transmit DMA mode Register */ -#define TDIAR 0x1e /* Transmit DMA Interrupt Arm Register */ -#define TBCR 0x2a /* Transmit Byte count Register */ -#define TARL 0x2c /* Transmit Address Register (low) */ -#define TARU 0x2e /* Transmit Address Register (high) */ -#define NTBCR 0x3a /* Next Transmit Byte count Register */ -#define NTARL 0x3c /* Next Transmit Address Register (low) */ -#define NTARU 0x3e /* Next Transmit Address Register (high) */ - -#define RDMR 0x82 /* Receive DMA mode Register (non-shared) */ -#define RDIAR 0x9e /* Receive DMA Interrupt Arm Register */ -#define RBCR 0xaa /* Receive Byte count Register */ -#define RARL 0xac /* Receive Address Register (low) */ -#define RARU 0xae /* Receive Address Register (high) */ -#define NRBCR 0xba /* Next Receive Byte count Register */ -#define NRARL 0xbc /* Next Receive Address Register (low) */ -#define NRARU 0xbe /* Next Receive Address Register (high) */ - - -/* - * MACRO DEFINITIONS FOR MODEM STATUS BITS - */ - -#define MODEMSTATUS_DTR 0x80 -#define MODEMSTATUS_DSR 0x40 -#define MODEMSTATUS_RTS 0x20 -#define MODEMSTATUS_CTS 0x10 -#define MODEMSTATUS_RI 0x04 -#define MODEMSTATUS_DCD 0x01 - - -/* - * Channel Command/Address Register (CCAR) Command Codes - */ - -#define RTCmd_Null 0x0000 -#define RTCmd_ResetHighestIus 0x1000 -#define RTCmd_TriggerChannelLoadDma 0x2000 -#define RTCmd_TriggerRxDma 0x2800 -#define RTCmd_TriggerTxDma 0x3000 -#define RTCmd_TriggerRxAndTxDma 0x3800 -#define RTCmd_PurgeRxFifo 0x4800 -#define RTCmd_PurgeTxFifo 0x5000 -#define RTCmd_PurgeRxAndTxFifo 0x5800 -#define RTCmd_LoadRcc 0x6800 -#define RTCmd_LoadTcc 0x7000 -#define RTCmd_LoadRccAndTcc 0x7800 -#define RTCmd_LoadTC0 0x8800 -#define RTCmd_LoadTC1 0x9000 -#define RTCmd_LoadTC0AndTC1 0x9800 -#define RTCmd_SerialDataLSBFirst 0xa000 -#define RTCmd_SerialDataMSBFirst 0xa800 -#define RTCmd_SelectBigEndian 0xb000 -#define RTCmd_SelectLittleEndian 0xb800 - - -/* - * DMA Command/Address Register (DCAR) Command Codes - */ - -#define DmaCmd_Null 0x0000 -#define DmaCmd_ResetTxChannel 0x1000 -#define DmaCmd_ResetRxChannel 0x1200 -#define DmaCmd_StartTxChannel 0x2000 -#define DmaCmd_StartRxChannel 0x2200 -#define DmaCmd_ContinueTxChannel 0x3000 -#define DmaCmd_ContinueRxChannel 0x3200 -#define DmaCmd_PauseTxChannel 0x4000 -#define DmaCmd_PauseRxChannel 0x4200 -#define DmaCmd_AbortTxChannel 0x5000 -#define DmaCmd_AbortRxChannel 0x5200 -#define DmaCmd_InitTxChannel 0x7000 -#define DmaCmd_InitRxChannel 0x7200 -#define DmaCmd_ResetHighestDmaIus 0x8000 -#define DmaCmd_ResetAllChannels 0x9000 -#define DmaCmd_StartAllChannels 0xa000 -#define DmaCmd_ContinueAllChannels 0xb000 -#define DmaCmd_PauseAllChannels 0xc000 -#define DmaCmd_AbortAllChannels 0xd000 -#define DmaCmd_InitAllChannels 0xf000 - -#define TCmd_Null 0x0000 -#define TCmd_ClearTxCRC 0x2000 -#define TCmd_SelectTicrTtsaData 0x4000 -#define TCmd_SelectTicrTxFifostatus 0x5000 -#define TCmd_SelectTicrIntLevel 0x6000 -#define TCmd_SelectTicrdma_level 0x7000 -#define TCmd_SendFrame 0x8000 -#define TCmd_SendAbort 0x9000 -#define TCmd_EnableDleInsertion 0xc000 -#define TCmd_DisableDleInsertion 0xd000 -#define TCmd_ClearEofEom 0xe000 -#define TCmd_SetEofEom 0xf000 - -#define RCmd_Null 0x0000 -#define RCmd_ClearRxCRC 0x2000 -#define RCmd_EnterHuntmode 0x3000 -#define RCmd_SelectRicrRtsaData 0x4000 -#define RCmd_SelectRicrRxFifostatus 0x5000 -#define RCmd_SelectRicrIntLevel 0x6000 -#define RCmd_SelectRicrdma_level 0x7000 - -/* - * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR) - */ - -#define RECEIVE_STATUS BIT5 -#define RECEIVE_DATA BIT4 -#define TRANSMIT_STATUS BIT3 -#define TRANSMIT_DATA BIT2 -#define IO_PIN BIT1 -#define MISC BIT0 - - -/* - * Receive status Bits in Receive Command/status Register RCSR - */ - -#define RXSTATUS_SHORT_FRAME BIT8 -#define RXSTATUS_CODE_VIOLATION BIT8 -#define RXSTATUS_EXITED_HUNT BIT7 -#define RXSTATUS_IDLE_RECEIVED BIT6 -#define RXSTATUS_BREAK_RECEIVED BIT5 -#define RXSTATUS_ABORT_RECEIVED BIT5 -#define RXSTATUS_RXBOUND BIT4 -#define RXSTATUS_CRC_ERROR BIT3 -#define RXSTATUS_FRAMING_ERROR BIT3 -#define RXSTATUS_ABORT BIT2 -#define RXSTATUS_PARITY_ERROR BIT2 -#define RXSTATUS_OVERRUN BIT1 -#define RXSTATUS_DATA_AVAILABLE BIT0 -#define RXSTATUS_ALL 0x01f6 -#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) ) - -/* - * Values for setting transmit idle mode in - * Transmit Control/status Register (TCSR) - */ -#define IDLEMODE_FLAGS 0x0000 -#define IDLEMODE_ALT_ONE_ZERO 0x0100 -#define IDLEMODE_ZERO 0x0200 -#define IDLEMODE_ONE 0x0300 -#define IDLEMODE_ALT_MARK_SPACE 0x0500 -#define IDLEMODE_SPACE 0x0600 -#define IDLEMODE_MARK 0x0700 -#define IDLEMODE_MASK 0x0700 - -/* - * IUSC revision identifiers - */ -#define IUSC_SL1660 0x4d44 -#define IUSC_PRE_SL1660 0x4553 - -/* - * Transmit status Bits in Transmit Command/status Register (TCSR) - */ - -#define TCSR_PRESERVE 0x0F00 - -#define TCSR_UNDERWAIT BIT11 -#define TXSTATUS_PREAMBLE_SENT BIT7 -#define TXSTATUS_IDLE_SENT BIT6 -#define TXSTATUS_ABORT_SENT BIT5 -#define TXSTATUS_EOF_SENT BIT4 -#define TXSTATUS_EOM_SENT BIT4 -#define TXSTATUS_CRC_SENT BIT3 -#define TXSTATUS_ALL_SENT BIT2 -#define TXSTATUS_UNDERRUN BIT1 -#define TXSTATUS_FIFO_EMPTY BIT0 -#define TXSTATUS_ALL 0x00fa -#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->tcsr_value + ((b) & 0x00FF)) ) - - -#define MISCSTATUS_RXC_LATCHED BIT15 -#define MISCSTATUS_RXC BIT14 -#define MISCSTATUS_TXC_LATCHED BIT13 -#define MISCSTATUS_TXC BIT12 -#define MISCSTATUS_RI_LATCHED BIT11 -#define MISCSTATUS_RI BIT10 -#define MISCSTATUS_DSR_LATCHED BIT9 -#define MISCSTATUS_DSR BIT8 -#define MISCSTATUS_DCD_LATCHED BIT7 -#define MISCSTATUS_DCD BIT6 -#define MISCSTATUS_CTS_LATCHED BIT5 -#define MISCSTATUS_CTS BIT4 -#define MISCSTATUS_RCC_UNDERRUN BIT3 -#define MISCSTATUS_DPLL_NO_SYNC BIT2 -#define MISCSTATUS_BRG1_ZERO BIT1 -#define MISCSTATUS_BRG0_ZERO BIT0 - -#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0)) -#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f)) - -#define SICR_RXC_ACTIVE BIT15 -#define SICR_RXC_INACTIVE BIT14 -#define SICR_RXC (BIT15+BIT14) -#define SICR_TXC_ACTIVE BIT13 -#define SICR_TXC_INACTIVE BIT12 -#define SICR_TXC (BIT13+BIT12) -#define SICR_RI_ACTIVE BIT11 -#define SICR_RI_INACTIVE BIT10 -#define SICR_RI (BIT11+BIT10) -#define SICR_DSR_ACTIVE BIT9 -#define SICR_DSR_INACTIVE BIT8 -#define SICR_DSR (BIT9+BIT8) -#define SICR_DCD_ACTIVE BIT7 -#define SICR_DCD_INACTIVE BIT6 -#define SICR_DCD (BIT7+BIT6) -#define SICR_CTS_ACTIVE BIT5 -#define SICR_CTS_INACTIVE BIT4 -#define SICR_CTS (BIT5+BIT4) -#define SICR_RCC_UNDERFLOW BIT3 -#define SICR_DPLL_NO_SYNC BIT2 -#define SICR_BRG1_ZERO BIT1 -#define SICR_BRG0_ZERO BIT0 - -void usc_DisableMasterIrqBit( struct mgsl_struct *info ); -void usc_EnableMasterIrqBit( struct mgsl_struct *info ); -void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask ); -void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask ); -void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask ); - -#define usc_EnableInterrupts( a, b ) \ - usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) ) - -#define usc_DisableInterrupts( a, b ) \ - usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) ) - -#define usc_EnableMasterIrqBit(a) \ - usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) ) - -#define usc_DisableMasterIrqBit(a) \ - usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) ) - -#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) ) - -/* - * Transmit status Bits in Transmit Control status Register (TCSR) - * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) - */ - -#define TXSTATUS_PREAMBLE_SENT BIT7 -#define TXSTATUS_IDLE_SENT BIT6 -#define TXSTATUS_ABORT_SENT BIT5 -#define TXSTATUS_EOF BIT4 -#define TXSTATUS_CRC_SENT BIT3 -#define TXSTATUS_ALL_SENT BIT2 -#define TXSTATUS_UNDERRUN BIT1 -#define TXSTATUS_FIFO_EMPTY BIT0 - -#define DICR_MASTER BIT15 -#define DICR_TRANSMIT BIT0 -#define DICR_RECEIVE BIT1 - -#define usc_EnableDmaInterrupts(a,b) \ - usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) ) - -#define usc_DisableDmaInterrupts(a,b) \ - usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) ) - -#define usc_EnableStatusIrqs(a,b) \ - usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) ) - -#define usc_DisablestatusIrqs(a,b) \ - usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) ) - -/* Transmit status Bits in Transmit Control status Register (TCSR) */ -/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */ - - -#define DISABLE_UNCONDITIONAL 0 -#define DISABLE_END_OF_FRAME 1 -#define ENABLE_UNCONDITIONAL 2 -#define ENABLE_AUTO_CTS 3 -#define ENABLE_AUTO_DCD 3 -#define usc_EnableTransmitter(a,b) \ - usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) ) -#define usc_EnableReceiver(a,b) \ - usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) ) - -static u16 usc_InDmaReg( struct mgsl_struct *info, u16 Port ); -static void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value ); -static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ); - -static u16 usc_InReg( struct mgsl_struct *info, u16 Port ); -static void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value ); -static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ); -void usc_RCmd( struct mgsl_struct *info, u16 Cmd ); -void usc_TCmd( struct mgsl_struct *info, u16 Cmd ); - -#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->tcsr_value + (b))) -#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b)) - -#define usc_SetTransmitSyncChars(a,s0,s1) usc_OutReg((a), TSR, (u16)(((u16)s0<<8)|(u16)s1)) - -static void usc_process_rxoverrun_sync( struct mgsl_struct *info ); -static void usc_start_receiver( struct mgsl_struct *info ); -static void usc_stop_receiver( struct mgsl_struct *info ); - -static void usc_start_transmitter( struct mgsl_struct *info ); -static void usc_stop_transmitter( struct mgsl_struct *info ); -static void usc_set_txidle( struct mgsl_struct *info ); -static void usc_load_txfifo( struct mgsl_struct *info ); - -static void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate ); -static void usc_enable_loopback( struct mgsl_struct *info, int enable ); - -static void usc_get_serial_signals( struct mgsl_struct *info ); -static void usc_set_serial_signals( struct mgsl_struct *info ); - -static void usc_reset( struct mgsl_struct *info ); - -static void usc_set_sync_mode( struct mgsl_struct *info ); -static void usc_set_sdlc_mode( struct mgsl_struct *info ); -static void usc_set_async_mode( struct mgsl_struct *info ); -static void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate ); - -static void usc_loopback_frame( struct mgsl_struct *info ); - -static void mgsl_tx_timeout(unsigned long context); - - -static void usc_loopmode_cancel_transmit( struct mgsl_struct * info ); -static void usc_loopmode_insert_request( struct mgsl_struct * info ); -static int usc_loopmode_active( struct mgsl_struct * info); -static void usc_loopmode_send_done( struct mgsl_struct * info ); - -static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg); - -#if SYNCLINK_GENERIC_HDLC -#define dev_to_port(D) (dev_to_hdlc(D)->priv) -static void hdlcdev_tx_done(struct mgsl_struct *info); -static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size); -static int hdlcdev_init(struct mgsl_struct *info); -static void hdlcdev_exit(struct mgsl_struct *info); -#endif - -/* - * Defines a BUS descriptor value for the PCI adapter - * local bus address ranges. - */ - -#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \ -(0x00400020 + \ -((WrHold) << 30) + \ -((WrDly) << 28) + \ -((RdDly) << 26) + \ -((Nwdd) << 20) + \ -((Nwad) << 15) + \ -((Nxda) << 13) + \ -((Nrdd) << 11) + \ -((Nrad) << 6) ) - -static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit); - -/* - * Adapter diagnostic routines - */ -static bool mgsl_register_test( struct mgsl_struct *info ); -static bool mgsl_irq_test( struct mgsl_struct *info ); -static bool mgsl_dma_test( struct mgsl_struct *info ); -static bool mgsl_memory_test( struct mgsl_struct *info ); -static int mgsl_adapter_test( struct mgsl_struct *info ); - -/* - * device and resource management routines - */ -static int mgsl_claim_resources(struct mgsl_struct *info); -static void mgsl_release_resources(struct mgsl_struct *info); -static void mgsl_add_device(struct mgsl_struct *info); -static struct mgsl_struct* mgsl_allocate_device(void); - -/* - * DMA buffer manupulation functions. - */ -static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ); -static bool mgsl_get_rx_frame( struct mgsl_struct *info ); -static bool mgsl_get_raw_rx_frame( struct mgsl_struct *info ); -static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ); -static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info ); -static int num_free_tx_dma_buffers(struct mgsl_struct *info); -static void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize); -static void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count); - -/* - * DMA and Shared Memory buffer allocation and formatting - */ -static int mgsl_allocate_dma_buffers(struct mgsl_struct *info); -static void mgsl_free_dma_buffers(struct mgsl_struct *info); -static int mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); -static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); -static int mgsl_alloc_buffer_list_memory(struct mgsl_struct *info); -static void mgsl_free_buffer_list_memory(struct mgsl_struct *info); -static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info); -static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info); -static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info); -static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info); -static bool load_next_tx_holding_buffer(struct mgsl_struct *info); -static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize); - -/* - * Bottom half interrupt handlers - */ -static void mgsl_bh_handler(struct work_struct *work); -static void mgsl_bh_receive(struct mgsl_struct *info); -static void mgsl_bh_transmit(struct mgsl_struct *info); -static void mgsl_bh_status(struct mgsl_struct *info); - -/* - * Interrupt handler routines and dispatch table. - */ -static void mgsl_isr_null( struct mgsl_struct *info ); -static void mgsl_isr_transmit_data( struct mgsl_struct *info ); -static void mgsl_isr_receive_data( struct mgsl_struct *info ); -static void mgsl_isr_receive_status( struct mgsl_struct *info ); -static void mgsl_isr_transmit_status( struct mgsl_struct *info ); -static void mgsl_isr_io_pin( struct mgsl_struct *info ); -static void mgsl_isr_misc( struct mgsl_struct *info ); -static void mgsl_isr_receive_dma( struct mgsl_struct *info ); -static void mgsl_isr_transmit_dma( struct mgsl_struct *info ); - -typedef void (*isr_dispatch_func)(struct mgsl_struct *); - -static isr_dispatch_func UscIsrTable[7] = -{ - mgsl_isr_null, - mgsl_isr_misc, - mgsl_isr_io_pin, - mgsl_isr_transmit_data, - mgsl_isr_transmit_status, - mgsl_isr_receive_data, - mgsl_isr_receive_status -}; - -/* - * ioctl call handlers - */ -static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount - __user *user_icount); -static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params); -static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params); -static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode); -static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode); -static int mgsl_txenable(struct mgsl_struct * info, int enable); -static int mgsl_txabort(struct mgsl_struct * info); -static int mgsl_rxenable(struct mgsl_struct * info, int enable); -static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask); -static int mgsl_loopmode_send_done( struct mgsl_struct * info ); - -/* set non-zero on successful registration with PCI subsystem */ -static bool pci_registered; - -/* - * Global linked list of SyncLink devices - */ -static struct mgsl_struct *mgsl_device_list; -static int mgsl_device_count; - -/* - * Set this param to non-zero to load eax with the - * .text section address and breakpoint on module load. - * This is useful for use with gdb and add-symbol-file command. - */ -static int break_on_load; - -/* - * Driver major number, defaults to zero to get auto - * assigned major number. May be forced as module parameter. - */ -static int ttymajor; - -/* - * Array of user specified options for ISA adapters. - */ -static int io[MAX_ISA_DEVICES]; -static int irq[MAX_ISA_DEVICES]; -static int dma[MAX_ISA_DEVICES]; -static int debug_level; -static int maxframe[MAX_TOTAL_DEVICES]; -static int txdmabufs[MAX_TOTAL_DEVICES]; -static int txholdbufs[MAX_TOTAL_DEVICES]; - -module_param(break_on_load, bool, 0); -module_param(ttymajor, int, 0); -module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param_array(dma, int, NULL, 0); -module_param(debug_level, int, 0); -module_param_array(maxframe, int, NULL, 0); -module_param_array(txdmabufs, int, NULL, 0); -module_param_array(txholdbufs, int, NULL, 0); - -static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "$Revision: 4.38 $"; - -static int synclink_init_one (struct pci_dev *dev, - const struct pci_device_id *ent); -static void synclink_remove_one (struct pci_dev *dev); - -static struct pci_device_id synclink_pci_tbl[] = { - { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_USC, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_MICROGATE, 0x0210, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, synclink_pci_tbl); - -MODULE_LICENSE("GPL"); - -static struct pci_driver synclink_pci_driver = { - .name = "synclink", - .id_table = synclink_pci_tbl, - .probe = synclink_init_one, - .remove = __devexit_p(synclink_remove_one), -}; - -static struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - - -static void mgsl_change_params(struct mgsl_struct *info); -static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout); - -/* - * 1st function defined in .text section. Calling this function in - * init_module() followed by a breakpoint allows a remote debugger - * (gdb) to get the .text address for the add-symbol-file command. - * This allows remote debugging of dynamically loadable modules. - */ -static void* mgsl_get_text_ptr(void) -{ - return mgsl_get_text_ptr; -} - -static inline int mgsl_paranoia_check(struct mgsl_struct *info, - char *name, const char *routine) -{ -#ifdef MGSL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for mgsl struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null mgsl_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != MGSL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#else - if (!info) - return 1; -#endif - return 0; -} - -/** - * line discipline callback wrappers - * - * The wrappers maintain line discipline references - * while calling into the line discipline. - * - * ldisc_receive_buf - pass receive data to line discipline - */ - -static void ldisc_receive_buf(struct tty_struct *tty, - const __u8 *data, char *flags, int count) -{ - struct tty_ldisc *ld; - if (!tty) - return; - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, data, flags, count); - tty_ldisc_deref(ld); - } -} - -/* mgsl_stop() throttle (stop) transmitter - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static void mgsl_stop(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (mgsl_paranoia_check(info, tty->name, "mgsl_stop")) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("mgsl_stop(%s)\n",info->device_name); - - spin_lock_irqsave(&info->irq_spinlock,flags); - if (info->tx_enabled) - usc_stop_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - -} /* end of mgsl_stop() */ - -/* mgsl_start() release (start) transmitter - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static void mgsl_start(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (mgsl_paranoia_check(info, tty->name, "mgsl_start")) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("mgsl_start(%s)\n",info->device_name); - - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!info->tx_enabled) - usc_start_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - -} /* end of mgsl_start() */ - -/* - * Bottom half work queue access functions - */ - -/* mgsl_bh_action() Return next bottom half action to perform. - * Return Value: BH action code or 0 if nothing to do. - */ -static int mgsl_bh_action(struct mgsl_struct *info) -{ - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&info->irq_spinlock,flags); - - if (info->pending_bh & BH_RECEIVE) { - info->pending_bh &= ~BH_RECEIVE; - rc = BH_RECEIVE; - } else if (info->pending_bh & BH_TRANSMIT) { - info->pending_bh &= ~BH_TRANSMIT; - rc = BH_TRANSMIT; - } else if (info->pending_bh & BH_STATUS) { - info->pending_bh &= ~BH_STATUS; - rc = BH_STATUS; - } - - if (!rc) { - /* Mark BH routine as complete */ - info->bh_running = false; - info->bh_requested = false; - } - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return rc; -} - -/* - * Perform bottom half processing of work items queued by ISR. - */ -static void mgsl_bh_handler(struct work_struct *work) -{ - struct mgsl_struct *info = - container_of(work, struct mgsl_struct, task); - int action; - - if (!info) - return; - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_handler(%s) entry\n", - __FILE__,__LINE__,info->device_name); - - info->bh_running = true; - - while((action = mgsl_bh_action(info)) != 0) { - - /* Process work item */ - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_handler() work item action=%d\n", - __FILE__,__LINE__,action); - - switch (action) { - - case BH_RECEIVE: - mgsl_bh_receive(info); - break; - case BH_TRANSMIT: - mgsl_bh_transmit(info); - break; - case BH_STATUS: - mgsl_bh_status(info); - break; - default: - /* unknown work item ID */ - printk("Unknown work item ID=%08X!\n", action); - break; - } - } - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_handler(%s) exit\n", - __FILE__,__LINE__,info->device_name); -} - -static void mgsl_bh_receive(struct mgsl_struct *info) -{ - bool (*get_rx_frame)(struct mgsl_struct *info) = - (info->params.mode == MGSL_MODE_HDLC ? mgsl_get_rx_frame : mgsl_get_raw_rx_frame); - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_receive(%s)\n", - __FILE__,__LINE__,info->device_name); - - do - { - if (info->rx_rcc_underrun) { - unsigned long flags; - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_start_receiver(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return; - } - } while(get_rx_frame(info)); -} - -static void mgsl_bh_transmit(struct mgsl_struct *info) -{ - struct tty_struct *tty = info->port.tty; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_transmit() entry on %s\n", - __FILE__,__LINE__,info->device_name); - - if (tty) - tty_wakeup(tty); - - /* if transmitter idle and loopmode_send_done_requested - * then start echoing RxD to TxD - */ - spin_lock_irqsave(&info->irq_spinlock,flags); - if ( !info->tx_active && info->loopmode_send_done_requested ) - usc_loopmode_send_done( info ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); -} - -static void mgsl_bh_status(struct mgsl_struct *info) -{ - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):mgsl_bh_status() entry on %s\n", - __FILE__,__LINE__,info->device_name); - - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - info->dcd_chkcount = 0; - info->cts_chkcount = 0; -} - -/* mgsl_isr_receive_status() - * - * Service a receive status interrupt. The type of status - * interrupt is indicated by the state of the RCSR. - * This is only used for HDLC mode. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_receive_status( struct mgsl_struct *info ) -{ - u16 status = usc_InReg( info, RCSR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_receive_status status=%04X\n", - __FILE__,__LINE__,status); - - if ( (status & RXSTATUS_ABORT_RECEIVED) && - info->loopmode_insert_requested && - usc_loopmode_active(info) ) - { - ++info->icount.rxabort; - info->loopmode_insert_requested = false; - - /* clear CMR:13 to start echoing RxD to TxD */ - info->cmr_value &= ~BIT13; - usc_OutReg(info, CMR, info->cmr_value); - - /* disable received abort irq (no longer required) */ - usc_OutReg(info, RICR, - (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); - } - - if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { - if (status & RXSTATUS_EXITED_HUNT) - info->icount.exithunt++; - if (status & RXSTATUS_IDLE_RECEIVED) - info->icount.rxidle++; - wake_up_interruptible(&info->event_wait_q); - } - - if (status & RXSTATUS_OVERRUN){ - info->icount.rxover++; - usc_process_rxoverrun_sync( info ); - } - - usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); - usc_UnlatchRxstatusBits( info, status ); - -} /* end of mgsl_isr_receive_status() */ - -/* mgsl_isr_transmit_status() - * - * Service a transmit status interrupt - * HDLC mode :end of transmit frame - * Async mode:all data is sent - * transmit status is indicated by bits in the TCSR. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_transmit_status( struct mgsl_struct *info ) -{ - u16 status = usc_InReg( info, TCSR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_transmit_status status=%04X\n", - __FILE__,__LINE__,status); - - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); - usc_UnlatchTxstatusBits( info, status ); - - if ( status & (TXSTATUS_UNDERRUN | TXSTATUS_ABORT_SENT) ) - { - /* finished sending HDLC abort. This may leave */ - /* the TxFifo with data from the aborted frame */ - /* so purge the TxFifo. Also shutdown the DMA */ - /* channel in case there is data remaining in */ - /* the DMA buffer */ - usc_DmaCmd( info, DmaCmd_ResetTxChannel ); - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - } - - if ( status & TXSTATUS_EOF_SENT ) - info->icount.txok++; - else if ( status & TXSTATUS_UNDERRUN ) - info->icount.txunder++; - else if ( status & TXSTATUS_ABORT_SENT ) - info->icount.txabort++; - else - info->icount.txunder++; - - info->tx_active = false; - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - del_timer(&info->tx_timer); - - if ( info->drop_rts_on_tx_done ) { - usc_get_serial_signals( info ); - if ( info->serial_signals & SerialSignal_RTS ) { - info->serial_signals &= ~SerialSignal_RTS; - usc_set_serial_signals( info ); - } - info->drop_rts_on_tx_done = false; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - if (info->port.tty->stopped || info->port.tty->hw_stopped) { - usc_stop_transmitter(info); - return; - } - info->pending_bh |= BH_TRANSMIT; - } - -} /* end of mgsl_isr_transmit_status() */ - -/* mgsl_isr_io_pin() - * - * Service an Input/Output pin interrupt. The type of - * interrupt is indicated by bits in the MISR - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_io_pin( struct mgsl_struct *info ) -{ - struct mgsl_icount *icount; - u16 status = usc_InReg( info, MISR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_io_pin status=%04X\n", - __FILE__,__LINE__,status); - - usc_ClearIrqPendingBits( info, IO_PIN ); - usc_UnlatchIostatusBits( info, status ); - - if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | - MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { - icount = &info->icount; - /* update input line counters */ - if (status & MISCSTATUS_RI_LATCHED) { - if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - usc_DisablestatusIrqs(info,SICR_RI); - icount->rng++; - if ( status & MISCSTATUS_RI ) - info->input_signal_events.ri_up++; - else - info->input_signal_events.ri_down++; - } - if (status & MISCSTATUS_DSR_LATCHED) { - if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - usc_DisablestatusIrqs(info,SICR_DSR); - icount->dsr++; - if ( status & MISCSTATUS_DSR ) - info->input_signal_events.dsr_up++; - else - info->input_signal_events.dsr_down++; - } - if (status & MISCSTATUS_DCD_LATCHED) { - if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - usc_DisablestatusIrqs(info,SICR_DCD); - icount->dcd++; - if (status & MISCSTATUS_DCD) { - info->input_signal_events.dcd_up++; - } else - info->input_signal_events.dcd_down++; -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) { - if (status & MISCSTATUS_DCD) - netif_carrier_on(info->netdev); - else - netif_carrier_off(info->netdev); - } -#endif - } - if (status & MISCSTATUS_CTS_LATCHED) - { - if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) - usc_DisablestatusIrqs(info,SICR_CTS); - icount->cts++; - if ( status & MISCSTATUS_CTS ) - info->input_signal_events.cts_up++; - else - info->input_signal_events.cts_down++; - } - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - if ( (info->port.flags & ASYNC_CHECK_CD) && - (status & MISCSTATUS_DCD_LATCHED) ) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s CD now %s...", info->device_name, - (status & MISCSTATUS_DCD) ? "on" : "off"); - if (status & MISCSTATUS_DCD) - wake_up_interruptible(&info->port.open_wait); - else { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("doing serial hangup..."); - if (info->port.tty) - tty_hangup(info->port.tty); - } - } - - if ( (info->port.flags & ASYNC_CTS_FLOW) && - (status & MISCSTATUS_CTS_LATCHED) ) { - if (info->port.tty->hw_stopped) { - if (status & MISCSTATUS_CTS) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("CTS tx start..."); - if (info->port.tty) - info->port.tty->hw_stopped = 0; - usc_start_transmitter(info); - info->pending_bh |= BH_TRANSMIT; - return; - } - } else { - if (!(status & MISCSTATUS_CTS)) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("CTS tx stop..."); - if (info->port.tty) - info->port.tty->hw_stopped = 1; - usc_stop_transmitter(info); - } - } - } - } - - info->pending_bh |= BH_STATUS; - - /* for diagnostics set IRQ flag */ - if ( status & MISCSTATUS_TXC_LATCHED ){ - usc_OutReg( info, SICR, - (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) ); - usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED ); - info->irq_occurred = true; - } - -} /* end of mgsl_isr_io_pin() */ - -/* mgsl_isr_transmit_data() - * - * Service a transmit data interrupt (async mode only). - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_transmit_data( struct mgsl_struct *info ) -{ - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n", - __FILE__,__LINE__,info->xmit_cnt); - - usc_ClearIrqPendingBits( info, TRANSMIT_DATA ); - - if (info->port.tty->stopped || info->port.tty->hw_stopped) { - usc_stop_transmitter(info); - return; - } - - if ( info->xmit_cnt ) - usc_load_txfifo( info ); - else - info->tx_active = false; - - if (info->xmit_cnt < WAKEUP_CHARS) - info->pending_bh |= BH_TRANSMIT; - -} /* end of mgsl_isr_transmit_data() */ - -/* mgsl_isr_receive_data() - * - * Service a receive data interrupt. This occurs - * when operating in asynchronous interrupt transfer mode. - * The receive data FIFO is flushed to the receive data buffers. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_receive_data( struct mgsl_struct *info ) -{ - int Fifocount; - u16 status; - int work = 0; - unsigned char DataByte; - struct tty_struct *tty = info->port.tty; - struct mgsl_icount *icount = &info->icount; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_receive_data\n", - __FILE__,__LINE__); - - usc_ClearIrqPendingBits( info, RECEIVE_DATA ); - - /* select FIFO status for RICR readback */ - usc_RCmd( info, RCmd_SelectRicrRxFifostatus ); - - /* clear the Wordstatus bit so that status readback */ - /* only reflects the status of this byte */ - usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 )); - - /* flush the receive FIFO */ - - while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) { - int flag; - - /* read one byte from RxFIFO */ - outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY), - info->io_base + CCAR ); - DataByte = inb( info->io_base + CCAR ); - - /* get the status of the received byte */ - status = usc_InReg(info, RCSR); - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) - usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); - - icount->rx++; - - flag = 0; - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { - printk("rxerr=%04X\n",status); - /* update error statistics */ - if ( status & RXSTATUS_BREAK_RECEIVED ) { - status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); - icount->brk++; - } else if (status & RXSTATUS_PARITY_ERROR) - icount->parity++; - else if (status & RXSTATUS_FRAMING_ERROR) - icount->frame++; - else if (status & RXSTATUS_OVERRUN) { - /* must issue purge fifo cmd before */ - /* 16C32 accepts more receive chars */ - usc_RTCmd(info,RTCmd_PurgeRxFifo); - icount->overrun++; - } - - /* discard char if tty control flags say so */ - if (status & info->ignore_status_mask) - continue; - - status &= info->read_status_mask; - - if (status & RXSTATUS_BREAK_RECEIVED) { - flag = TTY_BREAK; - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); - } else if (status & RXSTATUS_PARITY_ERROR) - flag = TTY_PARITY; - else if (status & RXSTATUS_FRAMING_ERROR) - flag = TTY_FRAME; - } /* end of if (error) */ - tty_insert_flip_char(tty, DataByte, flag); - if (status & RXSTATUS_OVERRUN) { - /* Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - work += tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - } - - if ( debug_level >= DEBUG_LEVEL_ISR ) { - printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n", - __FILE__,__LINE__,icount->rx,icount->brk, - icount->parity,icount->frame,icount->overrun); - } - - if(work) - tty_flip_buffer_push(tty); -} - -/* mgsl_isr_misc() - * - * Service a miscellaneous interrupt source. - * - * Arguments: info pointer to device extension (instance data) - * Return Value: None - */ -static void mgsl_isr_misc( struct mgsl_struct *info ) -{ - u16 status = usc_InReg( info, MISR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_misc status=%04X\n", - __FILE__,__LINE__,status); - - if ((status & MISCSTATUS_RCC_UNDERRUN) && - (info->params.mode == MGSL_MODE_HDLC)) { - - /* turn off receiver and rx DMA */ - usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); - usc_DmaCmd(info, DmaCmd_ResetRxChannel); - usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); - usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS); - - /* schedule BH handler to restart receiver */ - info->pending_bh |= BH_RECEIVE; - info->rx_rcc_underrun = true; - } - - usc_ClearIrqPendingBits( info, MISC ); - usc_UnlatchMiscstatusBits( info, status ); - -} /* end of mgsl_isr_misc() */ - -/* mgsl_isr_null() - * - * Services undefined interrupt vectors from the - * USC. (hence this function SHOULD never be called) - * - * Arguments: info pointer to device extension (instance data) - * Return Value: None - */ -static void mgsl_isr_null( struct mgsl_struct *info ) -{ - -} /* end of mgsl_isr_null() */ - -/* mgsl_isr_receive_dma() - * - * Service a receive DMA channel interrupt. - * For this driver there are two sources of receive DMA interrupts - * as identified in the Receive DMA mode Register (RDMR): - * - * BIT3 EOA/EOL End of List, all receive buffers in receive - * buffer list have been filled (no more free buffers - * available). The DMA controller has shut down. - * - * BIT2 EOB End of Buffer. This interrupt occurs when a receive - * DMA buffer is terminated in response to completion - * of a good frame or a frame with errors. The status - * of the frame is stored in the buffer entry in the - * list of receive buffer entries. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_receive_dma( struct mgsl_struct *info ) -{ - u16 status; - - /* clear interrupt pending and IUS bit for Rx DMA IRQ */ - usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); - - /* Read the receive DMA status to identify interrupt type. */ - /* This also clears the status bits. */ - status = usc_InDmaReg( info, RDMR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n", - __FILE__,__LINE__,info->device_name,status); - - info->pending_bh |= BH_RECEIVE; - - if ( status & BIT3 ) { - info->rx_overflow = true; - info->icount.buf_overrun++; - } - -} /* end of mgsl_isr_receive_dma() */ - -/* mgsl_isr_transmit_dma() - * - * This function services a transmit DMA channel interrupt. - * - * For this driver there is one source of transmit DMA interrupts - * as identified in the Transmit DMA Mode Register (TDMR): - * - * BIT2 EOB End of Buffer. This interrupt occurs when a - * transmit DMA buffer has been emptied. - * - * The driver maintains enough transmit DMA buffers to hold at least - * one max frame size transmit frame. When operating in a buffered - * transmit mode, there may be enough transmit DMA buffers to hold at - * least two or more max frame size frames. On an EOB condition, - * determine if there are any queued transmit buffers and copy into - * transmit DMA buffers if we have room. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_isr_transmit_dma( struct mgsl_struct *info ) -{ - u16 status; - - /* clear interrupt pending and IUS bit for Tx DMA IRQ */ - usc_OutDmaReg(info, CDIR, BIT8+BIT0 ); - - /* Read the transmit DMA status to identify interrupt type. */ - /* This also clears the status bits. */ - - status = usc_InDmaReg( info, TDMR ); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):mgsl_isr_transmit_dma(%s) status=%04X\n", - __FILE__,__LINE__,info->device_name,status); - - if ( status & BIT2 ) { - --info->tx_dma_buffers_used; - - /* if there are transmit frames queued, - * try to load the next one - */ - if ( load_next_tx_holding_buffer(info) ) { - /* if call returns non-zero value, we have - * at least one free tx holding buffer - */ - info->pending_bh |= BH_TRANSMIT; - } - } - -} /* end of mgsl_isr_transmit_dma() */ - -/* mgsl_interrupt() - * - * Interrupt service routine entry point. - * - * Arguments: - * - * irq interrupt number that caused interrupt - * dev_id device ID supplied during interrupt registration - * - * Return Value: None - */ -static irqreturn_t mgsl_interrupt(int dummy, void *dev_id) -{ - struct mgsl_struct *info = dev_id; - u16 UscVector; - u16 DmaVector; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)entry.\n", - __FILE__, __LINE__, info->irq_level); - - spin_lock(&info->irq_spinlock); - - for(;;) { - /* Read the interrupt vectors from hardware. */ - UscVector = usc_InReg(info, IVR) >> 9; - DmaVector = usc_InDmaReg(info, DIVR); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n", - __FILE__,__LINE__,info->device_name,UscVector,DmaVector); - - if ( !UscVector && !DmaVector ) - break; - - /* Dispatch interrupt vector */ - if ( UscVector ) - (*UscIsrTable[UscVector])(info); - else if ( (DmaVector&(BIT10|BIT9)) == BIT10) - mgsl_isr_transmit_dma(info); - else - mgsl_isr_receive_dma(info); - - if ( info->isr_overflow ) { - printk(KERN_ERR "%s(%d):%s isr overflow irq=%d\n", - __FILE__, __LINE__, info->device_name, info->irq_level); - usc_DisableMasterIrqBit(info); - usc_DisableDmaInterrupts(info,DICR_MASTER); - break; - } - } - - /* Request bottom half processing if there's something - * for it to do and the bh is not already running - */ - - if ( info->pending_bh && !info->bh_running && !info->bh_requested ) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s queueing bh task.\n", - __FILE__,__LINE__,info->device_name); - schedule_work(&info->task); - info->bh_requested = true; - } - - spin_unlock(&info->irq_spinlock); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)exit.\n", - __FILE__, __LINE__, info->irq_level); - - return IRQ_HANDLED; -} /* end of mgsl_interrupt() */ - -/* startup() - * - * Initialize and start device. - * - * Arguments: info pointer to device instance data - * Return Value: 0 if success, otherwise error code - */ -static int startup(struct mgsl_struct * info) -{ - int retval = 0; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name); - - if (info->port.flags & ASYNC_INITIALIZED) - return 0; - - if (!info->xmit_buf) { - /* allocate a page of memory for a transmit buffer */ - info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - if (!info->xmit_buf) { - printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", - __FILE__,__LINE__,info->device_name); - return -ENOMEM; - } - } - - info->pending_bh = 0; - - memset(&info->icount, 0, sizeof(info->icount)); - - setup_timer(&info->tx_timer, mgsl_tx_timeout, (unsigned long)info); - - /* Allocate and claim adapter resources */ - retval = mgsl_claim_resources(info); - - /* perform existence check and diagnostics */ - if ( !retval ) - retval = mgsl_adapter_test(info); - - if ( retval ) { - if (capable(CAP_SYS_ADMIN) && info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - mgsl_release_resources(info); - return retval; - } - - /* program hardware for current parameters */ - mgsl_change_params(info); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags |= ASYNC_INITIALIZED; - - return 0; - -} /* end of startup() */ - -/* shutdown() - * - * Called by mgsl_close() and mgsl_hangup() to shutdown hardware - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void shutdown(struct mgsl_struct * info) -{ - unsigned long flags; - - if (!(info->port.flags & ASYNC_INITIALIZED)) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_shutdown(%s)\n", - __FILE__,__LINE__, info->device_name ); - - /* clear status wait queue because status changes */ - /* can't happen after shutting down the hardware */ - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - del_timer_sync(&info->tx_timer); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = NULL; - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_DisableMasterIrqBit(info); - usc_stop_receiver(info); - usc_stop_transmitter(info); - usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + - TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); - usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); - - /* Disable DMAEN (Port 7, Bit 14) */ - /* This disconnects the DMA request signal from the ISA bus */ - /* on the ISA adapter. This has no effect for the PCI adapter */ - usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14)); - - /* Disable INTEN (Port 6, Bit12) */ - /* This disconnects the IRQ request signal to the ISA bus */ - /* on the ISA adapter. This has no effect for the PCI adapter */ - usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); - - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); - usc_set_serial_signals(info); - } - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - mgsl_release_resources(info); - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags &= ~ASYNC_INITIALIZED; - -} /* end of shutdown() */ - -static void mgsl_program_hw(struct mgsl_struct *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - - usc_stop_receiver(info); - usc_stop_transmitter(info); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - if (info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW || - info->netcount) - usc_set_sync_mode(info); - else - usc_set_async_mode(info); - - usc_set_serial_signals(info); - - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - - usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); - usc_EnableInterrupts(info, IO_PIN); - usc_get_serial_signals(info); - - if (info->netcount || info->port.tty->termios->c_cflag & CREAD) - usc_start_receiver(info); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); -} - -/* Reconfigure adapter based on new parameters - */ -static void mgsl_change_params(struct mgsl_struct *info) -{ - unsigned cflag; - int bits_per_char; - - if (!info->port.tty || !info->port.tty->termios) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_change_params(%s)\n", - __FILE__,__LINE__, info->device_name ); - - cflag = info->port.tty->termios->c_cflag; - - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ - if (cflag & CBAUD) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - - /* byte size and parity */ - - switch (cflag & CSIZE) { - case CS5: info->params.data_bits = 5; break; - case CS6: info->params.data_bits = 6; break; - case CS7: info->params.data_bits = 7; break; - case CS8: info->params.data_bits = 8; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: info->params.data_bits = 7; break; - } - - if (cflag & CSTOPB) - info->params.stop_bits = 2; - else - info->params.stop_bits = 1; - - info->params.parity = ASYNC_PARITY_NONE; - if (cflag & PARENB) { - if (cflag & PARODD) - info->params.parity = ASYNC_PARITY_ODD; - else - info->params.parity = ASYNC_PARITY_EVEN; -#ifdef CMSPAR - if (cflag & CMSPAR) - info->params.parity = ASYNC_PARITY_SPACE; -#endif - } - - /* calculate number of jiffies to transmit a full - * FIFO (32 bytes) at specified data rate - */ - bits_per_char = info->params.data_bits + - info->params.stop_bits + 1; - - /* if port data rate is set to 460800 or less then - * allow tty settings to override, otherwise keep the - * current data rate. - */ - if (info->params.data_rate <= 460800) - info->params.data_rate = tty_get_baud_rate(info->port.tty); - - if ( info->params.data_rate ) { - info->timeout = (32*HZ*bits_per_char) / - info->params.data_rate; - } - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; - - /* process tty input control flags */ - - info->read_status_mask = RXSTATUS_OVERRUN; - if (I_INPCK(info->port.tty)) - info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= RXSTATUS_BREAK_RECEIVED; - - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED; - /* If ignoring parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= RXSTATUS_OVERRUN; - } - - mgsl_program_hw(info); - -} /* end of mgsl_change_params() */ - -/* mgsl_put_char() - * - * Add a character to the transmit buffer. - * - * Arguments: tty pointer to tty information structure - * ch character to add to transmit buffer - * - * Return Value: None - */ -static int mgsl_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - int ret = 0; - - if (debug_level >= DEBUG_LEVEL_INFO) { - printk(KERN_DEBUG "%s(%d):mgsl_put_char(%d) on %s\n", - __FILE__, __LINE__, ch, info->device_name); - } - - if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char")) - return 0; - - if (!info->xmit_buf) - return 0; - - spin_lock_irqsave(&info->irq_spinlock, flags); - - if ((info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active) { - if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) { - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE-1; - info->xmit_cnt++; - ret = 1; - } - } - spin_unlock_irqrestore(&info->irq_spinlock, flags); - return ret; - -} /* end of mgsl_put_char() */ - -/* mgsl_flush_chars() - * - * Enable transmitter so remaining characters in the - * transmit buffer are sent. - * - * Arguments: tty pointer to tty information structure - * Return Value: None - */ -static void mgsl_flush_chars(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n", - __FILE__,__LINE__,info->device_name,info->xmit_cnt); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_chars")) - return; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n", - __FILE__,__LINE__,info->device_name ); - - spin_lock_irqsave(&info->irq_spinlock,flags); - - if (!info->tx_active) { - if ( (info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW) && info->xmit_cnt ) { - /* operating in synchronous (frame oriented) mode */ - /* copy data from circular xmit_buf to */ - /* transmit DMA buffer. */ - mgsl_load_tx_dma_buffer(info, - info->xmit_buf,info->xmit_cnt); - } - usc_start_transmitter(info); - } - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - -} /* end of mgsl_flush_chars() */ - -/* mgsl_write() - * - * Send a block of data - * - * Arguments: - * - * tty pointer to tty information structure - * buf pointer to buffer containing send data - * count size of send data in bytes - * - * Return Value: number of characters written - */ -static int mgsl_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_write(%s) count=%d\n", - __FILE__,__LINE__,info->device_name,count); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_write")) - goto cleanup; - - if (!info->xmit_buf) - goto cleanup; - - if ( info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - /* operating in synchronous (frame oriented) mode */ - /* operating in synchronous (frame oriented) mode */ - if (info->tx_active) { - - if ( info->params.mode == MGSL_MODE_HDLC ) { - ret = 0; - goto cleanup; - } - /* transmitter is actively sending data - - * if we have multiple transmit dma and - * holding buffers, attempt to queue this - * frame for transmission at a later time. - */ - if (info->tx_holding_count >= info->num_tx_holding_buffers ) { - /* no tx holding buffers available */ - ret = 0; - goto cleanup; - } - - /* queue transmit frame request */ - ret = count; - save_tx_buffer_request(info,buf,count); - - /* if we have sufficient tx dma buffers, - * load the next buffered tx request - */ - spin_lock_irqsave(&info->irq_spinlock,flags); - load_next_tx_holding_buffer(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - goto cleanup; - } - - /* if operating in HDLC LoopMode and the adapter */ - /* has yet to be inserted into the loop, we can't */ - /* transmit */ - - if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) && - !usc_loopmode_active(info) ) - { - ret = 0; - goto cleanup; - } - - if ( info->xmit_cnt ) { - /* Send accumulated from send_char() calls */ - /* as frame and wait before accepting more data. */ - ret = 0; - - /* copy data from circular xmit_buf to */ - /* transmit DMA buffer. */ - mgsl_load_tx_dma_buffer(info, - info->xmit_buf,info->xmit_cnt); - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n", - __FILE__,__LINE__,info->device_name); - } else { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n", - __FILE__,__LINE__,info->device_name); - ret = count; - info->xmit_cnt = count; - mgsl_load_tx_dma_buffer(info,buf,count); - } - } else { - while (1) { - spin_lock_irqsave(&info->irq_spinlock,flags); - c = min_t(int, count, - min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - spin_unlock_irqrestore(&info->irq_spinlock,flags); - break; - } - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & - (SERIAL_XMIT_SIZE-1)); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->irq_spinlock,flags); - buf += c; - count -= c; - ret += c; - } - } - - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!info->tx_active) - usc_start_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } -cleanup: - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_write(%s) returning=%d\n", - __FILE__,__LINE__,info->device_name,ret); - - return ret; - -} /* end of mgsl_write() */ - -/* mgsl_write_room() - * - * Return the count of free bytes in transmit buffer - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static int mgsl_write_room(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - int ret; - - if (mgsl_paranoia_check(info, tty->name, "mgsl_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_write_room(%s)=%d\n", - __FILE__,__LINE__, info->device_name,ret ); - - if ( info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - /* operating in synchronous (frame oriented) mode */ - if ( info->tx_active ) - return 0; - else - return HDLC_MAX_FRAME_SIZE; - } - - return ret; - -} /* end of mgsl_write_room() */ - -/* mgsl_chars_in_buffer() - * - * Return the count of bytes in transmit buffer - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static int mgsl_chars_in_buffer(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_chars_in_buffer(%s)\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_chars_in_buffer")) - return 0; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n", - __FILE__,__LINE__, info->device_name,info->xmit_cnt ); - - if ( info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - /* operating in synchronous (frame oriented) mode */ - if ( info->tx_active ) - return info->max_frame_size; - else - return 0; - } - - return info->xmit_cnt; -} /* end of mgsl_chars_in_buffer() */ - -/* mgsl_flush_buffer() - * - * Discard all data in the send buffer - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static void mgsl_flush_buffer(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_flush_buffer(%s) entry\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_buffer")) - return; - - spin_lock_irqsave(&info->irq_spinlock,flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - del_timer(&info->tx_timer); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - tty_wakeup(tty); -} - -/* mgsl_send_xchar() - * - * Send a high-priority XON/XOFF character - * - * Arguments: tty pointer to tty info structure - * ch character to send - * Return Value: None - */ -static void mgsl_send_xchar(struct tty_struct *tty, char ch) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_send_xchar(%s,%d)\n", - __FILE__,__LINE__, info->device_name, ch ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_send_xchar")) - return; - - info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!info->tx_enabled) - usc_start_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } -} /* end of mgsl_send_xchar() */ - -/* mgsl_throttle() - * - * Signal remote device to throttle send data (our receive data) - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static void mgsl_throttle(struct tty_struct * tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_throttle(%s) entry\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_throttle")) - return; - - if (I_IXOFF(tty)) - mgsl_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->irq_spinlock,flags); - info->serial_signals &= ~SerialSignal_RTS; - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } -} /* end of mgsl_throttle() */ - -/* mgsl_unthrottle() - * - * Signal remote device to stop throttling send data (our receive data) - * - * Arguments: tty pointer to tty info structure - * Return Value: None - */ -static void mgsl_unthrottle(struct tty_struct * tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_unthrottle(%s) entry\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - mgsl_send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->irq_spinlock,flags); - info->serial_signals |= SerialSignal_RTS; - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - -} /* end of mgsl_unthrottle() */ - -/* mgsl_get_stats() - * - * get the current serial parameters information - * - * Arguments: info pointer to device instance data - * user_icount pointer to buffer to hold returned stats - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *user_icount) -{ - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_get_params(%s)\n", - __FILE__,__LINE__, info->device_name); - - if (!user_icount) { - memset(&info->icount, 0, sizeof(info->icount)); - } else { - mutex_lock(&info->port.mutex); - COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount)); - mutex_unlock(&info->port.mutex); - if (err) - return -EFAULT; - } - - return 0; - -} /* end of mgsl_get_stats() */ - -/* mgsl_get_params() - * - * get the current serial parameters information - * - * Arguments: info pointer to device instance data - * user_params pointer to buffer to hold returned params - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_get_params(%s)\n", - __FILE__,__LINE__, info->device_name); - - mutex_lock(&info->port.mutex); - COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); - mutex_unlock(&info->port.mutex); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - return 0; - -} /* end of mgsl_get_params() */ - -/* mgsl_set_params() - * - * set the serial parameters - * - * Arguments: - * - * info pointer to device instance data - * new_params user buffer containing new serial params - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params) -{ - unsigned long flags; - MGSL_PARAMS tmp_params; - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__, - info->device_name ); - COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - mutex_lock(&info->port.mutex); - spin_lock_irqsave(&info->irq_spinlock,flags); - memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - mgsl_change_params(info); - mutex_unlock(&info->port.mutex); - - return 0; - -} /* end of mgsl_set_params() */ - -/* mgsl_get_txidle() - * - * get the current transmit idle mode - * - * Arguments: info pointer to device instance data - * idle_mode pointer to buffer to hold returned idle mode - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode) -{ - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_get_txidle(%s)=%d\n", - __FILE__,__LINE__, info->device_name, info->idle_mode); - - COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - return 0; - -} /* end of mgsl_get_txidle() */ - -/* mgsl_set_txidle() service ioctl to set transmit idle mode - * - * Arguments: info pointer to device instance data - * idle_mode new idle mode - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__, - info->device_name, idle_mode ); - - spin_lock_irqsave(&info->irq_spinlock,flags); - info->idle_mode = idle_mode; - usc_set_txidle( info ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; - -} /* end of mgsl_set_txidle() */ - -/* mgsl_txenable() - * - * enable or disable the transmitter - * - * Arguments: - * - * info pointer to device instance data - * enable 1 = enable, 0 = disable - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_txenable(struct mgsl_struct * info, int enable) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__, - info->device_name, enable); - - spin_lock_irqsave(&info->irq_spinlock,flags); - if ( enable ) { - if ( !info->tx_enabled ) { - - usc_start_transmitter(info); - /*-------------------------------------------------- - * if HDLC/SDLC Loop mode, attempt to insert the - * station in the 'loop' by setting CMR:13. Upon - * receipt of the next GoAhead (RxAbort) sequence, - * the OnLoop indicator (CCSR:7) should go active - * to indicate that we are on the loop - *--------------------------------------------------*/ - if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) - usc_loopmode_insert_request( info ); - } - } else { - if ( info->tx_enabled ) - usc_stop_transmitter(info); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; - -} /* end of mgsl_txenable() */ - -/* mgsl_txabort() abort send HDLC frame - * - * Arguments: info pointer to device instance data - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_txabort(struct mgsl_struct * info) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__, - info->device_name); - - spin_lock_irqsave(&info->irq_spinlock,flags); - if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) - { - if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) - usc_loopmode_cancel_transmit( info ); - else - usc_TCmd(info,TCmd_SendAbort); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; - -} /* end of mgsl_txabort() */ - -/* mgsl_rxenable() enable or disable the receiver - * - * Arguments: info pointer to device instance data - * enable 1 = enable, 0 = disable - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_rxenable(struct mgsl_struct * info, int enable) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__, - info->device_name, enable); - - spin_lock_irqsave(&info->irq_spinlock,flags); - if ( enable ) { - if ( !info->rx_enabled ) - usc_start_receiver(info); - } else { - if ( info->rx_enabled ) - usc_stop_receiver(info); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; - -} /* end of mgsl_rxenable() */ - -/* mgsl_wait_event() wait for specified event to occur - * - * Arguments: info pointer to device instance data - * mask pointer to bitmask of events to wait for - * Return Value: 0 if successful and bit mask updated with - * of events triggerred, - * otherwise error code - */ -static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr) -{ - unsigned long flags; - int s; - int rc=0; - struct mgsl_icount cprev, cnow; - int events; - int mask; - struct _input_signal_events oldsigs, newsigs; - DECLARE_WAITQUEUE(wait, current); - - COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); - if (rc) { - return -EFAULT; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, - info->device_name, mask); - - spin_lock_irqsave(&info->irq_spinlock,flags); - - /* return immediately if state matches requested events */ - usc_get_serial_signals(info); - s = info->serial_signals; - events = mask & - ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + - ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + - ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + - ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); - if (events) { - spin_unlock_irqrestore(&info->irq_spinlock,flags); - goto exit; - } - - /* save current irq counts */ - cprev = info->icount; - oldsigs = info->input_signal_events; - - /* enable hunt and idle irqs if needed */ - if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { - u16 oldreg = usc_InReg(info,RICR); - u16 newreg = oldreg + - (mask & MgslEvent_ExitHuntMode ? RXSTATUS_EXITED_HUNT:0) + - (mask & MgslEvent_IdleReceived ? RXSTATUS_IDLE_RECEIVED:0); - if (oldreg != newreg) - usc_OutReg(info, RICR, newreg); - } - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&info->event_wait_q, &wait); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get current irq counts */ - spin_lock_irqsave(&info->irq_spinlock,flags); - cnow = info->icount; - newsigs = info->input_signal_events; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - /* if no change, wait aborted for some reason */ - if (newsigs.dsr_up == oldsigs.dsr_up && - newsigs.dsr_down == oldsigs.dsr_down && - newsigs.dcd_up == oldsigs.dcd_up && - newsigs.dcd_down == oldsigs.dcd_down && - newsigs.cts_up == oldsigs.cts_up && - newsigs.cts_down == oldsigs.cts_down && - newsigs.ri_up == oldsigs.ri_up && - newsigs.ri_down == oldsigs.ri_down && - cnow.exithunt == cprev.exithunt && - cnow.rxidle == cprev.rxidle) { - rc = -EIO; - break; - } - - events = mask & - ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + - (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + - (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + - (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + - (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + - (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + - (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + - (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + - (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + - (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); - if (events) - break; - - cprev = cnow; - oldsigs = newsigs; - } - - remove_wait_queue(&info->event_wait_q, &wait); - set_current_state(TASK_RUNNING); - - if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!waitqueue_active(&info->event_wait_q)) { - /* disable enable exit hunt mode/idle rcvd IRQs */ - usc_OutReg(info, RICR, usc_InReg(info,RICR) & - ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } -exit: - if ( rc == 0 ) - PUT_USER(rc, events, mask_ptr); - - return rc; - -} /* end of mgsl_wait_event() */ - -static int modem_input_wait(struct mgsl_struct *info,int arg) -{ - unsigned long flags; - int rc; - struct mgsl_icount cprev, cnow; - DECLARE_WAITQUEUE(wait, current); - - /* save current irq counts */ - spin_lock_irqsave(&info->irq_spinlock,flags); - cprev = info->icount; - add_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get new irq counts */ - spin_lock_irqsave(&info->irq_spinlock,flags); - cnow = info->icount; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - /* if no change, wait aborted for some reason */ - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; - break; - } - - /* check for change in caller specified modem input */ - if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || - (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || - (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || - (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { - rc = 0; - break; - } - - cprev = cnow; - } - remove_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_RUNNING); - return rc; -} - -/* return the state of the serial control and status signals - */ -static int tiocmget(struct tty_struct *tty) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_get_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmget() value=%08X\n", - __FILE__,__LINE__, info->device_name, result ); - return result; -} - -/* set modem control signals (DTR/RTS) - */ -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmset(%x,%x)\n", - __FILE__,__LINE__,info->device_name, set, clear); - - if (set & TIOCM_RTS) - info->serial_signals |= SerialSignal_RTS; - if (set & TIOCM_DTR) - info->serial_signals |= SerialSignal_DTR; - if (clear & TIOCM_RTS) - info->serial_signals &= ~SerialSignal_RTS; - if (clear & TIOCM_DTR) - info->serial_signals &= ~SerialSignal_DTR; - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return 0; -} - -/* mgsl_break() Set or clear transmit break condition - * - * Arguments: tty pointer to tty instance data - * break_state -1=set break condition, 0=clear - * Return Value: error code - */ -static int mgsl_break(struct tty_struct *tty, int break_state) -{ - struct mgsl_struct * info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_break(%s,%d)\n", - __FILE__,__LINE__, info->device_name, break_state); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_break")) - return -EINVAL; - - spin_lock_irqsave(&info->irq_spinlock,flags); - if (break_state == -1) - usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7)); - else - usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; - -} /* end of mgsl_break() */ - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int msgl_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) - -{ - struct mgsl_struct * info = tty->driver_data; - struct mgsl_icount cnow; /* kernel counter temps */ - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - return 0; -} - -/* mgsl_ioctl() Service an IOCTL request - * - * Arguments: - * - * tty pointer to tty instance data - * cmd IOCTL command code - * arg command argument/context - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct mgsl_struct * info = tty->driver_data; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, - info->device_name, cmd ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - return mgsl_ioctl_common(info, cmd, arg); -} - -static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - - switch (cmd) { - case MGSL_IOCGPARAMS: - return mgsl_get_params(info, argp); - case MGSL_IOCSPARAMS: - return mgsl_set_params(info, argp); - case MGSL_IOCGTXIDLE: - return mgsl_get_txidle(info, argp); - case MGSL_IOCSTXIDLE: - return mgsl_set_txidle(info,(int)arg); - case MGSL_IOCTXENABLE: - return mgsl_txenable(info,(int)arg); - case MGSL_IOCRXENABLE: - return mgsl_rxenable(info,(int)arg); - case MGSL_IOCTXABORT: - return mgsl_txabort(info); - case MGSL_IOCGSTATS: - return mgsl_get_stats(info, argp); - case MGSL_IOCWAITEVENT: - return mgsl_wait_event(info, argp); - case MGSL_IOCLOOPTXDONE: - return mgsl_loopmode_send_done(info); - /* Wait for modem input (DCD,RI,DSR,CTS) change - * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS) - */ - case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -/* mgsl_set_termios() - * - * Set new termios settings - * - * Arguments: - * - * tty pointer to tty structure - * termios pointer to buffer to hold returned old termios - * - * Return Value: None - */ -static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct mgsl_struct *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__, - tty->driver->name ); - - mgsl_change_params(info); - - /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { - info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { - info->serial_signals |= SerialSignal_RTS; - } - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - - /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - mgsl_start(tty); - } - -} /* end of mgsl_set_termios() */ - -/* mgsl_close() - * - * Called when port is closed. Wait for remaining data to be - * sent. Disable port and free resources. - * - * Arguments: - * - * tty pointer to open tty structure - * filp pointer to open file object - * - * Return Value: None - */ -static void mgsl_close(struct tty_struct *tty, struct file * filp) -{ - struct mgsl_struct * info = tty->driver_data; - - if (mgsl_paranoia_check(info, tty->name, "mgsl_close")) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_close(%s) entry, count=%d\n", - __FILE__,__LINE__, info->device_name, info->port.count); - - if (tty_port_close_start(&info->port, tty, filp) == 0) - goto cleanup; - - mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) - mgsl_wait_until_sent(tty, info->timeout); - mgsl_flush_buffer(tty); - tty_ldisc_flush(tty); - shutdown(info); - mutex_unlock(&info->port.mutex); - - tty_port_close_end(&info->port, tty); - info->port.tty = NULL; -cleanup: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__, - tty->driver->name, info->port.count); - -} /* end of mgsl_close() */ - -/* mgsl_wait_until_sent() - * - * Wait until the transmitter is empty. - * - * Arguments: - * - * tty pointer to tty info structure - * timeout time to wait for send completion - * - * Return Value: None - */ -static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct mgsl_struct * info = tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (!info ) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_wait_until_sent(%s) entry\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_wait_until_sent")) - return; - - if (!(info->port.flags & ASYNC_INITIALIZED)) - goto exit; - - orig_jiffies = jiffies; - - /* Set check interval to 1/5 of estimated time to - * send a character, and make it at least 1. The check - * interval should also be less than the timeout. - * Note: use tight timings here to satisfy the NIST-PCTS. - */ - - if ( info->params.data_rate ) { - char_time = info->timeout/(32 * 5); - if (!char_time) - char_time++; - } else - char_time = 1; - - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - - if ( info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - while (info->tx_active) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } else { - while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && - info->tx_enabled) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } - -exit: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_wait_until_sent(%s) exit\n", - __FILE__,__LINE__, info->device_name ); - -} /* end of mgsl_wait_until_sent() */ - -/* mgsl_hangup() - * - * Called by tty_hangup() when a hangup is signaled. - * This is the same as to closing all open files for the port. - * - * Arguments: tty pointer to associated tty object - * Return Value: None - */ -static void mgsl_hangup(struct tty_struct *tty) -{ - struct mgsl_struct * info = tty->driver_data; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_hangup(%s)\n", - __FILE__,__LINE__, info->device_name ); - - if (mgsl_paranoia_check(info, tty->name, "mgsl_hangup")) - return; - - mgsl_flush_buffer(tty); - shutdown(info); - - info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - - wake_up_interruptible(&info->port.open_wait); - -} /* end of mgsl_hangup() */ - -/* - * carrier_raised() - * - * Return true if carrier is raised - */ - -static int carrier_raised(struct tty_port *port) -{ - unsigned long flags; - struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); - - spin_lock_irqsave(&info->irq_spinlock, flags); - usc_get_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock, flags); - return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; -} - -static void dtr_rts(struct tty_port *port, int on) -{ - struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - if (on) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - usc_set_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); -} - - -/* block_til_ready() - * - * Block the current process until the specified port - * is ready to be opened. - * - * Arguments: - * - * tty pointer to tty info structure - * filp pointer to open file object - * info pointer to device instance data - * - * Return Value: 0 if success, otherwise error code - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct mgsl_struct *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - bool do_clocal = false; - bool extra_count = false; - unsigned long flags; - int dcd; - struct tty_port *port = &info->port; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready on %s\n", - __FILE__,__LINE__, tty->driver->name ); - - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ - /* nonblock mode is set or port is not enabled */ - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = true; - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * mgsl_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready before block on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - spin_lock_irqsave(&info->irq_spinlock, flags); - if (!tty_hung_up_p(filp)) { - extra_count = true; - port->count--; - } - spin_unlock_irqrestore(&info->irq_spinlock, flags); - port->blocked_open++; - - while (1) { - if (tty->termios->c_cflag & CBAUD) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - dcd = tty_port_carrier_raised(&info->port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd)) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready blocking on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - tty_unlock(); - schedule(); - tty_lock(); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - /* FIXME: Racy on hangup during close wait */ - if (extra_count) - port->count++; - port->blocked_open--; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready after blocking on %s count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; - - return retval; - -} /* end of block_til_ready() */ - -/* mgsl_open() - * - * Called when a port is opened. Init and enable port. - * Perform serial-specific initialization for the tty structure. - * - * Arguments: tty pointer to tty info structure - * filp associated file pointer - * - * Return Value: 0 if success, otherwise error code - */ -static int mgsl_open(struct tty_struct *tty, struct file * filp) -{ - struct mgsl_struct *info; - int retval, line; - unsigned long flags; - - /* verify range of specified line number */ - line = tty->index; - if ((line < 0) || (line >= mgsl_device_count)) { - printk("%s(%d):mgsl_open with invalid line #%d.\n", - __FILE__,__LINE__,line); - return -ENODEV; - } - - /* find the info structure for the specified line */ - info = mgsl_device_list; - while(info && info->line != line) - info = info->next_device; - if (mgsl_paranoia_check(info, tty->name, "mgsl_open")) - return -ENODEV; - - tty->driver_data = info; - info->port.tty = tty; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_open(%s), old ref count = %d\n", - __FILE__,__LINE__,tty->driver->name, info->port.count); - - /* If port is closing, signal caller to try again */ - if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - spin_lock_irqsave(&info->netlock, flags); - if (info->netcount) { - retval = -EBUSY; - spin_unlock_irqrestore(&info->netlock, flags); - goto cleanup; - } - info->port.count++; - spin_unlock_irqrestore(&info->netlock, flags); - - if (info->port.count == 1) { - /* 1st open on this device, init hardware */ - retval = startup(info); - if (retval < 0) - goto cleanup; - } - - retval = block_til_ready(tty, filp, info); - if (retval) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):block_til_ready(%s) returned %d\n", - __FILE__,__LINE__, info->device_name, retval); - goto cleanup; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_open(%s) success\n", - __FILE__,__LINE__, info->device_name); - retval = 0; - -cleanup: - if (retval) { - if (tty->count == 1) - info->port.tty = NULL; /* tty layer will release tty struct */ - if(info->port.count) - info->port.count--; - } - - return retval; - -} /* end of mgsl_open() */ - -/* - * /proc fs routines.... - */ - -static inline void line_info(struct seq_file *m, struct mgsl_struct *info) -{ - char stat_buf[30]; - unsigned long flags; - - if (info->bus_type == MGSL_BUS_TYPE_PCI) { - seq_printf(m, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", - info->device_name, info->io_base, info->irq_level, - info->phys_memory_base, info->phys_lcr_base); - } else { - seq_printf(m, "%s:(E)ISA io:%04X irq:%d dma:%d", - info->device_name, info->io_base, - info->irq_level, info->dma_level); - } - - /* output current serial signal states */ - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_get_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (info->serial_signals & SerialSignal_RTS) - strcat(stat_buf, "|RTS"); - if (info->serial_signals & SerialSignal_CTS) - strcat(stat_buf, "|CTS"); - if (info->serial_signals & SerialSignal_DTR) - strcat(stat_buf, "|DTR"); - if (info->serial_signals & SerialSignal_DSR) - strcat(stat_buf, "|DSR"); - if (info->serial_signals & SerialSignal_DCD) - strcat(stat_buf, "|CD"); - if (info->serial_signals & SerialSignal_RI) - strcat(stat_buf, "|RI"); - - if (info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - seq_printf(m, " HDLC txok:%d rxok:%d", - info->icount.txok, info->icount.rxok); - if (info->icount.txunder) - seq_printf(m, " txunder:%d", info->icount.txunder); - if (info->icount.txabort) - seq_printf(m, " txabort:%d", info->icount.txabort); - if (info->icount.rxshort) - seq_printf(m, " rxshort:%d", info->icount.rxshort); - if (info->icount.rxlong) - seq_printf(m, " rxlong:%d", info->icount.rxlong); - if (info->icount.rxover) - seq_printf(m, " rxover:%d", info->icount.rxover); - if (info->icount.rxcrc) - seq_printf(m, " rxcrc:%d", info->icount.rxcrc); - } else { - seq_printf(m, " ASYNC tx:%d rx:%d", - info->icount.tx, info->icount.rx); - if (info->icount.frame) - seq_printf(m, " fe:%d", info->icount.frame); - if (info->icount.parity) - seq_printf(m, " pe:%d", info->icount.parity); - if (info->icount.brk) - seq_printf(m, " brk:%d", info->icount.brk); - if (info->icount.overrun) - seq_printf(m, " oe:%d", info->icount.overrun); - } - - /* Append serial signal status to end */ - seq_printf(m, " %s\n", stat_buf+1); - - seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", - info->tx_active,info->bh_requested,info->bh_running, - info->pending_bh); - - spin_lock_irqsave(&info->irq_spinlock,flags); - { - u16 Tcsr = usc_InReg( info, TCSR ); - u16 Tdmr = usc_InDmaReg( info, TDMR ); - u16 Ticr = usc_InReg( info, TICR ); - u16 Rscr = usc_InReg( info, RCSR ); - u16 Rdmr = usc_InDmaReg( info, RDMR ); - u16 Ricr = usc_InReg( info, RICR ); - u16 Icr = usc_InReg( info, ICR ); - u16 Dccr = usc_InReg( info, DCCR ); - u16 Tmr = usc_InReg( info, TMR ); - u16 Tccr = usc_InReg( info, TCCR ); - u16 Ccar = inw( info->io_base + CCAR ); - seq_printf(m, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" - "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n", - Tcsr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar ); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); -} - -/* Called to print information about devices */ -static int mgsl_proc_show(struct seq_file *m, void *v) -{ - struct mgsl_struct *info; - - seq_printf(m, "synclink driver:%s\n", driver_version); - - info = mgsl_device_list; - while( info ) { - line_info(m, info); - info = info->next_device; - } - return 0; -} - -static int mgsl_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, mgsl_proc_show, NULL); -} - -static const struct file_operations mgsl_proc_fops = { - .owner = THIS_MODULE, - .open = mgsl_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* mgsl_allocate_dma_buffers() - * - * Allocate and format DMA buffers (ISA adapter) - * or format shared memory buffers (PCI adapter). - * - * Arguments: info pointer to device instance data - * Return Value: 0 if success, otherwise error - */ -static int mgsl_allocate_dma_buffers(struct mgsl_struct *info) -{ - unsigned short BuffersPerFrame; - - info->last_mem_alloc = 0; - - /* Calculate the number of DMA buffers necessary to hold the */ - /* largest allowable frame size. Note: If the max frame size is */ - /* not an even multiple of the DMA buffer size then we need to */ - /* round the buffer count per frame up one. */ - - BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE); - if ( info->max_frame_size % DMABUFFERSIZE ) - BuffersPerFrame++; - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - /* - * The PCI adapter has 256KBytes of shared memory to use. - * This is 64 PAGE_SIZE buffers. - * - * The first page is used for padding at this time so the - * buffer list does not begin at offset 0 of the PCI - * adapter's shared memory. - * - * The 2nd page is used for the buffer list. A 4K buffer - * list can hold 128 DMA_BUFFER structures at 32 bytes - * each. - * - * This leaves 62 4K pages. - * - * The next N pages are used for transmit frame(s). We - * reserve enough 4K page blocks to hold the required - * number of transmit dma buffers (num_tx_dma_buffers), - * each of MaxFrameSize size. - * - * Of the remaining pages (62-N), determine how many can - * be used to receive full MaxFrameSize inbound frames - */ - info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame; - info->rx_buffer_count = 62 - info->tx_buffer_count; - } else { - /* Calculate the number of PAGE_SIZE buffers needed for */ - /* receive and transmit DMA buffers. */ - - - /* Calculate the number of DMA buffers necessary to */ - /* hold 7 max size receive frames and one max size transmit frame. */ - /* The receive buffer count is bumped by one so we avoid an */ - /* End of List condition if all receive buffers are used when */ - /* using linked list DMA buffers. */ - - info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame; - info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6; - - /* - * limit total TxBuffers & RxBuffers to 62 4K total - * (ala PCI Allocation) - */ - - if ( (info->tx_buffer_count + info->rx_buffer_count) > 62 ) - info->rx_buffer_count = 62 - info->tx_buffer_count; - - } - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n", - __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count); - - if ( mgsl_alloc_buffer_list_memory( info ) < 0 || - mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || - mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0 || - mgsl_alloc_intermediate_rxbuffer_memory(info) < 0 || - mgsl_alloc_intermediate_txbuffer_memory(info) < 0 ) { - printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__); - return -ENOMEM; - } - - mgsl_reset_rx_dma_buffers( info ); - mgsl_reset_tx_dma_buffers( info ); - - return 0; - -} /* end of mgsl_allocate_dma_buffers() */ - -/* - * mgsl_alloc_buffer_list_memory() - * - * Allocate a common DMA buffer for use as the - * receive and transmit buffer lists. - * - * A buffer list is a set of buffer entries where each entry contains - * a pointer to an actual buffer and a pointer to the next buffer entry - * (plus some other info about the buffer). - * - * The buffer entries for a list are built to form a circular list so - * that when the entire list has been traversed you start back at the - * beginning. - * - * This function allocates memory for just the buffer entries. - * The links (pointer to next entry) are filled in with the physical - * address of the next entry so the adapter can navigate the list - * using bus master DMA. The pointers to the actual buffers are filled - * out later when the actual buffers are allocated. - * - * Arguments: info pointer to device instance data - * Return Value: 0 if success, otherwise error - */ -static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info ) -{ - unsigned int i; - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - /* PCI adapter uses shared memory. */ - info->buffer_list = info->memory_base + info->last_mem_alloc; - info->buffer_list_phys = info->last_mem_alloc; - info->last_mem_alloc += BUFFERLISTSIZE; - } else { - /* ISA adapter uses system memory. */ - /* The buffer lists are allocated as a common buffer that both */ - /* the processor and adapter can access. This allows the driver to */ - /* inspect portions of the buffer while other portions are being */ - /* updated by the adapter using Bus Master DMA. */ - - info->buffer_list = dma_alloc_coherent(NULL, BUFFERLISTSIZE, &info->buffer_list_dma_addr, GFP_KERNEL); - if (info->buffer_list == NULL) - return -ENOMEM; - info->buffer_list_phys = (u32)(info->buffer_list_dma_addr); - } - - /* We got the memory for the buffer entry lists. */ - /* Initialize the memory block to all zeros. */ - memset( info->buffer_list, 0, BUFFERLISTSIZE ); - - /* Save virtual address pointers to the receive and */ - /* transmit buffer lists. (Receive 1st). These pointers will */ - /* be used by the processor to access the lists. */ - info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; - info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; - info->tx_buffer_list += info->rx_buffer_count; - - /* - * Build the links for the buffer entry lists such that - * two circular lists are built. (Transmit and Receive). - * - * Note: the links are physical addresses - * which are read by the adapter to determine the next - * buffer entry to use. - */ - - for ( i = 0; i < info->rx_buffer_count; i++ ) { - /* calculate and store physical address of this buffer entry */ - info->rx_buffer_list[i].phys_entry = - info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY)); - - /* calculate and store physical address of */ - /* next entry in cirular list of entries */ - - info->rx_buffer_list[i].link = info->buffer_list_phys; - - if ( i < info->rx_buffer_count - 1 ) - info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); - } - - for ( i = 0; i < info->tx_buffer_count; i++ ) { - /* calculate and store physical address of this buffer entry */ - info->tx_buffer_list[i].phys_entry = info->buffer_list_phys + - ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY)); - - /* calculate and store physical address of */ - /* next entry in cirular list of entries */ - - info->tx_buffer_list[i].link = info->buffer_list_phys + - info->rx_buffer_count * sizeof(DMABUFFERENTRY); - - if ( i < info->tx_buffer_count - 1 ) - info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); - } - - return 0; - -} /* end of mgsl_alloc_buffer_list_memory() */ - -/* Free DMA buffers allocated for use as the - * receive and transmit buffer lists. - * Warning: - * - * The data transfer buffers associated with the buffer list - * MUST be freed before freeing the buffer list itself because - * the buffer list contains the information necessary to free - * the individual buffers! - */ -static void mgsl_free_buffer_list_memory( struct mgsl_struct *info ) -{ - if (info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI) - dma_free_coherent(NULL, BUFFERLISTSIZE, info->buffer_list, info->buffer_list_dma_addr); - - info->buffer_list = NULL; - info->rx_buffer_list = NULL; - info->tx_buffer_list = NULL; - -} /* end of mgsl_free_buffer_list_memory() */ - -/* - * mgsl_alloc_frame_memory() - * - * Allocate the frame DMA buffers used by the specified buffer list. - * Each DMA buffer will be one memory page in size. This is necessary - * because memory can fragment enough that it may be impossible - * contiguous pages. - * - * Arguments: - * - * info pointer to device instance data - * BufferList pointer to list of buffer entries - * Buffercount count of buffer entries in buffer list - * - * Return Value: 0 if success, otherwise -ENOMEM - */ -static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount) -{ - int i; - u32 phys_addr; - - /* Allocate page sized buffers for the receive buffer list */ - - for ( i = 0; i < Buffercount; i++ ) { - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - /* PCI adapter uses shared memory buffers. */ - BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc; - phys_addr = info->last_mem_alloc; - info->last_mem_alloc += DMABUFFERSIZE; - } else { - /* ISA adapter uses system memory. */ - BufferList[i].virt_addr = dma_alloc_coherent(NULL, DMABUFFERSIZE, &BufferList[i].dma_addr, GFP_KERNEL); - if (BufferList[i].virt_addr == NULL) - return -ENOMEM; - phys_addr = (u32)(BufferList[i].dma_addr); - } - BufferList[i].phys_addr = phys_addr; - } - - return 0; - -} /* end of mgsl_alloc_frame_memory() */ - -/* - * mgsl_free_frame_memory() - * - * Free the buffers associated with - * each buffer entry of a buffer list. - * - * Arguments: - * - * info pointer to device instance data - * BufferList pointer to list of buffer entries - * Buffercount count of buffer entries in buffer list - * - * Return Value: None - */ -static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount) -{ - int i; - - if ( BufferList ) { - for ( i = 0 ; i < Buffercount ; i++ ) { - if ( BufferList[i].virt_addr ) { - if ( info->bus_type != MGSL_BUS_TYPE_PCI ) - dma_free_coherent(NULL, DMABUFFERSIZE, BufferList[i].virt_addr, BufferList[i].dma_addr); - BufferList[i].virt_addr = NULL; - } - } - } - -} /* end of mgsl_free_frame_memory() */ - -/* mgsl_free_dma_buffers() - * - * Free DMA buffers - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_free_dma_buffers( struct mgsl_struct *info ) -{ - mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count ); - mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count ); - mgsl_free_buffer_list_memory( info ); - -} /* end of mgsl_free_dma_buffers() */ - - -/* - * mgsl_alloc_intermediate_rxbuffer_memory() - * - * Allocate a buffer large enough to hold max_frame_size. This buffer - * is used to pass an assembled frame to the line discipline. - * - * Arguments: - * - * info pointer to device instance data - * - * Return Value: 0 if success, otherwise -ENOMEM - */ -static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info) -{ - info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA); - if ( info->intermediate_rxbuffer == NULL ) - return -ENOMEM; - - return 0; - -} /* end of mgsl_alloc_intermediate_rxbuffer_memory() */ - -/* - * mgsl_free_intermediate_rxbuffer_memory() - * - * - * Arguments: - * - * info pointer to device instance data - * - * Return Value: None - */ -static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info) -{ - kfree(info->intermediate_rxbuffer); - info->intermediate_rxbuffer = NULL; - -} /* end of mgsl_free_intermediate_rxbuffer_memory() */ - -/* - * mgsl_alloc_intermediate_txbuffer_memory() - * - * Allocate intermdiate transmit buffer(s) large enough to hold max_frame_size. - * This buffer is used to load transmit frames into the adapter's dma transfer - * buffers when there is sufficient space. - * - * Arguments: - * - * info pointer to device instance data - * - * Return Value: 0 if success, otherwise -ENOMEM - */ -static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info) -{ - int i; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s %s(%d) allocating %d tx holding buffers\n", - info->device_name, __FILE__,__LINE__,info->num_tx_holding_buffers); - - memset(info->tx_holding_buffers,0,sizeof(info->tx_holding_buffers)); - - for ( i=0; inum_tx_holding_buffers; ++i) { - info->tx_holding_buffers[i].buffer = - kmalloc(info->max_frame_size, GFP_KERNEL); - if (info->tx_holding_buffers[i].buffer == NULL) { - for (--i; i >= 0; i--) { - kfree(info->tx_holding_buffers[i].buffer); - info->tx_holding_buffers[i].buffer = NULL; - } - return -ENOMEM; - } - } - - return 0; - -} /* end of mgsl_alloc_intermediate_txbuffer_memory() */ - -/* - * mgsl_free_intermediate_txbuffer_memory() - * - * - * Arguments: - * - * info pointer to device instance data - * - * Return Value: None - */ -static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info) -{ - int i; - - for ( i=0; inum_tx_holding_buffers; ++i ) { - kfree(info->tx_holding_buffers[i].buffer); - info->tx_holding_buffers[i].buffer = NULL; - } - - info->get_tx_holding_index = 0; - info->put_tx_holding_index = 0; - info->tx_holding_count = 0; - -} /* end of mgsl_free_intermediate_txbuffer_memory() */ - - -/* - * load_next_tx_holding_buffer() - * - * attempts to load the next buffered tx request into the - * tx dma buffers - * - * Arguments: - * - * info pointer to device instance data - * - * Return Value: true if next buffered tx request loaded - * into adapter's tx dma buffer, - * false otherwise - */ -static bool load_next_tx_holding_buffer(struct mgsl_struct *info) -{ - bool ret = false; - - if ( info->tx_holding_count ) { - /* determine if we have enough tx dma buffers - * to accommodate the next tx frame - */ - struct tx_holding_buffer *ptx = - &info->tx_holding_buffers[info->get_tx_holding_index]; - int num_free = num_free_tx_dma_buffers(info); - int num_needed = ptx->buffer_size / DMABUFFERSIZE; - if ( ptx->buffer_size % DMABUFFERSIZE ) - ++num_needed; - - if (num_needed <= num_free) { - info->xmit_cnt = ptx->buffer_size; - mgsl_load_tx_dma_buffer(info,ptx->buffer,ptx->buffer_size); - - --info->tx_holding_count; - if ( ++info->get_tx_holding_index >= info->num_tx_holding_buffers) - info->get_tx_holding_index=0; - - /* restart transmit timer */ - mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000)); - - ret = true; - } - } - - return ret; -} - -/* - * save_tx_buffer_request() - * - * attempt to store transmit frame request for later transmission - * - * Arguments: - * - * info pointer to device instance data - * Buffer pointer to buffer containing frame to load - * BufferSize size in bytes of frame in Buffer - * - * Return Value: 1 if able to store, 0 otherwise - */ -static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize) -{ - struct tx_holding_buffer *ptx; - - if ( info->tx_holding_count >= info->num_tx_holding_buffers ) { - return 0; /* all buffers in use */ - } - - ptx = &info->tx_holding_buffers[info->put_tx_holding_index]; - ptx->buffer_size = BufferSize; - memcpy( ptx->buffer, Buffer, BufferSize); - - ++info->tx_holding_count; - if ( ++info->put_tx_holding_index >= info->num_tx_holding_buffers) - info->put_tx_holding_index=0; - - return 1; -} - -static int mgsl_claim_resources(struct mgsl_struct *info) -{ - if (request_region(info->io_base,info->io_addr_size,"synclink") == NULL) { - printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->io_base); - return -ENODEV; - } - info->io_addr_requested = true; - - if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags, - info->device_name, info ) < 0 ) { - printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n", - __FILE__,__LINE__,info->device_name, info->irq_level ); - goto errout; - } - info->irq_requested = true; - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - if (request_mem_region(info->phys_memory_base,0x40000,"synclink") == NULL) { - printk( "%s(%d):mem addr conflict device %s Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base); - goto errout; - } - info->shared_mem_requested = true; - if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclink") == NULL) { - printk( "%s(%d):lcr mem addr conflict device %s Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_lcr_base + info->lcr_offset); - goto errout; - } - info->lcr_mem_requested = true; - - info->memory_base = ioremap_nocache(info->phys_memory_base, - 0x40000); - if (!info->memory_base) { - printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base ); - goto errout; - } - - if ( !mgsl_memory_test(info) ) { - printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base ); - goto errout; - } - - info->lcr_base = ioremap_nocache(info->phys_lcr_base, - PAGE_SIZE); - if (!info->lcr_base) { - printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); - goto errout; - } - info->lcr_base += info->lcr_offset; - - } else { - /* claim DMA channel */ - - if (request_dma(info->dma_level,info->device_name) < 0){ - printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n", - __FILE__,__LINE__,info->device_name, info->dma_level ); - mgsl_release_resources( info ); - return -ENODEV; - } - info->dma_requested = true; - - /* ISA adapter uses bus master DMA */ - set_dma_mode(info->dma_level,DMA_MODE_CASCADE); - enable_dma(info->dma_level); - } - - if ( mgsl_allocate_dma_buffers(info) < 0 ) { - printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n", - __FILE__,__LINE__,info->device_name, info->dma_level ); - goto errout; - } - - return 0; -errout: - mgsl_release_resources(info); - return -ENODEV; - -} /* end of mgsl_claim_resources() */ - -static void mgsl_release_resources(struct mgsl_struct *info) -{ - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_release_resources(%s) entry\n", - __FILE__,__LINE__,info->device_name ); - - if ( info->irq_requested ) { - free_irq(info->irq_level, info); - info->irq_requested = false; - } - if ( info->dma_requested ) { - disable_dma(info->dma_level); - free_dma(info->dma_level); - info->dma_requested = false; - } - mgsl_free_dma_buffers(info); - mgsl_free_intermediate_rxbuffer_memory(info); - mgsl_free_intermediate_txbuffer_memory(info); - - if ( info->io_addr_requested ) { - release_region(info->io_base,info->io_addr_size); - info->io_addr_requested = false; - } - if ( info->shared_mem_requested ) { - release_mem_region(info->phys_memory_base,0x40000); - info->shared_mem_requested = false; - } - if ( info->lcr_mem_requested ) { - release_mem_region(info->phys_lcr_base + info->lcr_offset,128); - info->lcr_mem_requested = false; - } - if (info->memory_base){ - iounmap(info->memory_base); - info->memory_base = NULL; - } - if (info->lcr_base){ - iounmap(info->lcr_base - info->lcr_offset); - info->lcr_base = NULL; - } - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_release_resources(%s) exit\n", - __FILE__,__LINE__,info->device_name ); - -} /* end of mgsl_release_resources() */ - -/* mgsl_add_device() - * - * Add the specified device instance data structure to the - * global linked list of devices and increment the device count. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_add_device( struct mgsl_struct *info ) -{ - info->next_device = NULL; - info->line = mgsl_device_count; - sprintf(info->device_name,"ttySL%d",info->line); - - if (info->line < MAX_TOTAL_DEVICES) { - if (maxframe[info->line]) - info->max_frame_size = maxframe[info->line]; - - if (txdmabufs[info->line]) { - info->num_tx_dma_buffers = txdmabufs[info->line]; - if (info->num_tx_dma_buffers < 1) - info->num_tx_dma_buffers = 1; - } - - if (txholdbufs[info->line]) { - info->num_tx_holding_buffers = txholdbufs[info->line]; - if (info->num_tx_holding_buffers < 1) - info->num_tx_holding_buffers = 1; - else if (info->num_tx_holding_buffers > MAX_TX_HOLDING_BUFFERS) - info->num_tx_holding_buffers = MAX_TX_HOLDING_BUFFERS; - } - } - - mgsl_device_count++; - - if ( !mgsl_device_list ) - mgsl_device_list = info; - else { - struct mgsl_struct *current_dev = mgsl_device_list; - while( current_dev->next_device ) - current_dev = current_dev->next_device; - current_dev->next_device = info; - } - - if ( info->max_frame_size < 4096 ) - info->max_frame_size = 4096; - else if ( info->max_frame_size > 65535 ) - info->max_frame_size = 65535; - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - printk( "SyncLink PCI v%d %s: IO=%04X IRQ=%d Mem=%08X,%08X MaxFrameSize=%u\n", - info->hw_version + 1, info->device_name, info->io_base, info->irq_level, - info->phys_memory_base, info->phys_lcr_base, - info->max_frame_size ); - } else { - printk( "SyncLink ISA %s: IO=%04X IRQ=%d DMA=%d MaxFrameSize=%u\n", - info->device_name, info->io_base, info->irq_level, info->dma_level, - info->max_frame_size ); - } - -#if SYNCLINK_GENERIC_HDLC - hdlcdev_init(info); -#endif - -} /* end of mgsl_add_device() */ - -static const struct tty_port_operations mgsl_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - - -/* mgsl_allocate_device() - * - * Allocate and initialize a device instance structure - * - * Arguments: none - * Return Value: pointer to mgsl_struct if success, otherwise NULL - */ -static struct mgsl_struct* mgsl_allocate_device(void) -{ - struct mgsl_struct *info; - - info = kzalloc(sizeof(struct mgsl_struct), - GFP_KERNEL); - - if (!info) { - printk("Error can't allocate device instance data\n"); - } else { - tty_port_init(&info->port); - info->port.ops = &mgsl_port_ops; - info->magic = MGSL_MAGIC; - INIT_WORK(&info->task, mgsl_bh_handler); - info->max_frame_size = 4096; - info->port.close_delay = 5*HZ/10; - info->port.closing_wait = 30*HZ; - init_waitqueue_head(&info->status_event_wait_q); - init_waitqueue_head(&info->event_wait_q); - spin_lock_init(&info->irq_spinlock); - spin_lock_init(&info->netlock); - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; - info->num_tx_dma_buffers = 1; - info->num_tx_holding_buffers = 0; - } - - return info; - -} /* end of mgsl_allocate_device()*/ - -static const struct tty_operations mgsl_ops = { - .open = mgsl_open, - .close = mgsl_close, - .write = mgsl_write, - .put_char = mgsl_put_char, - .flush_chars = mgsl_flush_chars, - .write_room = mgsl_write_room, - .chars_in_buffer = mgsl_chars_in_buffer, - .flush_buffer = mgsl_flush_buffer, - .ioctl = mgsl_ioctl, - .throttle = mgsl_throttle, - .unthrottle = mgsl_unthrottle, - .send_xchar = mgsl_send_xchar, - .break_ctl = mgsl_break, - .wait_until_sent = mgsl_wait_until_sent, - .set_termios = mgsl_set_termios, - .stop = mgsl_stop, - .start = mgsl_start, - .hangup = mgsl_hangup, - .tiocmget = tiocmget, - .tiocmset = tiocmset, - .get_icount = msgl_get_icount, - .proc_fops = &mgsl_proc_fops, -}; - -/* - * perform tty device initialization - */ -static int mgsl_init_tty(void) -{ - int rc; - - serial_driver = alloc_tty_driver(128); - if (!serial_driver) - return -ENOMEM; - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = "synclink"; - serial_driver->name = "ttySL"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - 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->init_termios.c_ispeed = 9600; - serial_driver->init_termios.c_ospeed = 9600; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &mgsl_ops); - if ((rc = tty_register_driver(serial_driver)) < 0) { - printk("%s(%d):Couldn't register serial driver\n", - __FILE__,__LINE__); - put_tty_driver(serial_driver); - serial_driver = NULL; - return rc; - } - - printk("%s %s, tty major#%d\n", - driver_name, driver_version, - serial_driver->major); - return 0; -} - -/* enumerate user specified ISA adapters - */ -static void mgsl_enum_isa_devices(void) -{ - struct mgsl_struct *info; - int i; - - /* Check for user specified ISA devices */ - - for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){ - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("ISA device specified io=%04X,irq=%d,dma=%d\n", - io[i], irq[i], dma[i] ); - - info = mgsl_allocate_device(); - if ( !info ) { - /* error allocating device instance data */ - if ( debug_level >= DEBUG_LEVEL_ERROR ) - printk( "can't allocate device instance data.\n"); - continue; - } - - /* Copy user configuration info to device instance data */ - info->io_base = (unsigned int)io[i]; - info->irq_level = (unsigned int)irq[i]; - info->irq_level = irq_canonicalize(info->irq_level); - info->dma_level = (unsigned int)dma[i]; - info->bus_type = MGSL_BUS_TYPE_ISA; - info->io_addr_size = 16; - info->irq_flags = 0; - - mgsl_add_device( info ); - } -} - -static void synclink_cleanup(void) -{ - int rc; - struct mgsl_struct *info; - struct mgsl_struct *tmp; - - printk("Unloading %s: %s\n", driver_name, driver_version); - - if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) - printk("%s(%d) failed to unregister tty driver err=%d\n", - __FILE__,__LINE__,rc); - put_tty_driver(serial_driver); - } - - info = mgsl_device_list; - while(info) { -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - mgsl_release_resources(info); - tmp = info; - info = info->next_device; - kfree(tmp); - } - - if (pci_registered) - pci_unregister_driver(&synclink_pci_driver); -} - -static int __init synclink_init(void) -{ - int rc; - - if (break_on_load) { - mgsl_get_text_ptr(); - BREAKPOINT(); - } - - printk("%s %s\n", driver_name, driver_version); - - mgsl_enum_isa_devices(); - if ((rc = pci_register_driver(&synclink_pci_driver)) < 0) - printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc); - else - pci_registered = true; - - if ((rc = mgsl_init_tty()) < 0) - goto error; - - return 0; - -error: - synclink_cleanup(); - return rc; -} - -static void __exit synclink_exit(void) -{ - synclink_cleanup(); -} - -module_init(synclink_init); -module_exit(synclink_exit); - -/* - * usc_RTCmd() - * - * Issue a USC Receive/Transmit command to the - * Channel Command/Address Register (CCAR). - * - * Notes: - * - * The command is encoded in the most significant 5 bits <15..11> - * of the CCAR value. Bits <10..7> of the CCAR must be preserved - * and Bits <6..0> must be written as zeros. - * - * Arguments: - * - * info pointer to device information structure - * Cmd command mask (use symbolic macros) - * - * Return Value: - * - * None - */ -static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ) -{ - /* output command to CCAR in bits <15..11> */ - /* preserve bits <10..7>, bits <6..0> must be zero */ - - outw( Cmd + info->loopback_bits, info->io_base + CCAR ); - - /* Read to flush write to CCAR */ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - inw( info->io_base + CCAR ); - -} /* end of usc_RTCmd() */ - -/* - * usc_DmaCmd() - * - * Issue a DMA command to the DMA Command/Address Register (DCAR). - * - * Arguments: - * - * info pointer to device information structure - * Cmd DMA command mask (usc_DmaCmd_XX Macros) - * - * Return Value: - * - * None - */ -static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ) -{ - /* write command mask to DCAR */ - outw( Cmd + info->mbre_bit, info->io_base ); - - /* Read to flush write to DCAR */ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - inw( info->io_base ); - -} /* end of usc_DmaCmd() */ - -/* - * usc_OutDmaReg() - * - * Write a 16-bit value to a USC DMA register - * - * Arguments: - * - * info pointer to device info structure - * RegAddr register address (number) for write - * RegValue 16-bit value to write to register - * - * Return Value: - * - * None - * - */ -static void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) -{ - /* Note: The DCAR is located at the adapter base address */ - /* Note: must preserve state of BIT8 in DCAR */ - - outw( RegAddr + info->mbre_bit, info->io_base ); - outw( RegValue, info->io_base ); - - /* Read to flush write to DCAR */ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - inw( info->io_base ); - -} /* end of usc_OutDmaReg() */ - -/* - * usc_InDmaReg() - * - * Read a 16-bit value from a DMA register - * - * Arguments: - * - * info pointer to device info structure - * RegAddr register address (number) to read from - * - * Return Value: - * - * The 16-bit value read from register - * - */ -static u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr ) -{ - /* Note: The DCAR is located at the adapter base address */ - /* Note: must preserve state of BIT8 in DCAR */ - - outw( RegAddr + info->mbre_bit, info->io_base ); - return inw( info->io_base ); - -} /* end of usc_InDmaReg() */ - -/* - * - * usc_OutReg() - * - * Write a 16-bit value to a USC serial channel register - * - * Arguments: - * - * info pointer to device info structure - * RegAddr register address (number) to write to - * RegValue 16-bit value to write to register - * - * Return Value: - * - * None - * - */ -static void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) -{ - outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); - outw( RegValue, info->io_base + CCAR ); - - /* Read to flush write to CCAR */ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - inw( info->io_base + CCAR ); - -} /* end of usc_OutReg() */ - -/* - * usc_InReg() - * - * Reads a 16-bit value from a USC serial channel register - * - * Arguments: - * - * info pointer to device extension - * RegAddr register address (number) to read from - * - * Return Value: - * - * 16-bit value read from register - */ -static u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr ) -{ - outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); - return inw( info->io_base + CCAR ); - -} /* end of usc_InReg() */ - -/* usc_set_sdlc_mode() - * - * Set up the adapter for SDLC DMA communications. - * - * Arguments: info pointer to device instance data - * Return Value: NONE - */ -static void usc_set_sdlc_mode( struct mgsl_struct *info ) -{ - u16 RegValue; - bool PreSL1660; - - /* - * determine if the IUSC on the adapter is pre-SL1660. If - * not, take advantage of the UnderWait feature of more - * modern chips. If an underrun occurs and this bit is set, - * the transmitter will idle the programmed idle pattern - * until the driver has time to service the underrun. Otherwise, - * the dma controller may get the cycles previously requested - * and begin transmitting queued tx data. - */ - usc_OutReg(info,TMCR,0x1f); - RegValue=usc_InReg(info,TMDR); - PreSL1660 = (RegValue == IUSC_PRE_SL1660); - - if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) - { - /* - ** Channel Mode Register (CMR) - ** - ** <15..14> 10 Tx Sub Modes, Send Flag on Underrun - ** <13> 0 0 = Transmit Disabled (initially) - ** <12> 0 1 = Consecutive Idles share common 0 - ** <11..8> 1110 Transmitter Mode = HDLC/SDLC Loop - ** <7..4> 0000 Rx Sub Modes, addr/ctrl field handling - ** <3..0> 0110 Receiver Mode = HDLC/SDLC - ** - ** 1000 1110 0000 0110 = 0x8e06 - */ - RegValue = 0x8e06; - - /*-------------------------------------------------- - * ignore user options for UnderRun Actions and - * preambles - *--------------------------------------------------*/ - } - else - { - /* Channel mode Register (CMR) - * - * <15..14> 00 Tx Sub modes, Underrun Action - * <13> 0 1 = Send Preamble before opening flag - * <12> 0 1 = Consecutive Idles share common 0 - * <11..8> 0110 Transmitter mode = HDLC/SDLC - * <7..4> 0000 Rx Sub modes, addr/ctrl field handling - * <3..0> 0110 Receiver mode = HDLC/SDLC - * - * 0000 0110 0000 0110 = 0x0606 - */ - if (info->params.mode == MGSL_MODE_RAW) { - RegValue = 0x0001; /* Set Receive mode = external sync */ - - usc_OutReg( info, IOCR, /* Set IOCR DCD is RxSync Detect Input */ - (unsigned short)((usc_InReg(info, IOCR) & ~(BIT13|BIT12)) | BIT12)); - - /* - * TxSubMode: - * CMR <15> 0 Don't send CRC on Tx Underrun - * CMR <14> x undefined - * CMR <13> 0 Send preamble before openning sync - * CMR <12> 0 Send 8-bit syncs, 1=send Syncs per TxLength - * - * TxMode: - * CMR <11-8) 0100 MonoSync - * - * 0x00 0100 xxxx xxxx 04xx - */ - RegValue |= 0x0400; - } - else { - - RegValue = 0x0606; - - if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) - RegValue |= BIT14; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) - RegValue |= BIT15; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) - RegValue |= BIT15 + BIT14; - } - - if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) - RegValue |= BIT13; - } - - if ( info->params.mode == MGSL_MODE_HDLC && - (info->params.flags & HDLC_FLAG_SHARE_ZERO) ) - RegValue |= BIT12; - - if ( info->params.addr_filter != 0xff ) - { - /* set up receive address filtering */ - usc_OutReg( info, RSR, info->params.addr_filter ); - RegValue |= BIT4; - } - - usc_OutReg( info, CMR, RegValue ); - info->cmr_value = RegValue; - - /* Receiver mode Register (RMR) - * - * <15..13> 000 encoding - * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) - * <10> 1 1 = Set CRC to all 1s (use for SDLC/HDLC) - * <9> 0 1 = Include Receive chars in CRC - * <8> 1 1 = Use Abort/PE bit as abort indicator - * <7..6> 00 Even parity - * <5> 0 parity disabled - * <4..2> 000 Receive Char Length = 8 bits - * <1..0> 00 Disable Receiver - * - * 0000 0101 0000 0000 = 0x0500 - */ - - RegValue = 0x0500; - - switch ( info->params.encoding ) { - case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; - case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; - case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; - } - - if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) - RegValue |= BIT9; - else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) - RegValue |= ( BIT12 | BIT10 | BIT9 ); - - usc_OutReg( info, RMR, RegValue ); - - /* Set the Receive count Limit Register (RCLR) to 0xffff. */ - /* When an opening flag of an SDLC frame is recognized the */ - /* Receive Character count (RCC) is loaded with the value in */ - /* RCLR. The RCC is decremented for each received byte. The */ - /* value of RCC is stored after the closing flag of the frame */ - /* allowing the frame size to be computed. */ - - usc_OutReg( info, RCLR, RCLRVALUE ); - - usc_RCmd( info, RCmd_SelectRicrdma_level ); - - /* Receive Interrupt Control Register (RICR) - * - * <15..8> ? RxFIFO DMA Request Level - * <7> 0 Exited Hunt IA (Interrupt Arm) - * <6> 0 Idle Received IA - * <5> 0 Break/Abort IA - * <4> 0 Rx Bound IA - * <3> 1 Queued status reflects oldest 2 bytes in FIFO - * <2> 0 Abort/PE IA - * <1> 1 Rx Overrun IA - * <0> 0 Select TC0 value for readback - * - * 0000 0000 0000 1000 = 0x000a - */ - - /* Carry over the Exit Hunt and Idle Received bits */ - /* in case they have been armed by usc_ArmEvents. */ - - RegValue = usc_InReg( info, RICR ) & 0xc0; - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - usc_OutReg( info, RICR, (u16)(0x030a | RegValue) ); - else - usc_OutReg( info, RICR, (u16)(0x140a | RegValue) ); - - /* Unlatch all Rx status bits and clear Rx status IRQ Pending */ - - usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); - - /* Transmit mode Register (TMR) - * - * <15..13> 000 encoding - * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) - * <10> 1 1 = Start CRC as all 1s (use for SDLC/HDLC) - * <9> 0 1 = Tx CRC Enabled - * <8> 0 1 = Append CRC to end of transmit frame - * <7..6> 00 Transmit parity Even - * <5> 0 Transmit parity Disabled - * <4..2> 000 Tx Char Length = 8 bits - * <1..0> 00 Disable Transmitter - * - * 0000 0100 0000 0000 = 0x0400 - */ - - RegValue = 0x0400; - - switch ( info->params.encoding ) { - case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; - case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; - case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; - } - - if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) - RegValue |= BIT9 + BIT8; - else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) - RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8); - - usc_OutReg( info, TMR, RegValue ); - - usc_set_txidle( info ); - - - usc_TCmd( info, TCmd_SelectTicrdma_level ); - - /* Transmit Interrupt Control Register (TICR) - * - * <15..8> ? Transmit FIFO DMA Level - * <7> 0 Present IA (Interrupt Arm) - * <6> 0 Idle Sent IA - * <5> 1 Abort Sent IA - * <4> 1 EOF/EOM Sent IA - * <3> 0 CRC Sent IA - * <2> 1 1 = Wait for SW Trigger to Start Frame - * <1> 1 Tx Underrun IA - * <0> 0 TC0 constant on read back - * - * 0000 0000 0011 0110 = 0x0036 - */ - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - usc_OutReg( info, TICR, 0x0736 ); - else - usc_OutReg( info, TICR, 0x1436 ); - - usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); - - /* - ** Transmit Command/Status Register (TCSR) - ** - ** <15..12> 0000 TCmd - ** <11> 0/1 UnderWait - ** <10..08> 000 TxIdle - ** <7> x PreSent - ** <6> x IdleSent - ** <5> x AbortSent - ** <4> x EOF/EOM Sent - ** <3> x CRC Sent - ** <2> x All Sent - ** <1> x TxUnder - ** <0> x TxEmpty - ** - ** 0000 0000 0000 0000 = 0x0000 - */ - info->tcsr_value = 0; - - if ( !PreSL1660 ) - info->tcsr_value |= TCSR_UNDERWAIT; - - usc_OutReg( info, TCSR, info->tcsr_value ); - - /* Clock mode Control Register (CMCR) - * - * <15..14> 00 counter 1 Source = Disabled - * <13..12> 00 counter 0 Source = Disabled - * <11..10> 11 BRG1 Input is TxC Pin - * <9..8> 11 BRG0 Input is TxC Pin - * <7..6> 01 DPLL Input is BRG1 Output - * <5..3> XXX TxCLK comes from Port 0 - * <2..0> XXX RxCLK comes from Port 1 - * - * 0000 1111 0111 0111 = 0x0f77 - */ - - RegValue = 0x0f40; - - if ( info->params.flags & HDLC_FLAG_RXC_DPLL ) - RegValue |= 0x0003; /* RxCLK from DPLL */ - else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) - RegValue |= 0x0004; /* RxCLK from BRG0 */ - else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN) - RegValue |= 0x0006; /* RxCLK from TXC Input */ - else - RegValue |= 0x0007; /* RxCLK from Port1 */ - - if ( info->params.flags & HDLC_FLAG_TXC_DPLL ) - RegValue |= 0x0018; /* TxCLK from DPLL */ - else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) - RegValue |= 0x0020; /* TxCLK from BRG0 */ - else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN) - RegValue |= 0x0038; /* RxCLK from TXC Input */ - else - RegValue |= 0x0030; /* TxCLK from Port0 */ - - usc_OutReg( info, CMCR, RegValue ); - - - /* Hardware Configuration Register (HCR) - * - * <15..14> 00 CTR0 Divisor:00=32,01=16,10=8,11=4 - * <13> 0 CTR1DSel:0=CTR0Div determines CTR0Div - * <12> 0 CVOK:0=report code violation in biphase - * <11..10> 00 DPLL Divisor:00=32,01=16,10=8,11=4 - * <9..8> XX DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level - * <7..6> 00 reserved - * <5> 0 BRG1 mode:0=continuous,1=single cycle - * <4> X BRG1 Enable - * <3..2> 00 reserved - * <1> 0 BRG0 mode:0=continuous,1=single cycle - * <0> 0 BRG0 Enable - */ - - RegValue = 0x0000; - - if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { - u32 XtalSpeed; - u32 DpllDivisor; - u16 Tc; - - /* DPLL is enabled. Use BRG1 to provide continuous reference clock */ - /* for DPLL. DPLL mode in HCR is dependent on the encoding used. */ - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - XtalSpeed = 11059200; - else - XtalSpeed = 14745600; - - if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { - DpllDivisor = 16; - RegValue |= BIT10; - } - else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { - DpllDivisor = 8; - RegValue |= BIT11; - } - else - DpllDivisor = 32; - - /* Tc = (Xtal/Speed) - 1 */ - /* If twice the remainder of (Xtal/Speed) is greater than Speed */ - /* then rounding up gives a more precise time constant. Instead */ - /* of rounding up and then subtracting 1 we just don't subtract */ - /* the one in this case. */ - - /*-------------------------------------------------- - * ejz: for DPLL mode, application should use the - * same clock speed as the partner system, even - * though clocking is derived from the input RxData. - * In case the user uses a 0 for the clock speed, - * default to 0xffffffff and don't try to divide by - * zero - *--------------------------------------------------*/ - if ( info->params.clock_speed ) - { - Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); - if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) - / info->params.clock_speed) ) - Tc--; - } - else - Tc = -1; - - - /* Write 16-bit Time Constant for BRG1 */ - usc_OutReg( info, TC1R, Tc ); - - RegValue |= BIT4; /* enable BRG1 */ - - switch ( info->params.encoding ) { - case HDLC_ENCODING_NRZ: - case HDLC_ENCODING_NRZB: - case HDLC_ENCODING_NRZI_MARK: - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break; - case HDLC_ENCODING_BIPHASE_MARK: - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; - case HDLC_ENCODING_BIPHASE_LEVEL: - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; - } - } - - usc_OutReg( info, HCR, RegValue ); - - - /* Channel Control/status Register (CCSR) - * - * <15> X RCC FIFO Overflow status (RO) - * <14> X RCC FIFO Not Empty status (RO) - * <13> 0 1 = Clear RCC FIFO (WO) - * <12> X DPLL Sync (RW) - * <11> X DPLL 2 Missed Clocks status (RO) - * <10> X DPLL 1 Missed Clock status (RO) - * <9..8> 00 DPLL Resync on rising and falling edges (RW) - * <7> X SDLC Loop On status (RO) - * <6> X SDLC Loop Send status (RO) - * <5> 1 Bypass counters for TxClk and RxClk (RW) - * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) - * <1..0> 00 reserved - * - * 0000 0000 0010 0000 = 0x0020 - */ - - usc_OutReg( info, CCSR, 0x1020 ); - - - if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) { - usc_OutReg( info, SICR, - (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) ); - } - - - /* enable Master Interrupt Enable bit (MIE) */ - usc_EnableMasterIrqBit( info ); - - usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + - TRANSMIT_STATUS + TRANSMIT_DATA + MISC); - - /* arm RCC underflow interrupt */ - usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3)); - usc_EnableInterrupts(info, MISC); - - info->mbre_bit = 0; - outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ - usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ - info->mbre_bit = BIT8; - outw( BIT8, info->io_base ); /* set Master Bus Enable (DCAR) */ - - if (info->bus_type == MGSL_BUS_TYPE_ISA) { - /* Enable DMAEN (Port 7, Bit 14) */ - /* This connects the DMA request signal to the ISA bus */ - usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14)); - } - - /* DMA Control Register (DCR) - * - * <15..14> 10 Priority mode = Alternating Tx/Rx - * 01 Rx has priority - * 00 Tx has priority - * - * <13> 1 Enable Priority Preempt per DCR<15..14> - * (WARNING DCR<11..10> must be 00 when this is 1) - * 0 Choose activate channel per DCR<11..10> - * - * <12> 0 Little Endian for Array/List - * <11..10> 00 Both Channels can use each bus grant - * <9..6> 0000 reserved - * <5> 0 7 CLK - Minimum Bus Re-request Interval - * <4> 0 1 = drive D/C and S/D pins - * <3> 1 1 = Add one wait state to all DMA cycles. - * <2> 0 1 = Strobe /UAS on every transfer. - * <1..0> 11 Addr incrementing only affects LS24 bits - * - * 0110 0000 0000 1011 = 0x600b - */ - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - /* PCI adapter does not need DMA wait state */ - usc_OutDmaReg( info, DCR, 0xa00b ); - } - else - usc_OutDmaReg( info, DCR, 0x800b ); - - - /* Receive DMA mode Register (RDMR) - * - * <15..14> 11 DMA mode = Linked List Buffer mode - * <13> 1 RSBinA/L = store Rx status Block in Arrary/List entry - * <12> 1 Clear count of List Entry after fetching - * <11..10> 00 Address mode = Increment - * <9> 1 Terminate Buffer on RxBound - * <8> 0 Bus Width = 16bits - * <7..0> ? status Bits (write as 0s) - * - * 1111 0010 0000 0000 = 0xf200 - */ - - usc_OutDmaReg( info, RDMR, 0xf200 ); - - - /* Transmit DMA mode Register (TDMR) - * - * <15..14> 11 DMA mode = Linked List Buffer mode - * <13> 1 TCBinA/L = fetch Tx Control Block from List entry - * <12> 1 Clear count of List Entry after fetching - * <11..10> 00 Address mode = Increment - * <9> 1 Terminate Buffer on end of frame - * <8> 0 Bus Width = 16bits - * <7..0> ? status Bits (Read Only so write as 0) - * - * 1111 0010 0000 0000 = 0xf200 - */ - - usc_OutDmaReg( info, TDMR, 0xf200 ); - - - /* DMA Interrupt Control Register (DICR) - * - * <15> 1 DMA Interrupt Enable - * <14> 0 1 = Disable IEO from USC - * <13> 0 1 = Don't provide vector during IntAck - * <12> 1 1 = Include status in Vector - * <10..2> 0 reserved, Must be 0s - * <1> 0 1 = Rx DMA Interrupt Enabled - * <0> 0 1 = Tx DMA Interrupt Enabled - * - * 1001 0000 0000 0000 = 0x9000 - */ - - usc_OutDmaReg( info, DICR, 0x9000 ); - - usc_InDmaReg( info, RDMR ); /* clear pending receive DMA IRQ bits */ - usc_InDmaReg( info, TDMR ); /* clear pending transmit DMA IRQ bits */ - usc_OutDmaReg( info, CDIR, 0x0303 ); /* clear IUS and Pending for Tx and Rx */ - - /* Channel Control Register (CCR) - * - * <15..14> 10 Use 32-bit Tx Control Blocks (TCBs) - * <13> 0 Trigger Tx on SW Command Disabled - * <12> 0 Flag Preamble Disabled - * <11..10> 00 Preamble Length - * <9..8> 00 Preamble Pattern - * <7..6> 10 Use 32-bit Rx status Blocks (RSBs) - * <5> 0 Trigger Rx on SW Command Disabled - * <4..0> 0 reserved - * - * 1000 0000 1000 0000 = 0x8080 - */ - - RegValue = 0x8080; - - switch ( info->params.preamble_length ) { - case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; - case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; - case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; - } - - switch ( info->params.preamble ) { - case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; - case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; - case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; - case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; - } - - usc_OutReg( info, CCR, RegValue ); - - - /* - * Burst/Dwell Control Register - * - * <15..8> 0x20 Maximum number of transfers per bus grant - * <7..0> 0x00 Maximum number of clock cycles per bus grant - */ - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - /* don't limit bus occupancy on PCI adapter */ - usc_OutDmaReg( info, BDCR, 0x0000 ); - } - else - usc_OutDmaReg( info, BDCR, 0x2000 ); - - usc_stop_transmitter(info); - usc_stop_receiver(info); - -} /* end of usc_set_sdlc_mode() */ - -/* usc_enable_loopback() - * - * Set the 16C32 for internal loopback mode. - * The TxCLK and RxCLK signals are generated from the BRG0 and - * the TxD is looped back to the RxD internally. - * - * Arguments: info pointer to device instance data - * enable 1 = enable loopback, 0 = disable - * Return Value: None - */ -static void usc_enable_loopback(struct mgsl_struct *info, int enable) -{ - if (enable) { - /* blank external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); - - /* Clock mode Control Register (CMCR) - * - * <15..14> 00 counter 1 Disabled - * <13..12> 00 counter 0 Disabled - * <11..10> 11 BRG1 Input is TxC Pin - * <9..8> 11 BRG0 Input is TxC Pin - * <7..6> 01 DPLL Input is BRG1 Output - * <5..3> 100 TxCLK comes from BRG0 - * <2..0> 100 RxCLK comes from BRG0 - * - * 0000 1111 0110 0100 = 0x0f64 - */ - - usc_OutReg( info, CMCR, 0x0f64 ); - - /* Write 16-bit Time Constant for BRG0 */ - /* use clock speed if available, otherwise use 8 for diagnostics */ - if (info->params.clock_speed) { - if (info->bus_type == MGSL_BUS_TYPE_PCI) - usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1)); - else - usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1)); - } else - usc_OutReg(info, TC0R, (u16)8); - - /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0 - mode = Continuous Set Bit 0 to enable BRG0. */ - usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); - - /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ - usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004)); - - /* set Internal Data loopback mode */ - info->loopback_bits = 0x300; - outw( 0x0300, info->io_base + CCAR ); - } else { - /* enable external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); - - /* clear Internal Data loopback mode */ - info->loopback_bits = 0; - outw( 0,info->io_base + CCAR ); - } - -} /* end of usc_enable_loopback() */ - -/* usc_enable_aux_clock() - * - * Enabled the AUX clock output at the specified frequency. - * - * Arguments: - * - * info pointer to device extension - * data_rate data rate of clock in bits per second - * A data rate of 0 disables the AUX clock. - * - * Return Value: None - */ -static void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate ) -{ - u32 XtalSpeed; - u16 Tc; - - if ( data_rate ) { - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - XtalSpeed = 11059200; - else - XtalSpeed = 14745600; - - - /* Tc = (Xtal/Speed) - 1 */ - /* If twice the remainder of (Xtal/Speed) is greater than Speed */ - /* then rounding up gives a more precise time constant. Instead */ - /* of rounding up and then subtracting 1 we just don't subtract */ - /* the one in this case. */ - - - Tc = (u16)(XtalSpeed/data_rate); - if ( !(((XtalSpeed % data_rate) * 2) / data_rate) ) - Tc--; - - /* Write 16-bit Time Constant for BRG0 */ - usc_OutReg( info, TC0R, Tc ); - - /* - * Hardware Configuration Register (HCR) - * Clear Bit 1, BRG0 mode = Continuous - * Set Bit 0 to enable BRG0. - */ - - usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); - - /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ - usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); - } else { - /* data rate == 0 so turn off BRG0 */ - usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); - } - -} /* end of usc_enable_aux_clock() */ - -/* - * - * usc_process_rxoverrun_sync() - * - * This function processes a receive overrun by resetting the - * receive DMA buffers and issuing a Purge Rx FIFO command - * to allow the receiver to continue receiving. - * - * Arguments: - * - * info pointer to device extension - * - * Return Value: None - */ -static void usc_process_rxoverrun_sync( struct mgsl_struct *info ) -{ - int start_index; - int end_index; - int frame_start_index; - bool start_of_frame_found = false; - bool end_of_frame_found = false; - bool reprogram_dma = false; - - DMABUFFERENTRY *buffer_list = info->rx_buffer_list; - u32 phys_addr; - - usc_DmaCmd( info, DmaCmd_PauseRxChannel ); - usc_RCmd( info, RCmd_EnterHuntmode ); - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - - /* CurrentRxBuffer points to the 1st buffer of the next */ - /* possibly available receive frame. */ - - frame_start_index = start_index = end_index = info->current_rx_buffer; - - /* Search for an unfinished string of buffers. This means */ - /* that a receive frame started (at least one buffer with */ - /* count set to zero) but there is no terminiting buffer */ - /* (status set to non-zero). */ - - while( !buffer_list[end_index].count ) - { - /* Count field has been reset to zero by 16C32. */ - /* This buffer is currently in use. */ - - if ( !start_of_frame_found ) - { - start_of_frame_found = true; - frame_start_index = end_index; - end_of_frame_found = false; - } - - if ( buffer_list[end_index].status ) - { - /* Status field has been set by 16C32. */ - /* This is the last buffer of a received frame. */ - - /* We want to leave the buffers for this frame intact. */ - /* Move on to next possible frame. */ - - start_of_frame_found = false; - end_of_frame_found = true; - } - - /* advance to next buffer entry in linked list */ - end_index++; - if ( end_index == info->rx_buffer_count ) - end_index = 0; - - if ( start_index == end_index ) - { - /* The entire list has been searched with all Counts == 0 and */ - /* all Status == 0. The receive buffers are */ - /* completely screwed, reset all receive buffers! */ - mgsl_reset_rx_dma_buffers( info ); - frame_start_index = 0; - start_of_frame_found = false; - reprogram_dma = true; - break; - } - } - - if ( start_of_frame_found && !end_of_frame_found ) - { - /* There is an unfinished string of receive DMA buffers */ - /* as a result of the receiver overrun. */ - - /* Reset the buffers for the unfinished frame */ - /* and reprogram the receive DMA controller to start */ - /* at the 1st buffer of unfinished frame. */ - - start_index = frame_start_index; - - do - { - *((unsigned long *)&(info->rx_buffer_list[start_index++].count)) = DMABUFFERSIZE; - - /* Adjust index for wrap around. */ - if ( start_index == info->rx_buffer_count ) - start_index = 0; - - } while( start_index != end_index ); - - reprogram_dma = true; - } - - if ( reprogram_dma ) - { - usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA|RECEIVE_STATUS); - usc_UnlatchRxstatusBits(info, RECEIVE_DATA|RECEIVE_STATUS); - - usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); - - /* This empties the receive FIFO and loads the RCC with RCLR */ - usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); - - /* program 16C32 with physical address of 1st DMA buffer entry */ - phys_addr = info->rx_buffer_list[frame_start_index].phys_entry; - usc_OutDmaReg( info, NRARL, (u16)phys_addr ); - usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); - - usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); - usc_EnableInterrupts( info, RECEIVE_STATUS ); - - /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ - /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); - usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); - usc_DmaCmd( info, DmaCmd_InitRxChannel ); - if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) - usc_EnableReceiver(info,ENABLE_AUTO_DCD); - else - usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); - } - else - { - /* This empties the receive FIFO and loads the RCC with RCLR */ - usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - } - -} /* end of usc_process_rxoverrun_sync() */ - -/* usc_stop_receiver() - * - * Disable USC receiver - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_stop_receiver( struct mgsl_struct *info ) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):usc_stop_receiver(%s)\n", - __FILE__,__LINE__, info->device_name ); - - /* Disable receive DMA channel. */ - /* This also disables receive DMA channel interrupts */ - usc_DmaCmd( info, DmaCmd_ResetRxChannel ); - - usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); - usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); - - usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); - - /* This empties the receive FIFO and loads the RCC with RCLR */ - usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - - info->rx_enabled = false; - info->rx_overflow = false; - info->rx_rcc_underrun = false; - -} /* end of stop_receiver() */ - -/* usc_start_receiver() - * - * Enable the USC receiver - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_start_receiver( struct mgsl_struct *info ) -{ - u32 phys_addr; - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):usc_start_receiver(%s)\n", - __FILE__,__LINE__, info->device_name ); - - mgsl_reset_rx_dma_buffers( info ); - usc_stop_receiver( info ); - - usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - - if ( info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW ) { - /* DMA mode Transfers */ - /* Program the DMA controller. */ - /* Enable the DMA controller end of buffer interrupt. */ - - /* program 16C32 with physical address of 1st DMA buffer entry */ - phys_addr = info->rx_buffer_list[0].phys_entry; - usc_OutDmaReg( info, NRARL, (u16)phys_addr ); - usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); - - usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); - usc_EnableInterrupts( info, RECEIVE_STATUS ); - - /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ - /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); - usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); - usc_DmaCmd( info, DmaCmd_InitRxChannel ); - if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) - usc_EnableReceiver(info,ENABLE_AUTO_DCD); - else - usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); - } else { - usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); - usc_EnableInterrupts(info, RECEIVE_DATA); - - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - usc_RCmd( info, RCmd_EnterHuntmode ); - - usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); - } - - usc_OutReg( info, CCSR, 0x1020 ); - - info->rx_enabled = true; - -} /* end of usc_start_receiver() */ - -/* usc_start_transmitter() - * - * Enable the USC transmitter and send a transmit frame if - * one is loaded in the DMA buffers. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_start_transmitter( struct mgsl_struct *info ) -{ - u32 phys_addr; - unsigned int FrameSize; - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):usc_start_transmitter(%s)\n", - __FILE__,__LINE__, info->device_name ); - - if ( info->xmit_cnt ) { - - /* If auto RTS enabled and RTS is inactive, then assert */ - /* RTS and set a flag indicating that the driver should */ - /* negate RTS when the transmission completes. */ - - info->drop_rts_on_tx_done = false; - - if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { - usc_get_serial_signals( info ); - if ( !(info->serial_signals & SerialSignal_RTS) ) { - info->serial_signals |= SerialSignal_RTS; - usc_set_serial_signals( info ); - info->drop_rts_on_tx_done = true; - } - } - - - if ( info->params.mode == MGSL_MODE_ASYNC ) { - if ( !info->tx_active ) { - usc_UnlatchTxstatusBits(info, TXSTATUS_ALL); - usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA); - usc_EnableInterrupts(info, TRANSMIT_DATA); - usc_load_txfifo(info); - } - } else { - /* Disable transmit DMA controller while programming. */ - usc_DmaCmd( info, DmaCmd_ResetTxChannel ); - - /* Transmit DMA buffer is loaded, so program USC */ - /* to send the frame contained in the buffers. */ - - FrameSize = info->tx_buffer_list[info->start_tx_dma_buffer].rcc; - - /* if operating in Raw sync mode, reset the rcc component - * of the tx dma buffer entry, otherwise, the serial controller - * will send a closing sync char after this count. - */ - if ( info->params.mode == MGSL_MODE_RAW ) - info->tx_buffer_list[info->start_tx_dma_buffer].rcc = 0; - - /* Program the Transmit Character Length Register (TCLR) */ - /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ - usc_OutReg( info, TCLR, (u16)FrameSize ); - - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - - /* Program the address of the 1st DMA Buffer Entry in linked list */ - phys_addr = info->tx_buffer_list[info->start_tx_dma_buffer].phys_entry; - usc_OutDmaReg( info, NTARL, (u16)phys_addr ); - usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) ); - - usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); - usc_EnableInterrupts( info, TRANSMIT_STATUS ); - - if ( info->params.mode == MGSL_MODE_RAW && - info->num_tx_dma_buffers > 1 ) { - /* When running external sync mode, attempt to 'stream' transmit */ - /* by filling tx dma buffers as they become available. To do this */ - /* we need to enable Tx DMA EOB Status interrupts : */ - /* */ - /* 1. Arm End of Buffer (EOB) Transmit DMA Interrupt (BIT2 of TDIAR) */ - /* 2. Enable Transmit DMA Interrupts (BIT0 of DICR) */ - - usc_OutDmaReg( info, TDIAR, BIT2|BIT3 ); - usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT0) ); - } - - /* Initialize Transmit DMA Channel */ - usc_DmaCmd( info, DmaCmd_InitTxChannel ); - - usc_TCmd( info, TCmd_SendFrame ); - - mod_timer(&info->tx_timer, jiffies + - msecs_to_jiffies(5000)); - } - info->tx_active = true; - } - - if ( !info->tx_enabled ) { - info->tx_enabled = true; - if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) - usc_EnableTransmitter(info,ENABLE_AUTO_CTS); - else - usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); - } - -} /* end of usc_start_transmitter() */ - -/* usc_stop_transmitter() - * - * Stops the transmitter and DMA - * - * Arguments: info pointer to device isntance data - * Return Value: None - */ -static void usc_stop_transmitter( struct mgsl_struct *info ) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):usc_stop_transmitter(%s)\n", - __FILE__,__LINE__, info->device_name ); - - del_timer(&info->tx_timer); - - usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA ); - usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA ); - - usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL); - usc_DmaCmd( info, DmaCmd_ResetTxChannel ); - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - - info->tx_enabled = false; - info->tx_active = false; - -} /* end of usc_stop_transmitter() */ - -/* usc_load_txfifo() - * - * Fill the transmit FIFO until the FIFO is full or - * there is no more data to load. - * - * Arguments: info pointer to device extension (instance data) - * Return Value: None - */ -static void usc_load_txfifo( struct mgsl_struct *info ) -{ - int Fifocount; - u8 TwoBytes[2]; - - if ( !info->xmit_cnt && !info->x_char ) - return; - - /* Select transmit FIFO status readback in TICR */ - usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); - - /* load the Transmit FIFO until FIFOs full or all data sent */ - - while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) { - /* there is more space in the transmit FIFO and */ - /* there is more data in transmit buffer */ - - if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) { - /* write a 16-bit word from transmit buffer to 16C32 */ - - TwoBytes[0] = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - TwoBytes[1] = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - - outw( *((u16 *)TwoBytes), info->io_base + DATAREG); - - info->xmit_cnt -= 2; - info->icount.tx += 2; - } else { - /* only 1 byte left to transmit or 1 FIFO slot left */ - - outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY), - info->io_base + CCAR ); - - if (info->x_char) { - /* transmit pending high priority char */ - outw( info->x_char,info->io_base + CCAR ); - info->x_char = 0; - } else { - outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR ); - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } - info->icount.tx++; - } - } - -} /* end of usc_load_txfifo() */ - -/* usc_reset() - * - * Reset the adapter to a known state and prepare it for further use. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_reset( struct mgsl_struct *info ) -{ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { - int i; - u32 readval; - - /* Set BIT30 of Misc Control Register */ - /* (Local Control Register 0x50) to force reset of USC. */ - - volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); - u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28); - - info->misc_ctrl_value |= BIT30; - *MiscCtrl = info->misc_ctrl_value; - - /* - * Force at least 170ns delay before clearing - * reset bit. Each read from LCR takes at least - * 30ns so 10 times for 300ns to be safe. - */ - for(i=0;i<10;i++) - readval = *MiscCtrl; - - info->misc_ctrl_value &= ~BIT30; - *MiscCtrl = info->misc_ctrl_value; - - *LCR0BRDR = BUS_DESCRIPTOR( - 1, // Write Strobe Hold (0-3) - 2, // Write Strobe Delay (0-3) - 2, // Read Strobe Delay (0-3) - 0, // NWDD (Write data-data) (0-3) - 4, // NWAD (Write Addr-data) (0-31) - 0, // NXDA (Read/Write Data-Addr) (0-3) - 0, // NRDD (Read Data-Data) (0-3) - 5 // NRAD (Read Addr-Data) (0-31) - ); - } else { - /* do HW reset */ - outb( 0,info->io_base + 8 ); - } - - info->mbre_bit = 0; - info->loopback_bits = 0; - info->usc_idle_mode = 0; - - /* - * Program the Bus Configuration Register (BCR) - * - * <15> 0 Don't use separate address - * <14..6> 0 reserved - * <5..4> 00 IAckmode = Default, don't care - * <3> 1 Bus Request Totem Pole output - * <2> 1 Use 16 Bit data bus - * <1> 0 IRQ Totem Pole output - * <0> 0 Don't Shift Right Addr - * - * 0000 0000 0000 1100 = 0x000c - * - * By writing to io_base + SDPIN the Wait/Ack pin is - * programmed to work as a Wait pin. - */ - - outw( 0x000c,info->io_base + SDPIN ); - - - outw( 0,info->io_base ); - outw( 0,info->io_base + CCAR ); - - /* select little endian byte ordering */ - usc_RTCmd( info, RTCmd_SelectLittleEndian ); - - - /* Port Control Register (PCR) - * - * <15..14> 11 Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled) - * <13..12> 11 Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled) - * <11..10> 00 Port 5 is Input (No Connect, Don't Care) - * <9..8> 00 Port 4 is Input (No Connect, Don't Care) - * <7..6> 11 Port 3 is Output (~RTS, Bit 6 : 0 = Enabled ) - * <5..4> 11 Port 2 is Output (~DTR, Bit 4 : 0 = Enabled ) - * <3..2> 01 Port 1 is Input (Dedicated RxC) - * <1..0> 01 Port 0 is Input (Dedicated TxC) - * - * 1111 0000 1111 0101 = 0xf0f5 - */ - - usc_OutReg( info, PCR, 0xf0f5 ); - - - /* - * Input/Output Control Register - * - * <15..14> 00 CTS is active low input - * <13..12> 00 DCD is active low input - * <11..10> 00 TxREQ pin is input (DSR) - * <9..8> 00 RxREQ pin is input (RI) - * <7..6> 00 TxD is output (Transmit Data) - * <5..3> 000 TxC Pin in Input (14.7456MHz Clock) - * <2..0> 100 RxC is Output (drive with BRG0) - * - * 0000 0000 0000 0100 = 0x0004 - */ - - usc_OutReg( info, IOCR, 0x0004 ); - -} /* end of usc_reset() */ - -/* usc_set_async_mode() - * - * Program adapter for asynchronous communications. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_set_async_mode( struct mgsl_struct *info ) -{ - u16 RegValue; - - /* disable interrupts while programming USC */ - usc_DisableMasterIrqBit( info ); - - outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ - usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ - - usc_loopback_frame( info ); - - /* Channel mode Register (CMR) - * - * <15..14> 00 Tx Sub modes, 00 = 1 Stop Bit - * <13..12> 00 00 = 16X Clock - * <11..8> 0000 Transmitter mode = Asynchronous - * <7..6> 00 reserved? - * <5..4> 00 Rx Sub modes, 00 = 16X Clock - * <3..0> 0000 Receiver mode = Asynchronous - * - * 0000 0000 0000 0000 = 0x0 - */ - - RegValue = 0; - if ( info->params.stop_bits != 1 ) - RegValue |= BIT14; - usc_OutReg( info, CMR, RegValue ); - - - /* Receiver mode Register (RMR) - * - * <15..13> 000 encoding = None - * <12..08> 00000 reserved (Sync Only) - * <7..6> 00 Even parity - * <5> 0 parity disabled - * <4..2> 000 Receive Char Length = 8 bits - * <1..0> 00 Disable Receiver - * - * 0000 0000 0000 0000 = 0x0 - */ - - RegValue = 0; - - if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; - - if ( info->params.parity != ASYNC_PARITY_NONE ) { - RegValue |= BIT5; - if ( info->params.parity != ASYNC_PARITY_ODD ) - RegValue |= BIT6; - } - - usc_OutReg( info, RMR, RegValue ); - - - /* Set IRQ trigger level */ - - usc_RCmd( info, RCmd_SelectRicrIntLevel ); - - - /* Receive Interrupt Control Register (RICR) - * - * <15..8> ? RxFIFO IRQ Request Level - * - * Note: For async mode the receive FIFO level must be set - * to 0 to avoid the situation where the FIFO contains fewer bytes - * than the trigger level and no more data is expected. - * - * <7> 0 Exited Hunt IA (Interrupt Arm) - * <6> 0 Idle Received IA - * <5> 0 Break/Abort IA - * <4> 0 Rx Bound IA - * <3> 0 Queued status reflects oldest byte in FIFO - * <2> 0 Abort/PE IA - * <1> 0 Rx Overrun IA - * <0> 0 Select TC0 value for readback - * - * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB) - */ - - usc_OutReg( info, RICR, 0x0000 ); - - usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); - - - /* Transmit mode Register (TMR) - * - * <15..13> 000 encoding = None - * <12..08> 00000 reserved (Sync Only) - * <7..6> 00 Transmit parity Even - * <5> 0 Transmit parity Disabled - * <4..2> 000 Tx Char Length = 8 bits - * <1..0> 00 Disable Transmitter - * - * 0000 0000 0000 0000 = 0x0 - */ - - RegValue = 0; - - if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; - - if ( info->params.parity != ASYNC_PARITY_NONE ) { - RegValue |= BIT5; - if ( info->params.parity != ASYNC_PARITY_ODD ) - RegValue |= BIT6; - } - - usc_OutReg( info, TMR, RegValue ); - - usc_set_txidle( info ); - - - /* Set IRQ trigger level */ - - usc_TCmd( info, TCmd_SelectTicrIntLevel ); - - - /* Transmit Interrupt Control Register (TICR) - * - * <15..8> ? Transmit FIFO IRQ Level - * <7> 0 Present IA (Interrupt Arm) - * <6> 1 Idle Sent IA - * <5> 0 Abort Sent IA - * <4> 0 EOF/EOM Sent IA - * <3> 0 CRC Sent IA - * <2> 0 1 = Wait for SW Trigger to Start Frame - * <1> 0 Tx Underrun IA - * <0> 0 TC0 constant on read back - * - * 0000 0000 0100 0000 = 0x0040 - */ - - usc_OutReg( info, TICR, 0x1f40 ); - - usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); - - usc_enable_async_clock( info, info->params.data_rate ); - - - /* Channel Control/status Register (CCSR) - * - * <15> X RCC FIFO Overflow status (RO) - * <14> X RCC FIFO Not Empty status (RO) - * <13> 0 1 = Clear RCC FIFO (WO) - * <12> X DPLL in Sync status (RO) - * <11> X DPLL 2 Missed Clocks status (RO) - * <10> X DPLL 1 Missed Clock status (RO) - * <9..8> 00 DPLL Resync on rising and falling edges (RW) - * <7> X SDLC Loop On status (RO) - * <6> X SDLC Loop Send status (RO) - * <5> 1 Bypass counters for TxClk and RxClk (RW) - * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) - * <1..0> 00 reserved - * - * 0000 0000 0010 0000 = 0x0020 - */ - - usc_OutReg( info, CCSR, 0x0020 ); - - usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA + - RECEIVE_DATA + RECEIVE_STATUS ); - - usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA + - RECEIVE_DATA + RECEIVE_STATUS ); - - usc_EnableMasterIrqBit( info ); - - if (info->bus_type == MGSL_BUS_TYPE_ISA) { - /* Enable INTEN (Port 6, Bit12) */ - /* This connects the IRQ request signal to the ISA bus */ - usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); - } - - if (info->params.loopback) { - info->loopback_bits = 0x300; - outw(0x0300, info->io_base + CCAR); - } - -} /* end of usc_set_async_mode() */ - -/* usc_loopback_frame() - * - * Loop back a small (2 byte) dummy SDLC frame. - * Interrupts and DMA are NOT used. The purpose of this is to - * clear any 'stale' status info left over from running in async mode. - * - * The 16C32 shows the strange behaviour of marking the 1st - * received SDLC frame with a CRC error even when there is no - * CRC error. To get around this a small dummy from of 2 bytes - * is looped back when switching from async to sync mode. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_loopback_frame( struct mgsl_struct *info ) -{ - int i; - unsigned long oldmode = info->params.mode; - - info->params.mode = MGSL_MODE_HDLC; - - usc_DisableMasterIrqBit( info ); - - usc_set_sdlc_mode( info ); - usc_enable_loopback( info, 1 ); - - /* Write 16-bit Time Constant for BRG0 */ - usc_OutReg( info, TC0R, 0 ); - - /* Channel Control Register (CCR) - * - * <15..14> 00 Don't use 32-bit Tx Control Blocks (TCBs) - * <13> 0 Trigger Tx on SW Command Disabled - * <12> 0 Flag Preamble Disabled - * <11..10> 00 Preamble Length = 8-Bits - * <9..8> 01 Preamble Pattern = flags - * <7..6> 10 Don't use 32-bit Rx status Blocks (RSBs) - * <5> 0 Trigger Rx on SW Command Disabled - * <4..0> 0 reserved - * - * 0000 0001 0000 0000 = 0x0100 - */ - - usc_OutReg( info, CCR, 0x0100 ); - - /* SETUP RECEIVER */ - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); - - /* SETUP TRANSMITTER */ - /* Program the Transmit Character Length Register (TCLR) */ - /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ - usc_OutReg( info, TCLR, 2 ); - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - - /* unlatch Tx status bits, and start transmit channel. */ - usc_UnlatchTxstatusBits(info,TXSTATUS_ALL); - outw(0,info->io_base + DATAREG); - - /* ENABLE TRANSMITTER */ - usc_TCmd( info, TCmd_SendFrame ); - usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); - - /* WAIT FOR RECEIVE COMPLETE */ - for (i=0 ; i<1000 ; i++) - if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) - break; - - /* clear Internal Data loopback mode */ - usc_enable_loopback(info, 0); - - usc_EnableMasterIrqBit(info); - - info->params.mode = oldmode; - -} /* end of usc_loopback_frame() */ - -/* usc_set_sync_mode() Programs the USC for SDLC communications. - * - * Arguments: info pointer to adapter info structure - * Return Value: None - */ -static void usc_set_sync_mode( struct mgsl_struct *info ) -{ - usc_loopback_frame( info ); - usc_set_sdlc_mode( info ); - - if (info->bus_type == MGSL_BUS_TYPE_ISA) { - /* Enable INTEN (Port 6, Bit12) */ - /* This connects the IRQ request signal to the ISA bus */ - usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); - } - - usc_enable_aux_clock(info, info->params.clock_speed); - - if (info->params.loopback) - usc_enable_loopback(info,1); - -} /* end of mgsl_set_sync_mode() */ - -/* usc_set_txidle() Set the HDLC idle mode for the transmitter. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_set_txidle( struct mgsl_struct *info ) -{ - u16 usc_idle_mode = IDLEMODE_FLAGS; - - /* Map API idle mode to USC register bits */ - - switch( info->idle_mode ){ - case HDLC_TXIDLE_FLAGS: usc_idle_mode = IDLEMODE_FLAGS; break; - case HDLC_TXIDLE_ALT_ZEROS_ONES: usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break; - case HDLC_TXIDLE_ZEROS: usc_idle_mode = IDLEMODE_ZERO; break; - case HDLC_TXIDLE_ONES: usc_idle_mode = IDLEMODE_ONE; break; - case HDLC_TXIDLE_ALT_MARK_SPACE: usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break; - case HDLC_TXIDLE_SPACE: usc_idle_mode = IDLEMODE_SPACE; break; - case HDLC_TXIDLE_MARK: usc_idle_mode = IDLEMODE_MARK; break; - } - - info->usc_idle_mode = usc_idle_mode; - //usc_OutReg(info, TCSR, usc_idle_mode); - info->tcsr_value &= ~IDLEMODE_MASK; /* clear idle mode bits */ - info->tcsr_value += usc_idle_mode; - usc_OutReg(info, TCSR, info->tcsr_value); - - /* - * if SyncLink WAN adapter is running in external sync mode, the - * transmitter has been set to Monosync in order to try to mimic - * a true raw outbound bit stream. Monosync still sends an open/close - * sync char at the start/end of a frame. Try to match those sync - * patterns to the idle mode set here - */ - if ( info->params.mode == MGSL_MODE_RAW ) { - unsigned char syncpat = 0; - switch( info->idle_mode ) { - case HDLC_TXIDLE_FLAGS: - syncpat = 0x7e; - break; - case HDLC_TXIDLE_ALT_ZEROS_ONES: - syncpat = 0x55; - break; - case HDLC_TXIDLE_ZEROS: - case HDLC_TXIDLE_SPACE: - syncpat = 0x00; - break; - case HDLC_TXIDLE_ONES: - case HDLC_TXIDLE_MARK: - syncpat = 0xff; - break; - case HDLC_TXIDLE_ALT_MARK_SPACE: - syncpat = 0xaa; - break; - } - - usc_SetTransmitSyncChars(info,syncpat,syncpat); - } - -} /* end of usc_set_txidle() */ - -/* usc_get_serial_signals() - * - * Query the adapter for the state of the V24 status (input) signals. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_get_serial_signals( struct mgsl_struct *info ) -{ - u16 status; - - /* clear all serial signals except DTR and RTS */ - info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; - - /* Read the Misc Interrupt status Register (MISR) to get */ - /* the V24 status signals. */ - - status = usc_InReg( info, MISR ); - - /* set serial signal bits to reflect MISR */ - - if ( status & MISCSTATUS_CTS ) - info->serial_signals |= SerialSignal_CTS; - - if ( status & MISCSTATUS_DCD ) - info->serial_signals |= SerialSignal_DCD; - - if ( status & MISCSTATUS_RI ) - info->serial_signals |= SerialSignal_RI; - - if ( status & MISCSTATUS_DSR ) - info->serial_signals |= SerialSignal_DSR; - -} /* end of usc_get_serial_signals() */ - -/* usc_set_serial_signals() - * - * Set the state of DTR and RTS based on contents of - * serial_signals member of device extension. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void usc_set_serial_signals( struct mgsl_struct *info ) -{ - u16 Control; - unsigned char V24Out = info->serial_signals; - - /* get the current value of the Port Control Register (PCR) */ - - Control = usc_InReg( info, PCR ); - - if ( V24Out & SerialSignal_RTS ) - Control &= ~(BIT6); - else - Control |= BIT6; - - if ( V24Out & SerialSignal_DTR ) - Control &= ~(BIT4); - else - Control |= BIT4; - - usc_OutReg( info, PCR, Control ); - -} /* end of usc_set_serial_signals() */ - -/* usc_enable_async_clock() - * - * Enable the async clock at the specified frequency. - * - * Arguments: info pointer to device instance data - * data_rate data rate of clock in bps - * 0 disables the AUX clock. - * Return Value: None - */ -static void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate ) -{ - if ( data_rate ) { - /* - * Clock mode Control Register (CMCR) - * - * <15..14> 00 counter 1 Disabled - * <13..12> 00 counter 0 Disabled - * <11..10> 11 BRG1 Input is TxC Pin - * <9..8> 11 BRG0 Input is TxC Pin - * <7..6> 01 DPLL Input is BRG1 Output - * <5..3> 100 TxCLK comes from BRG0 - * <2..0> 100 RxCLK comes from BRG0 - * - * 0000 1111 0110 0100 = 0x0f64 - */ - - usc_OutReg( info, CMCR, 0x0f64 ); - - - /* - * Write 16-bit Time Constant for BRG0 - * Time Constant = (ClkSpeed / data_rate) - 1 - * ClkSpeed = 921600 (ISA), 691200 (PCI) - */ - - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) ); - else - usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) ); - - - /* - * Hardware Configuration Register (HCR) - * Clear Bit 1, BRG0 mode = Continuous - * Set Bit 0 to enable BRG0. - */ - - usc_OutReg( info, HCR, - (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); - - - /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ - - usc_OutReg( info, IOCR, - (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); - } else { - /* data rate == 0 so turn off BRG0 */ - usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); - } - -} /* end of usc_enable_async_clock() */ - -/* - * Buffer Structures: - * - * Normal memory access uses virtual addresses that can make discontiguous - * physical memory pages appear to be contiguous in the virtual address - * space (the processors memory mapping handles the conversions). - * - * DMA transfers require physically contiguous memory. This is because - * the DMA system controller and DMA bus masters deal with memory using - * only physical addresses. - * - * This causes a problem under Windows NT when large DMA buffers are - * needed. Fragmentation of the nonpaged pool prevents allocations of - * physically contiguous buffers larger than the PAGE_SIZE. - * - * However the 16C32 supports Bus Master Scatter/Gather DMA which - * allows DMA transfers to physically discontiguous buffers. Information - * about each data transfer buffer is contained in a memory structure - * called a 'buffer entry'. A list of buffer entries is maintained - * to track and control the use of the data transfer buffers. - * - * To support this strategy we will allocate sufficient PAGE_SIZE - * contiguous memory buffers to allow for the total required buffer - * space. - * - * The 16C32 accesses the list of buffer entries using Bus Master - * DMA. Control information is read from the buffer entries by the - * 16C32 to control data transfers. status information is written to - * the buffer entries by the 16C32 to indicate the status of completed - * transfers. - * - * The CPU writes control information to the buffer entries to control - * the 16C32 and reads status information from the buffer entries to - * determine information about received and transmitted frames. - * - * Because the CPU and 16C32 (adapter) both need simultaneous access - * to the buffer entries, the buffer entry memory is allocated with - * HalAllocateCommonBuffer(). This restricts the size of the buffer - * entry list to PAGE_SIZE. - * - * The actual data buffers on the other hand will only be accessed - * by the CPU or the adapter but not by both simultaneously. This allows - * Scatter/Gather packet based DMA procedures for using physically - * discontiguous pages. - */ - -/* - * mgsl_reset_tx_dma_buffers() - * - * Set the count for all transmit buffers to 0 to indicate the - * buffer is available for use and set the current buffer to the - * first buffer. This effectively makes all buffers free and - * discards any data in buffers. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info ) -{ - unsigned int i; - - for ( i = 0; i < info->tx_buffer_count; i++ ) { - *((unsigned long *)&(info->tx_buffer_list[i].count)) = 0; - } - - info->current_tx_buffer = 0; - info->start_tx_dma_buffer = 0; - info->tx_dma_buffers_used = 0; - - info->get_tx_holding_index = 0; - info->put_tx_holding_index = 0; - info->tx_holding_count = 0; - -} /* end of mgsl_reset_tx_dma_buffers() */ - -/* - * num_free_tx_dma_buffers() - * - * returns the number of free tx dma buffers available - * - * Arguments: info pointer to device instance data - * Return Value: number of free tx dma buffers - */ -static int num_free_tx_dma_buffers(struct mgsl_struct *info) -{ - return info->tx_buffer_count - info->tx_dma_buffers_used; -} - -/* - * mgsl_reset_rx_dma_buffers() - * - * Set the count for all receive buffers to DMABUFFERSIZE - * and set the current buffer to the first buffer. This effectively - * makes all buffers free and discards any data in buffers. - * - * Arguments: info pointer to device instance data - * Return Value: None - */ -static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ) -{ - unsigned int i; - - for ( i = 0; i < info->rx_buffer_count; i++ ) { - *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE; -// info->rx_buffer_list[i].count = DMABUFFERSIZE; -// info->rx_buffer_list[i].status = 0; - } - - info->current_rx_buffer = 0; - -} /* end of mgsl_reset_rx_dma_buffers() */ - -/* - * mgsl_free_rx_frame_buffers() - * - * Free the receive buffers used by a received SDLC - * frame such that the buffers can be reused. - * - * Arguments: - * - * info pointer to device instance data - * StartIndex index of 1st receive buffer of frame - * EndIndex index of last receive buffer of frame - * - * Return Value: None - */ -static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ) -{ - bool Done = false; - DMABUFFERENTRY *pBufEntry; - unsigned int Index; - - /* Starting with 1st buffer entry of the frame clear the status */ - /* field and set the count field to DMA Buffer Size. */ - - Index = StartIndex; - - while( !Done ) { - pBufEntry = &(info->rx_buffer_list[Index]); - - if ( Index == EndIndex ) { - /* This is the last buffer of the frame! */ - Done = true; - } - - /* reset current buffer for reuse */ -// pBufEntry->status = 0; -// pBufEntry->count = DMABUFFERSIZE; - *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE; - - /* advance to next buffer entry in linked list */ - Index++; - if ( Index == info->rx_buffer_count ) - Index = 0; - } - - /* set current buffer to next buffer after last buffer of frame */ - info->current_rx_buffer = Index; - -} /* end of free_rx_frame_buffers() */ - -/* mgsl_get_rx_frame() - * - * This function attempts to return a received SDLC frame from the - * receive DMA buffers. Only frames received without errors are returned. - * - * Arguments: info pointer to device extension - * Return Value: true if frame returned, otherwise false - */ -static bool mgsl_get_rx_frame(struct mgsl_struct *info) -{ - unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ - unsigned short status; - DMABUFFERENTRY *pBufEntry; - unsigned int framesize = 0; - bool ReturnCode = false; - unsigned long flags; - struct tty_struct *tty = info->port.tty; - bool return_frame = false; - - /* - * current_rx_buffer points to the 1st buffer of the next available - * receive frame. To find the last buffer of the frame look for - * a non-zero status field in the buffer entries. (The status - * field is set by the 16C32 after completing a receive frame. - */ - - StartIndex = EndIndex = info->current_rx_buffer; - - while( !info->rx_buffer_list[EndIndex].status ) { - /* - * If the count field of the buffer entry is non-zero then - * this buffer has not been used. (The 16C32 clears the count - * field when it starts using the buffer.) If an unused buffer - * is encountered then there are no frames available. - */ - - if ( info->rx_buffer_list[EndIndex].count ) - goto Cleanup; - - /* advance to next buffer entry in linked list */ - EndIndex++; - if ( EndIndex == info->rx_buffer_count ) - EndIndex = 0; - - /* if entire list searched then no frame available */ - if ( EndIndex == StartIndex ) { - /* If this occurs then something bad happened, - * all buffers have been 'used' but none mark - * the end of a frame. Reset buffers and receiver. - */ - - if ( info->rx_enabled ){ - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_start_receiver(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - goto Cleanup; - } - } - - - /* check status of receive frame */ - - status = info->rx_buffer_list[EndIndex].status; - - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { - if ( status & RXSTATUS_SHORT_FRAME ) - info->icount.rxshort++; - else if ( status & RXSTATUS_ABORT ) - info->icount.rxabort++; - else if ( status & RXSTATUS_OVERRUN ) - info->icount.rxover++; - else { - info->icount.rxcrc++; - if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) - return_frame = true; - } - framesize = 0; -#if SYNCLINK_GENERIC_HDLC - { - info->netdev->stats.rx_errors++; - info->netdev->stats.rx_frame_errors++; - } -#endif - } else - return_frame = true; - - if ( return_frame ) { - /* receive frame has no errors, get frame size. - * The frame size is the starting value of the RCC (which was - * set to 0xffff) minus the ending value of the RCC (decremented - * once for each receive character) minus 2 for the 16-bit CRC. - */ - - framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc; - - /* adjust frame size for CRC if any */ - if ( info->params.crc_type == HDLC_CRC_16_CCITT ) - framesize -= 2; - else if ( info->params.crc_type == HDLC_CRC_32_CCITT ) - framesize -= 4; - } - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n", - __FILE__,__LINE__,info->device_name,status,framesize); - - if ( debug_level >= DEBUG_LEVEL_DATA ) - mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr, - min_t(int, framesize, DMABUFFERSIZE),0); - - if (framesize) { - if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) && - ((framesize+1) > info->max_frame_size) ) || - (framesize > info->max_frame_size) ) - info->icount.rxlong++; - else { - /* copy dma buffer(s) to contiguous intermediate buffer */ - int copy_count = framesize; - int index = StartIndex; - unsigned char *ptmp = info->intermediate_rxbuffer; - - if ( !(status & RXSTATUS_CRC_ERROR)) - info->icount.rxok++; - - while(copy_count) { - int partial_count; - if ( copy_count > DMABUFFERSIZE ) - partial_count = DMABUFFERSIZE; - else - partial_count = copy_count; - - pBufEntry = &(info->rx_buffer_list[index]); - memcpy( ptmp, pBufEntry->virt_addr, partial_count ); - ptmp += partial_count; - copy_count -= partial_count; - - if ( ++index == info->rx_buffer_count ) - index = 0; - } - - if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) { - ++framesize; - *ptmp = (status & RXSTATUS_CRC_ERROR ? - RX_CRC_ERROR : - RX_OK); - - if ( debug_level >= DEBUG_LEVEL_DATA ) - printk("%s(%d):mgsl_get_rx_frame(%s) rx frame status=%d\n", - __FILE__,__LINE__,info->device_name, - *ptmp); - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_rx(info,info->intermediate_rxbuffer,framesize); - else -#endif - ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); - } - } - /* Free the buffers used by this frame. */ - mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex ); - - ReturnCode = true; - -Cleanup: - - if ( info->rx_enabled && info->rx_overflow ) { - /* The receiver needs to restarted because of - * a receive overflow (buffer or FIFO). If the - * receive buffers are now empty, then restart receiver. - */ - - if ( !info->rx_buffer_list[EndIndex].status && - info->rx_buffer_list[EndIndex].count ) { - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_start_receiver(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - } - - return ReturnCode; - -} /* end of mgsl_get_rx_frame() */ - -/* mgsl_get_raw_rx_frame() - * - * This function attempts to return a received frame from the - * receive DMA buffers when running in external loop mode. In this mode, - * we will return at most one DMABUFFERSIZE frame to the application. - * The USC receiver is triggering off of DCD going active to start a new - * frame, and DCD going inactive to terminate the frame (similar to - * processing a closing flag character). - * - * In this routine, we will return DMABUFFERSIZE "chunks" at a time. - * If DCD goes inactive, the last Rx DMA Buffer will have a non-zero - * status field and the RCC field will indicate the length of the - * entire received frame. We take this RCC field and get the modulus - * of RCC and DMABUFFERSIZE to determine if number of bytes in the - * last Rx DMA buffer and return that last portion of the frame. - * - * Arguments: info pointer to device extension - * Return Value: true if frame returned, otherwise false - */ -static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info) -{ - unsigned int CurrentIndex, NextIndex; - unsigned short status; - DMABUFFERENTRY *pBufEntry; - unsigned int framesize = 0; - bool ReturnCode = false; - unsigned long flags; - struct tty_struct *tty = info->port.tty; - - /* - * current_rx_buffer points to the 1st buffer of the next available - * receive frame. The status field is set by the 16C32 after - * completing a receive frame. If the status field of this buffer - * is zero, either the USC is still filling this buffer or this - * is one of a series of buffers making up a received frame. - * - * If the count field of this buffer is zero, the USC is either - * using this buffer or has used this buffer. Look at the count - * field of the next buffer. If that next buffer's count is - * non-zero, the USC is still actively using the current buffer. - * Otherwise, if the next buffer's count field is zero, the - * current buffer is complete and the USC is using the next - * buffer. - */ - CurrentIndex = NextIndex = info->current_rx_buffer; - ++NextIndex; - if ( NextIndex == info->rx_buffer_count ) - NextIndex = 0; - - if ( info->rx_buffer_list[CurrentIndex].status != 0 || - (info->rx_buffer_list[CurrentIndex].count == 0 && - info->rx_buffer_list[NextIndex].count == 0)) { - /* - * Either the status field of this dma buffer is non-zero - * (indicating the last buffer of a receive frame) or the next - * buffer is marked as in use -- implying this buffer is complete - * and an intermediate buffer for this received frame. - */ - - status = info->rx_buffer_list[CurrentIndex].status; - - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { - if ( status & RXSTATUS_SHORT_FRAME ) - info->icount.rxshort++; - else if ( status & RXSTATUS_ABORT ) - info->icount.rxabort++; - else if ( status & RXSTATUS_OVERRUN ) - info->icount.rxover++; - else - info->icount.rxcrc++; - framesize = 0; - } else { - /* - * A receive frame is available, get frame size and status. - * - * The frame size is the starting value of the RCC (which was - * set to 0xffff) minus the ending value of the RCC (decremented - * once for each receive character) minus 2 or 4 for the 16-bit - * or 32-bit CRC. - * - * If the status field is zero, this is an intermediate buffer. - * It's size is 4K. - * - * If the DMA Buffer Entry's Status field is non-zero, the - * receive operation completed normally (ie: DCD dropped). The - * RCC field is valid and holds the received frame size. - * It is possible that the RCC field will be zero on a DMA buffer - * entry with a non-zero status. This can occur if the total - * frame size (number of bytes between the time DCD goes active - * to the time DCD goes inactive) exceeds 65535 bytes. In this - * case the 16C32 has underrun on the RCC count and appears to - * stop updating this counter to let us know the actual received - * frame size. If this happens (non-zero status and zero RCC), - * simply return the entire RxDMA Buffer - */ - if ( status ) { - /* - * In the event that the final RxDMA Buffer is - * terminated with a non-zero status and the RCC - * field is zero, we interpret this as the RCC - * having underflowed (received frame > 65535 bytes). - * - * Signal the event to the user by passing back - * a status of RxStatus_CrcError returning the full - * buffer and let the app figure out what data is - * actually valid - */ - if ( info->rx_buffer_list[CurrentIndex].rcc ) - framesize = RCLRVALUE - info->rx_buffer_list[CurrentIndex].rcc; - else - framesize = DMABUFFERSIZE; - } - else - framesize = DMABUFFERSIZE; - } - - if ( framesize > DMABUFFERSIZE ) { - /* - * if running in raw sync mode, ISR handler for - * End Of Buffer events terminates all buffers at 4K. - * If this frame size is said to be >4K, get the - * actual number of bytes of the frame in this buffer. - */ - framesize = framesize % DMABUFFERSIZE; - } - - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk("%s(%d):mgsl_get_raw_rx_frame(%s) status=%04X size=%d\n", - __FILE__,__LINE__,info->device_name,status,framesize); - - if ( debug_level >= DEBUG_LEVEL_DATA ) - mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr, - min_t(int, framesize, DMABUFFERSIZE),0); - - if (framesize) { - /* copy dma buffer(s) to contiguous intermediate buffer */ - /* NOTE: we never copy more than DMABUFFERSIZE bytes */ - - pBufEntry = &(info->rx_buffer_list[CurrentIndex]); - memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize); - info->icount.rxok++; - - ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); - } - - /* Free the buffers used by this frame. */ - mgsl_free_rx_frame_buffers( info, CurrentIndex, CurrentIndex ); - - ReturnCode = true; - } - - - if ( info->rx_enabled && info->rx_overflow ) { - /* The receiver needs to restarted because of - * a receive overflow (buffer or FIFO). If the - * receive buffers are now empty, then restart receiver. - */ - - if ( !info->rx_buffer_list[CurrentIndex].status && - info->rx_buffer_list[CurrentIndex].count ) { - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_start_receiver(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - } - - return ReturnCode; - -} /* end of mgsl_get_raw_rx_frame() */ - -/* mgsl_load_tx_dma_buffer() - * - * Load the transmit DMA buffer with the specified data. - * - * Arguments: - * - * info pointer to device extension - * Buffer pointer to buffer containing frame to load - * BufferSize size in bytes of frame in Buffer - * - * Return Value: None - */ -static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, - const char *Buffer, unsigned int BufferSize) -{ - unsigned short Copycount; - unsigned int i = 0; - DMABUFFERENTRY *pBufEntry; - - if ( debug_level >= DEBUG_LEVEL_DATA ) - mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1); - - if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { - /* set CMR:13 to start transmit when - * next GoAhead (abort) is received - */ - info->cmr_value |= BIT13; - } - - /* begin loading the frame in the next available tx dma - * buffer, remember it's starting location for setting - * up tx dma operation - */ - i = info->current_tx_buffer; - info->start_tx_dma_buffer = i; - - /* Setup the status and RCC (Frame Size) fields of the 1st */ - /* buffer entry in the transmit DMA buffer list. */ - - info->tx_buffer_list[i].status = info->cmr_value & 0xf000; - info->tx_buffer_list[i].rcc = BufferSize; - info->tx_buffer_list[i].count = BufferSize; - - /* Copy frame data from 1st source buffer to the DMA buffers. */ - /* The frame data may span multiple DMA buffers. */ - - while( BufferSize ){ - /* Get a pointer to next DMA buffer entry. */ - pBufEntry = &info->tx_buffer_list[i++]; - - if ( i == info->tx_buffer_count ) - i=0; - - /* Calculate the number of bytes that can be copied from */ - /* the source buffer to this DMA buffer. */ - if ( BufferSize > DMABUFFERSIZE ) - Copycount = DMABUFFERSIZE; - else - Copycount = BufferSize; - - /* Actually copy data from source buffer to DMA buffer. */ - /* Also set the data count for this individual DMA buffer. */ - if ( info->bus_type == MGSL_BUS_TYPE_PCI ) - mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount); - else - memcpy(pBufEntry->virt_addr, Buffer, Copycount); - - pBufEntry->count = Copycount; - - /* Advance source pointer and reduce remaining data count. */ - Buffer += Copycount; - BufferSize -= Copycount; - - ++info->tx_dma_buffers_used; - } - - /* remember next available tx dma buffer */ - info->current_tx_buffer = i; - -} /* end of mgsl_load_tx_dma_buffer() */ - -/* - * mgsl_register_test() - * - * Performs a register test of the 16C32. - * - * Arguments: info pointer to device instance data - * Return Value: true if test passed, otherwise false - */ -static bool mgsl_register_test( struct mgsl_struct *info ) -{ - static unsigned short BitPatterns[] = - { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f }; - static unsigned int Patterncount = ARRAY_SIZE(BitPatterns); - unsigned int i; - bool rc = true; - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_reset(info); - - /* Verify the reset state of some registers. */ - - if ( (usc_InReg( info, SICR ) != 0) || - (usc_InReg( info, IVR ) != 0) || - (usc_InDmaReg( info, DIVR ) != 0) ){ - rc = false; - } - - if ( rc ){ - /* Write bit patterns to various registers but do it out of */ - /* sync, then read back and verify values. */ - - for ( i = 0 ; i < Patterncount ; i++ ) { - usc_OutReg( info, TC0R, BitPatterns[i] ); - usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] ); - usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] ); - usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] ); - usc_OutReg( info, RSR, BitPatterns[(i+4)%Patterncount] ); - usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] ); - - if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) || - (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) || - (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) || - (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) || - (usc_InReg( info, RSR ) != BitPatterns[(i+4)%Patterncount]) || - (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){ - rc = false; - break; - } - } - } - - usc_reset(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return rc; - -} /* end of mgsl_register_test() */ - -/* mgsl_irq_test() Perform interrupt test of the 16C32. - * - * Arguments: info pointer to device instance data - * Return Value: true if test passed, otherwise false - */ -static bool mgsl_irq_test( struct mgsl_struct *info ) -{ - unsigned long EndTime; - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_reset(info); - - /* - * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. - * The ISR sets irq_occurred to true. - */ - - info->irq_occurred = false; - - /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */ - /* Enable INTEN (Port 6, Bit12) */ - /* This connects the IRQ request signal to the ISA bus */ - /* on the ISA adapter. This has no effect for the PCI adapter */ - usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); - - usc_EnableMasterIrqBit(info); - usc_EnableInterrupts(info, IO_PIN); - usc_ClearIrqPendingBits(info, IO_PIN); - - usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED); - usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - EndTime=100; - while( EndTime-- && !info->irq_occurred ) { - msleep_interruptible(10); - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_reset(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return info->irq_occurred; - -} /* end of mgsl_irq_test() */ - -/* mgsl_dma_test() - * - * Perform a DMA test of the 16C32. A small frame is - * transmitted via DMA from a transmit buffer to a receive buffer - * using single buffer DMA mode. - * - * Arguments: info pointer to device instance data - * Return Value: true if test passed, otherwise false - */ -static bool mgsl_dma_test( struct mgsl_struct *info ) -{ - unsigned short FifoLevel; - unsigned long phys_addr; - unsigned int FrameSize; - unsigned int i; - char *TmpPtr; - bool rc = true; - unsigned short status=0; - unsigned long EndTime; - unsigned long flags; - MGSL_PARAMS tmp_params; - - /* save current port options */ - memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); - /* load default port options */ - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - -#define TESTFRAMESIZE 40 - - spin_lock_irqsave(&info->irq_spinlock,flags); - - /* setup 16C32 for SDLC DMA transfer mode */ - - usc_reset(info); - usc_set_sdlc_mode(info); - usc_enable_loopback(info,1); - - /* Reprogram the RDMR so that the 16C32 does NOT clear the count - * field of the buffer entry after fetching buffer address. This - * way we can detect a DMA failure for a DMA read (which should be - * non-destructive to system memory) before we try and write to - * memory (where a failure could corrupt system memory). - */ - - /* Receive DMA mode Register (RDMR) - * - * <15..14> 11 DMA mode = Linked List Buffer mode - * <13> 1 RSBinA/L = store Rx status Block in List entry - * <12> 0 1 = Clear count of List Entry after fetching - * <11..10> 00 Address mode = Increment - * <9> 1 Terminate Buffer on RxBound - * <8> 0 Bus Width = 16bits - * <7..0> ? status Bits (write as 0s) - * - * 1110 0010 0000 0000 = 0xe200 - */ - - usc_OutDmaReg( info, RDMR, 0xe200 ); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - - /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */ - - FrameSize = TESTFRAMESIZE; - - /* setup 1st transmit buffer entry: */ - /* with frame size and transmit control word */ - - info->tx_buffer_list[0].count = FrameSize; - info->tx_buffer_list[0].rcc = FrameSize; - info->tx_buffer_list[0].status = 0x4000; - - /* build a transmit frame in 1st transmit DMA buffer */ - - TmpPtr = info->tx_buffer_list[0].virt_addr; - for (i = 0; i < FrameSize; i++ ) - *TmpPtr++ = i; - - /* setup 1st receive buffer entry: */ - /* clear status, set max receive buffer size */ - - info->rx_buffer_list[0].status = 0; - info->rx_buffer_list[0].count = FrameSize + 4; - - /* zero out the 1st receive buffer */ - - memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 ); - - /* Set count field of next buffer entries to prevent */ - /* 16C32 from using buffers after the 1st one. */ - - info->tx_buffer_list[1].count = 0; - info->rx_buffer_list[1].count = 0; - - - /***************************/ - /* Program 16C32 receiver. */ - /***************************/ - - spin_lock_irqsave(&info->irq_spinlock,flags); - - /* setup DMA transfers */ - usc_RTCmd( info, RTCmd_PurgeRxFifo ); - - /* program 16C32 receiver with physical address of 1st DMA buffer entry */ - phys_addr = info->rx_buffer_list[0].phys_entry; - usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr ); - usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) ); - - /* Clear the Rx DMA status bits (read RDMR) and start channel */ - usc_InDmaReg( info, RDMR ); - usc_DmaCmd( info, DmaCmd_InitRxChannel ); - - /* Enable Receiver (RMR <1..0> = 10) */ - usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) ); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - - /*************************************************************/ - /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */ - /*************************************************************/ - - /* Wait 100ms for interrupt. */ - EndTime = jiffies + msecs_to_jiffies(100); - - for(;;) { - if (time_after(jiffies, EndTime)) { - rc = false; - break; - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - status = usc_InDmaReg( info, RDMR ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - if ( !(status & BIT4) && (status & BIT5) ) { - /* INITG (BIT 4) is inactive (no entry read in progress) AND */ - /* BUSY (BIT 5) is active (channel still active). */ - /* This means the buffer entry read has completed. */ - break; - } - } - - - /******************************/ - /* Program 16C32 transmitter. */ - /******************************/ - - spin_lock_irqsave(&info->irq_spinlock,flags); - - /* Program the Transmit Character Length Register (TCLR) */ - /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ - - usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count ); - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - - /* Program the address of the 1st DMA Buffer Entry in linked list */ - - phys_addr = info->tx_buffer_list[0].phys_entry; - usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr ); - usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) ); - - /* unlatch Tx status bits, and start transmit channel. */ - - usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0f00) | 0xfa) ); - usc_DmaCmd( info, DmaCmd_InitTxChannel ); - - /* wait for DMA controller to fill transmit FIFO */ - - usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - - /**********************************/ - /* WAIT FOR TRANSMIT FIFO TO FILL */ - /**********************************/ - - /* Wait 100ms */ - EndTime = jiffies + msecs_to_jiffies(100); - - for(;;) { - if (time_after(jiffies, EndTime)) { - rc = false; - break; - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - FifoLevel = usc_InReg(info, TICR) >> 8; - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - if ( FifoLevel < 16 ) - break; - else - if ( FrameSize < 32 ) { - /* This frame is smaller than the entire transmit FIFO */ - /* so wait for the entire frame to be loaded. */ - if ( FifoLevel <= (32 - FrameSize) ) - break; - } - } - - - if ( rc ) - { - /* Enable 16C32 transmitter. */ - - spin_lock_irqsave(&info->irq_spinlock,flags); - - /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */ - usc_TCmd( info, TCmd_SendFrame ); - usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) ); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - - /******************************/ - /* WAIT FOR TRANSMIT COMPLETE */ - /******************************/ - - /* Wait 100ms */ - EndTime = jiffies + msecs_to_jiffies(100); - - /* While timer not expired wait for transmit complete */ - - spin_lock_irqsave(&info->irq_spinlock,flags); - status = usc_InReg( info, TCSR ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { - if (time_after(jiffies, EndTime)) { - rc = false; - break; - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - status = usc_InReg( info, TCSR ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - } - } - - - if ( rc ){ - /* CHECK FOR TRANSMIT ERRORS */ - if ( status & (BIT5 + BIT1) ) - rc = false; - } - - if ( rc ) { - /* WAIT FOR RECEIVE COMPLETE */ - - /* Wait 100ms */ - EndTime = jiffies + msecs_to_jiffies(100); - - /* Wait for 16C32 to write receive status to buffer entry. */ - status=info->rx_buffer_list[0].status; - while ( status == 0 ) { - if (time_after(jiffies, EndTime)) { - rc = false; - break; - } - status=info->rx_buffer_list[0].status; - } - } - - - if ( rc ) { - /* CHECK FOR RECEIVE ERRORS */ - status = info->rx_buffer_list[0].status; - - if ( status & (BIT8 + BIT3 + BIT1) ) { - /* receive error has occurred */ - rc = false; - } else { - if ( memcmp( info->tx_buffer_list[0].virt_addr , - info->rx_buffer_list[0].virt_addr, FrameSize ) ){ - rc = false; - } - } - } - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_reset( info ); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - /* restore current port options */ - memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); - - return rc; - -} /* end of mgsl_dma_test() */ - -/* mgsl_adapter_test() - * - * Perform the register, IRQ, and DMA tests for the 16C32. - * - * Arguments: info pointer to device instance data - * Return Value: 0 if success, otherwise -ENODEV - */ -static int mgsl_adapter_test( struct mgsl_struct *info ) -{ - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):Testing device %s\n", - __FILE__,__LINE__,info->device_name ); - - if ( !mgsl_register_test( info ) ) { - info->init_error = DiagStatus_AddressFailure; - printk( "%s(%d):Register test failure for device %s Addr=%04X\n", - __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) ); - return -ENODEV; - } - - if ( !mgsl_irq_test( info ) ) { - info->init_error = DiagStatus_IrqFailure; - printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", - __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); - return -ENODEV; - } - - if ( !mgsl_dma_test( info ) ) { - info->init_error = DiagStatus_DmaFailure; - printk( "%s(%d):DMA test failure for device %s DMA=%d\n", - __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) ); - return -ENODEV; - } - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):device %s passed diagnostics\n", - __FILE__,__LINE__,info->device_name ); - - return 0; - -} /* end of mgsl_adapter_test() */ - -/* mgsl_memory_test() - * - * Test the shared memory on a PCI adapter. - * - * Arguments: info pointer to device instance data - * Return Value: true if test passed, otherwise false - */ -static bool mgsl_memory_test( struct mgsl_struct *info ) -{ - static unsigned long BitPatterns[] = - { 0x0, 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; - unsigned long Patterncount = ARRAY_SIZE(BitPatterns); - unsigned long i; - unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long); - unsigned long * TestAddr; - - if ( info->bus_type != MGSL_BUS_TYPE_PCI ) - return true; - - TestAddr = (unsigned long *)info->memory_base; - - /* Test data lines with test pattern at one location. */ - - for ( i = 0 ; i < Patterncount ; i++ ) { - *TestAddr = BitPatterns[i]; - if ( *TestAddr != BitPatterns[i] ) - return false; - } - - /* Test address lines with incrementing pattern over */ - /* entire address range. */ - - for ( i = 0 ; i < TestLimit ; i++ ) { - *TestAddr = i * 4; - TestAddr++; - } - - TestAddr = (unsigned long *)info->memory_base; - - for ( i = 0 ; i < TestLimit ; i++ ) { - if ( *TestAddr != i * 4 ) - return false; - TestAddr++; - } - - memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE ); - - return true; - -} /* End Of mgsl_memory_test() */ - - -/* mgsl_load_pci_memory() - * - * Load a large block of data into the PCI shared memory. - * Use this instead of memcpy() or memmove() to move data - * into the PCI shared memory. - * - * Notes: - * - * This function prevents the PCI9050 interface chip from hogging - * the adapter local bus, which can starve the 16C32 by preventing - * 16C32 bus master cycles. - * - * The PCI9050 documentation says that the 9050 will always release - * control of the local bus after completing the current read - * or write operation. - * - * It appears that as long as the PCI9050 write FIFO is full, the - * PCI9050 treats all of the writes as a single burst transaction - * and will not release the bus. This causes DMA latency problems - * at high speeds when copying large data blocks to the shared - * memory. - * - * This function in effect, breaks the a large shared memory write - * into multiple transations by interleaving a shared memory read - * which will flush the write FIFO and 'complete' the write - * transation. This allows any pending DMA request to gain control - * of the local bus in a timely fasion. - * - * Arguments: - * - * TargetPtr pointer to target address in PCI shared memory - * SourcePtr pointer to source buffer for data - * count count in bytes of data to copy - * - * Return Value: None - */ -static void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, - unsigned short count ) -{ - /* 16 32-bit writes @ 60ns each = 960ns max latency on local bus */ -#define PCI_LOAD_INTERVAL 64 - - unsigned short Intervalcount = count / PCI_LOAD_INTERVAL; - unsigned short Index; - unsigned long Dummy; - - for ( Index = 0 ; Index < Intervalcount ; Index++ ) - { - memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL); - Dummy = *((volatile unsigned long *)TargetPtr); - TargetPtr += PCI_LOAD_INTERVAL; - SourcePtr += PCI_LOAD_INTERVAL; - } - - memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL ); - -} /* End Of mgsl_load_pci_memory() */ - -static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit) -{ - int i; - int linecount; - if (xmit) - printk("%s tx data:\n",info->device_name); - else - printk("%s rx data:\n",info->device_name); - - while(count) { - if (count > 16) - linecount = 16; - else - linecount = count; - - for(i=0;i=040 && data[i]<=0176) - printk("%c",data[i]); - else - printk("."); - } - printk("\n"); - - data += linecount; - count -= linecount; - } -} /* end of mgsl_trace_block() */ - -/* mgsl_tx_timeout() - * - * called when HDLC frame times out - * update stats and do tx completion processing - * - * Arguments: context pointer to device instance data - * Return Value: None - */ -static void mgsl_tx_timeout(unsigned long context) -{ - struct mgsl_struct *info = (struct mgsl_struct*)context; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):mgsl_tx_timeout(%s)\n", - __FILE__,__LINE__,info->device_name); - if(info->tx_active && - (info->params.mode == MGSL_MODE_HDLC || - info->params.mode == MGSL_MODE_RAW) ) { - info->icount.txtimeout++; - } - spin_lock_irqsave(&info->irq_spinlock,flags); - info->tx_active = false; - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) - usc_loopmode_cancel_transmit( info ); - - spin_unlock_irqrestore(&info->irq_spinlock,flags); - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - mgsl_bh_transmit(info); - -} /* end of mgsl_tx_timeout() */ - -/* signal that there are no more frames to send, so that - * line is 'released' by echoing RxD to TxD when current - * transmission is complete (or immediately if no tx in progress). - */ -static int mgsl_loopmode_send_done( struct mgsl_struct * info ) -{ - unsigned long flags; - - spin_lock_irqsave(&info->irq_spinlock,flags); - if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { - if (info->tx_active) - info->loopmode_send_done_requested = true; - else - usc_loopmode_send_done(info); - } - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return 0; -} - -/* release the line by echoing RxD to TxD - * upon completion of a transmit frame - */ -static void usc_loopmode_send_done( struct mgsl_struct * info ) -{ - info->loopmode_send_done_requested = false; - /* clear CMR:13 to 0 to start echoing RxData to TxData */ - info->cmr_value &= ~BIT13; - usc_OutReg(info, CMR, info->cmr_value); -} - -/* abort a transmit in progress while in HDLC LoopMode - */ -static void usc_loopmode_cancel_transmit( struct mgsl_struct * info ) -{ - /* reset tx dma channel and purge TxFifo */ - usc_RTCmd( info, RTCmd_PurgeTxFifo ); - usc_DmaCmd( info, DmaCmd_ResetTxChannel ); - usc_loopmode_send_done( info ); -} - -/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled - * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort) - * we must clear CMR:13 to begin repeating TxData to RxData - */ -static void usc_loopmode_insert_request( struct mgsl_struct * info ) -{ - info->loopmode_insert_requested = true; - - /* enable RxAbort irq. On next RxAbort, clear CMR:13 to - * begin repeating TxData on RxData (complete insertion) - */ - usc_OutReg( info, RICR, - (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) ); - - /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */ - info->cmr_value |= BIT13; - usc_OutReg(info, CMR, info->cmr_value); -} - -/* return 1 if station is inserted into the loop, otherwise 0 - */ -static int usc_loopmode_active( struct mgsl_struct * info) -{ - return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ; -} - -#if SYNCLINK_GENERIC_HDLC - -/** - * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) - * set encoding and frame check sequence (FCS) options - * - * dev pointer to network device structure - * encoding serial encoding setting - * parity FCS setting - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, - unsigned short parity) -{ - struct mgsl_struct *info = dev_to_port(dev); - unsigned char new_encoding; - unsigned short new_crctype; - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - switch (encoding) - { - case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; - case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; - case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; - case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; - case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; - default: return -EINVAL; - } - - switch (parity) - { - case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; - case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; - case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; - default: return -EINVAL; - } - - info->params.encoding = new_encoding; - info->params.crc_type = new_crctype; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - mgsl_program_hw(info); - - return 0; -} - -/** - * called by generic HDLC layer to send frame - * - * skb socket buffer containing HDLC frame - * dev pointer to network device structure - */ -static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct mgsl_struct *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name); - - /* stop sending until this frame completes */ - netif_stop_queue(dev); - - /* copy data to device buffers */ - info->xmit_cnt = skb->len; - mgsl_load_tx_dma_buffer(info, skb->data, skb->len); - - /* update network statistics */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - - /* save start time for transmit timeout detection */ - dev->trans_start = jiffies; - - /* start hardware transmitter if necessary */ - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!info->tx_active) - usc_start_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - return NETDEV_TX_OK; -} - -/** - * called by network layer when interface enabled - * claim resources and initialize hardware - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_open(struct net_device *dev) -{ - struct mgsl_struct *info = dev_to_port(dev); - int rc; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); - - /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) - return rc; - - /* arbitrate between network and tty opens */ - spin_lock_irqsave(&info->netlock, flags); - if (info->port.count != 0 || info->netcount != 0) { - printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); - spin_unlock_irqrestore(&info->netlock, flags); - return -EBUSY; - } - info->netcount=1; - spin_unlock_irqrestore(&info->netlock, flags); - - /* claim resources and init adapter */ - if ((rc = startup(info)) != 0) { - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - - /* assert DTR and RTS, apply hardware settings */ - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - mgsl_program_hw(info); - - /* enable network layer transmit */ - dev->trans_start = jiffies; - netif_start_queue(dev); - - /* inform generic HDLC layer of current DCD status */ - spin_lock_irqsave(&info->irq_spinlock, flags); - usc_get_serial_signals(info); - spin_unlock_irqrestore(&info->irq_spinlock, flags); - if (info->serial_signals & SerialSignal_DCD) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - return 0; -} - -/** - * called by network layer when interface is disabled - * shutdown hardware and release resources - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_close(struct net_device *dev) -{ - struct mgsl_struct *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name); - - netif_stop_queue(dev); - - /* shutdown adapter and release resources */ - shutdown(info); - - hdlc_close(dev); - - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - - return 0; -} - -/** - * called by network layer to process IOCTL call to network device - * - * dev pointer to network device structure - * ifr pointer to network interface request structure - * cmd IOCTL command code - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - const size_t size = sizeof(sync_serial_settings); - sync_serial_settings new_line; - sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; - struct mgsl_struct *info = dev_to_port(dev); - unsigned int flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name); - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - if (cmd != SIOCWANDEV) - return hdlc_ioctl(dev, ifr, cmd); - - switch(ifr->ifr_settings.type) { - case IF_GET_IFACE: /* return current sync_serial_settings */ - - ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; - if (ifr->ifr_settings.size < size) { - ifr->ifr_settings.size = size; /* data size wanted */ - return -ENOBUFS; - } - - flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - - switch (flags){ - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; - case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; - default: new_line.clock_type = CLOCK_DEFAULT; - } - - new_line.clock_rate = info->params.clock_speed; - new_line.loopback = info->params.loopback ? 1:0; - - if (copy_to_user(line, &new_line, size)) - return -EFAULT; - return 0; - - case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&new_line, line, size)) - return -EFAULT; - - switch (new_line.clock_type) - { - case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; - case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; - case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; - case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; - case CLOCK_DEFAULT: flags = info->params.flags & - (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; - default: return -EINVAL; - } - - if (new_line.loopback != 0 && new_line.loopback != 1) - return -EINVAL; - - info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - info->params.flags |= flags; - - info->params.loopback = new_line.loopback; - - if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) - info->params.clock_speed = new_line.clock_rate; - else - info->params.clock_speed = 0; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - mgsl_program_hw(info); - return 0; - - default: - return hdlc_ioctl(dev, ifr, cmd); - } -} - -/** - * called by network layer when transmit timeout is detected - * - * dev pointer to network device structure - */ -static void hdlcdev_tx_timeout(struct net_device *dev) -{ - struct mgsl_struct *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_tx_timeout(%s)\n",dev->name); - - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_stop_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); - - netif_wake_queue(dev); -} - -/** - * called by device driver when transmit completes - * reenable network layer transmit if stopped - * - * info pointer to device instance information - */ -static void hdlcdev_tx_done(struct mgsl_struct *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); -} - -/** - * called by device driver when frame received - * pass frame to network layer - * - * info pointer to device instance information - * buf pointer to buffer contianing frame data - * size count of data bytes in buf - */ -static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - struct net_device *dev = info->netdev; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_rx(%s)\n", dev->name); - - if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", - dev->name); - dev->stats.rx_dropped++; - return; - } - - memcpy(skb_put(skb, size), buf, size); - - skb->protocol = hdlc_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - netif_rx(skb); -} - -static const struct net_device_ops hdlcdev_ops = { - .ndo_open = hdlcdev_open, - .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_do_ioctl = hdlcdev_ioctl, - .ndo_tx_timeout = hdlcdev_tx_timeout, -}; - -/** - * called by device driver when adding device instance - * do generic HDLC initialization - * - * info pointer to device instance information - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_init(struct mgsl_struct *info) -{ - int rc; - struct net_device *dev; - hdlc_device *hdlc; - - /* allocate and initialize network and HDLC layer objects */ - - if (!(dev = alloc_hdlcdev(info))) { - printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); - return -ENOMEM; - } - - /* for network layer reporting purposes only */ - dev->base_addr = info->io_base; - dev->irq = info->irq_level; - dev->dma = info->dma_level; - - /* network layer callbacks and settings */ - dev->netdev_ops = &hdlcdev_ops; - dev->watchdog_timeo = 10 * HZ; - dev->tx_queue_len = 50; - - /* generic HDLC layer callbacks and settings */ - hdlc = dev_to_hdlc(dev); - hdlc->attach = hdlcdev_attach; - hdlc->xmit = hdlcdev_xmit; - - /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { - printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); - free_netdev(dev); - return rc; - } - - info->netdev = dev; - return 0; -} - -/** - * called by device driver when removing device instance - * do generic HDLC cleanup - * - * info pointer to device instance information - */ -static void hdlcdev_exit(struct mgsl_struct *info) -{ - unregister_hdlc_device(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; -} - -#endif /* CONFIG_HDLC */ - - -static int __devinit synclink_init_one (struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct mgsl_struct *info; - - if (pci_enable_device(dev)) { - printk("error enabling pci device %p\n", dev); - return -EIO; - } - - if (!(info = mgsl_allocate_device())) { - printk("can't allocate device instance data.\n"); - return -EIO; - } - - /* Copy user configuration info to device instance data */ - - info->io_base = pci_resource_start(dev, 2); - info->irq_level = dev->irq; - info->phys_memory_base = pci_resource_start(dev, 3); - - /* Because veremap only works on page boundaries we must map - * a larger area than is actually implemented for the LCR - * memory range. We map a full page starting at the page boundary. - */ - info->phys_lcr_base = pci_resource_start(dev, 0); - info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); - info->phys_lcr_base &= ~(PAGE_SIZE-1); - - info->bus_type = MGSL_BUS_TYPE_PCI; - info->io_addr_size = 8; - info->irq_flags = IRQF_SHARED; - - if (dev->device == 0x0210) { - /* Version 1 PCI9030 based universal PCI adapter */ - info->misc_ctrl_value = 0x007c4080; - info->hw_version = 1; - } else { - /* Version 0 PCI9050 based 5V PCI adapter - * A PCI9050 bug prevents reading LCR registers if - * LCR base address bit 7 is set. Maintain shadow - * value so we can write to LCR misc control reg. - */ - info->misc_ctrl_value = 0x087e4546; - info->hw_version = 0; - } - - mgsl_add_device(info); - - return 0; -} - -static void __devexit synclink_remove_one (struct pci_dev *dev) -{ -} - diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c deleted file mode 100644 index a35dd549a008..000000000000 --- a/drivers/char/synclink_gt.c +++ /dev/null @@ -1,5161 +0,0 @@ -/* - * Device driver for Microgate SyncLink GT serial adapters. - * - * written by Paul Fulghum for Microgate Corporation - * paulkf@microgate.com - * - * Microgate and SyncLink are trademarks of Microgate Corporation - * - * This code is released under the GNU General Public License (GPL) - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * DEBUG OUTPUT DEFINITIONS - * - * uncomment lines below to enable specific types of debug output - * - * DBGINFO information - most verbose output - * DBGERR serious errors - * DBGBH bottom half service routine debugging - * DBGISR interrupt service routine debugging - * DBGDATA output receive and transmit data - * DBGTBUF output transmit DMA buffers and registers - * DBGRBUF output receive DMA buffers and registers - */ - -#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt -#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt -#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt -#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt -#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label)) -/*#define DBGTBUF(info) dump_tbufs(info)*/ -/*#define DBGRBUF(info) dump_rbufs(info)*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) -#define SYNCLINK_GENERIC_HDLC 1 -#else -#define SYNCLINK_GENERIC_HDLC 0 -#endif - -/* - * module identification - */ -static char *driver_name = "SyncLink GT"; -static char *tty_driver_name = "synclink_gt"; -static char *tty_dev_prefix = "ttySLG"; -MODULE_LICENSE("GPL"); -#define MGSL_MAGIC 0x5401 -#define MAX_DEVICES 32 - -static struct pci_device_id pci_table[] = { - {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - {PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - {0,}, /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, pci_table); - -static int init_one(struct pci_dev *dev,const struct pci_device_id *ent); -static void remove_one(struct pci_dev *dev); -static struct pci_driver pci_driver = { - .name = "synclink_gt", - .id_table = pci_table, - .probe = init_one, - .remove = __devexit_p(remove_one), -}; - -static bool pci_registered; - -/* - * module configuration and status - */ -static struct slgt_info *slgt_device_list; -static int slgt_device_count; - -static int ttymajor; -static int debug_level; -static int maxframe[MAX_DEVICES]; - -module_param(ttymajor, int, 0); -module_param(debug_level, int, 0); -module_param_array(maxframe, int, NULL, 0); - -MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned"); -MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail"); -MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); - -/* - * tty support and callbacks - */ -static struct tty_driver *serial_driver; - -static int open(struct tty_struct *tty, struct file * filp); -static void close(struct tty_struct *tty, struct file * filp); -static void hangup(struct tty_struct *tty); -static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); - -static int write(struct tty_struct *tty, const unsigned char *buf, int count); -static int put_char(struct tty_struct *tty, unsigned char ch); -static void send_xchar(struct tty_struct *tty, char ch); -static void wait_until_sent(struct tty_struct *tty, int timeout); -static int write_room(struct tty_struct *tty); -static void flush_chars(struct tty_struct *tty); -static void flush_buffer(struct tty_struct *tty); -static void tx_hold(struct tty_struct *tty); -static void tx_release(struct tty_struct *tty); - -static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int chars_in_buffer(struct tty_struct *tty); -static void throttle(struct tty_struct * tty); -static void unthrottle(struct tty_struct * tty); -static int set_break(struct tty_struct *tty, int break_state); - -/* - * generic HDLC support and callbacks - */ -#if SYNCLINK_GENERIC_HDLC -#define dev_to_port(D) (dev_to_hdlc(D)->priv) -static void hdlcdev_tx_done(struct slgt_info *info); -static void hdlcdev_rx(struct slgt_info *info, char *buf, int size); -static int hdlcdev_init(struct slgt_info *info); -static void hdlcdev_exit(struct slgt_info *info); -#endif - - -/* - * device specific structures, macros and functions - */ - -#define SLGT_MAX_PORTS 4 -#define SLGT_REG_SIZE 256 - -/* - * conditional wait facility - */ -struct cond_wait { - struct cond_wait *next; - wait_queue_head_t q; - wait_queue_t wait; - unsigned int data; -}; -static void init_cond_wait(struct cond_wait *w, unsigned int data); -static void add_cond_wait(struct cond_wait **head, struct cond_wait *w); -static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w); -static void flush_cond_wait(struct cond_wait **head); - -/* - * DMA buffer descriptor and access macros - */ -struct slgt_desc -{ - __le16 count; - __le16 status; - __le32 pbuf; /* physical address of data buffer */ - __le32 next; /* physical address of next descriptor */ - - /* driver book keeping */ - char *buf; /* virtual address of data buffer */ - unsigned int pdesc; /* physical address of this descriptor */ - dma_addr_t buf_dma_addr; - unsigned short buf_count; -}; - -#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) -#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b)) -#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b)) -#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0)) -#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b)) -#define desc_count(a) (le16_to_cpu((a).count)) -#define desc_status(a) (le16_to_cpu((a).status)) -#define desc_complete(a) (le16_to_cpu((a).status) & BIT15) -#define desc_eof(a) (le16_to_cpu((a).status) & BIT2) -#define desc_crc_error(a) (le16_to_cpu((a).status) & BIT1) -#define desc_abort(a) (le16_to_cpu((a).status) & BIT0) -#define desc_residue(a) ((le16_to_cpu((a).status) & 0x38) >> 3) - -struct _input_signal_events { - int ri_up; - int ri_down; - int dsr_up; - int dsr_down; - int dcd_up; - int dcd_down; - int cts_up; - int cts_down; -}; - -/* - * device instance data structure - */ -struct slgt_info { - void *if_ptr; /* General purpose pointer (used by SPPP) */ - struct tty_port port; - - struct slgt_info *next_device; /* device list link */ - - int magic; - - char device_name[25]; - struct pci_dev *pdev; - - int port_count; /* count of ports on adapter */ - int adapter_num; /* adapter instance number */ - int port_num; /* port instance number */ - - /* array of pointers to port contexts on this adapter */ - struct slgt_info *port_array[SLGT_MAX_PORTS]; - - int line; /* tty line instance number */ - - struct mgsl_icount icount; - - int timeout; - int x_char; /* xon/xoff character */ - unsigned int read_status_mask; - unsigned int ignore_status_mask; - - wait_queue_head_t status_event_wait_q; - wait_queue_head_t event_wait_q; - struct timer_list tx_timer; - struct timer_list rx_timer; - - unsigned int gpio_present; - struct cond_wait *gpio_wait_q; - - spinlock_t lock; /* spinlock for synchronizing with ISR */ - - struct work_struct task; - u32 pending_bh; - bool bh_requested; - bool bh_running; - - int isr_overflow; - bool irq_requested; /* true if IRQ requested */ - bool irq_occurred; /* for diagnostics use */ - - /* device configuration */ - - unsigned int bus_type; - unsigned int irq_level; - unsigned long irq_flags; - - unsigned char __iomem * reg_addr; /* memory mapped registers address */ - u32 phys_reg_addr; - bool reg_addr_requested; - - MGSL_PARAMS params; /* communications parameters */ - u32 idle_mode; - u32 max_frame_size; /* as set by device config */ - - unsigned int rbuf_fill_level; - unsigned int rx_pio; - unsigned int if_mode; - unsigned int base_clock; - unsigned int xsync; - unsigned int xctrl; - - /* device status */ - - bool rx_enabled; - bool rx_restart; - - bool tx_enabled; - bool tx_active; - - unsigned char signals; /* serial signal states */ - int init_error; /* initialization error */ - - unsigned char *tx_buf; - int tx_count; - - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; - bool drop_rts_on_tx_done; - struct _input_signal_events input_signal_events; - - int dcd_chkcount; /* check counts to prevent */ - int cts_chkcount; /* too many IRQs if a signal */ - int dsr_chkcount; /* is floating */ - int ri_chkcount; - - char *bufs; /* virtual address of DMA buffer lists */ - dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */ - - unsigned int rbuf_count; - struct slgt_desc *rbufs; - unsigned int rbuf_current; - unsigned int rbuf_index; - unsigned int rbuf_fill_index; - unsigned short rbuf_fill_count; - - unsigned int tbuf_count; - struct slgt_desc *tbufs; - unsigned int tbuf_current; - unsigned int tbuf_start; - - unsigned char *tmp_rbuf; - unsigned int tmp_rbuf_count; - - /* SPPP/Cisco HDLC device parts */ - - int netcount; - spinlock_t netlock; -#if SYNCLINK_GENERIC_HDLC - struct net_device *netdev; -#endif - -}; - -static MGSL_PARAMS default_params = { - .mode = MGSL_MODE_HDLC, - .loopback = 0, - .flags = HDLC_FLAG_UNDERRUN_ABORT15, - .encoding = HDLC_ENCODING_NRZI_SPACE, - .clock_speed = 0, - .addr_filter = 0xff, - .crc_type = HDLC_CRC_16_CCITT, - .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS, - .preamble = HDLC_PREAMBLE_PATTERN_NONE, - .data_rate = 9600, - .data_bits = 8, - .stop_bits = 1, - .parity = ASYNC_PARITY_NONE -}; - - -#define BH_RECEIVE 1 -#define BH_TRANSMIT 2 -#define BH_STATUS 4 -#define IO_PIN_SHUTDOWN_LIMIT 100 - -#define DMABUFSIZE 256 -#define DESC_LIST_SIZE 4096 - -#define MASK_PARITY BIT1 -#define MASK_FRAMING BIT0 -#define MASK_BREAK BIT14 -#define MASK_OVERRUN BIT4 - -#define GSR 0x00 /* global status */ -#define JCR 0x04 /* JTAG control */ -#define IODR 0x08 /* GPIO direction */ -#define IOER 0x0c /* GPIO interrupt enable */ -#define IOVR 0x10 /* GPIO value */ -#define IOSR 0x14 /* GPIO interrupt status */ -#define TDR 0x80 /* tx data */ -#define RDR 0x80 /* rx data */ -#define TCR 0x82 /* tx control */ -#define TIR 0x84 /* tx idle */ -#define TPR 0x85 /* tx preamble */ -#define RCR 0x86 /* rx control */ -#define VCR 0x88 /* V.24 control */ -#define CCR 0x89 /* clock control */ -#define BDR 0x8a /* baud divisor */ -#define SCR 0x8c /* serial control */ -#define SSR 0x8e /* serial status */ -#define RDCSR 0x90 /* rx DMA control/status */ -#define TDCSR 0x94 /* tx DMA control/status */ -#define RDDAR 0x98 /* rx DMA descriptor address */ -#define TDDAR 0x9c /* tx DMA descriptor address */ -#define XSR 0x40 /* extended sync pattern */ -#define XCR 0x44 /* extended control */ - -#define RXIDLE BIT14 -#define RXBREAK BIT14 -#define IRQ_TXDATA BIT13 -#define IRQ_TXIDLE BIT12 -#define IRQ_TXUNDER BIT11 /* HDLC */ -#define IRQ_RXDATA BIT10 -#define IRQ_RXIDLE BIT9 /* HDLC */ -#define IRQ_RXBREAK BIT9 /* async */ -#define IRQ_RXOVER BIT8 -#define IRQ_DSR BIT7 -#define IRQ_CTS BIT6 -#define IRQ_DCD BIT5 -#define IRQ_RI BIT4 -#define IRQ_ALL 0x3ff0 -#define IRQ_MASTER BIT0 - -#define slgt_irq_on(info, mask) \ - wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask))) -#define slgt_irq_off(info, mask) \ - wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask))) - -static __u8 rd_reg8(struct slgt_info *info, unsigned int addr); -static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value); -static __u16 rd_reg16(struct slgt_info *info, unsigned int addr); -static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value); -static __u32 rd_reg32(struct slgt_info *info, unsigned int addr); -static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value); - -static void msc_set_vcr(struct slgt_info *info); - -static int startup(struct slgt_info *info); -static int block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info); -static void shutdown(struct slgt_info *info); -static void program_hw(struct slgt_info *info); -static void change_params(struct slgt_info *info); - -static int register_test(struct slgt_info *info); -static int irq_test(struct slgt_info *info); -static int loopback_test(struct slgt_info *info); -static int adapter_test(struct slgt_info *info); - -static void reset_adapter(struct slgt_info *info); -static void reset_port(struct slgt_info *info); -static void async_mode(struct slgt_info *info); -static void sync_mode(struct slgt_info *info); - -static void rx_stop(struct slgt_info *info); -static void rx_start(struct slgt_info *info); -static void reset_rbufs(struct slgt_info *info); -static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); -static void rdma_reset(struct slgt_info *info); -static bool rx_get_frame(struct slgt_info *info); -static bool rx_get_buf(struct slgt_info *info); - -static void tx_start(struct slgt_info *info); -static void tx_stop(struct slgt_info *info); -static void tx_set_idle(struct slgt_info *info); -static unsigned int free_tbuf_count(struct slgt_info *info); -static unsigned int tbuf_bytes(struct slgt_info *info); -static void reset_tbufs(struct slgt_info *info); -static void tdma_reset(struct slgt_info *info); -static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count); - -static void get_signals(struct slgt_info *info); -static void set_signals(struct slgt_info *info); -static void enable_loopback(struct slgt_info *info); -static void set_rate(struct slgt_info *info, u32 data_rate); - -static int bh_action(struct slgt_info *info); -static void bh_handler(struct work_struct *work); -static void bh_transmit(struct slgt_info *info); -static void isr_serial(struct slgt_info *info); -static void isr_rdma(struct slgt_info *info); -static void isr_txeom(struct slgt_info *info, unsigned short status); -static void isr_tdma(struct slgt_info *info); - -static int alloc_dma_bufs(struct slgt_info *info); -static void free_dma_bufs(struct slgt_info *info); -static int alloc_desc(struct slgt_info *info); -static void free_desc(struct slgt_info *info); -static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); -static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); - -static int alloc_tmp_rbuf(struct slgt_info *info); -static void free_tmp_rbuf(struct slgt_info *info); - -static void tx_timeout(unsigned long context); -static void rx_timeout(unsigned long context); - -/* - * ioctl handlers - */ -static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount); -static int get_params(struct slgt_info *info, MGSL_PARAMS __user *params); -static int set_params(struct slgt_info *info, MGSL_PARAMS __user *params); -static int get_txidle(struct slgt_info *info, int __user *idle_mode); -static int set_txidle(struct slgt_info *info, int idle_mode); -static int tx_enable(struct slgt_info *info, int enable); -static int tx_abort(struct slgt_info *info); -static int rx_enable(struct slgt_info *info, int enable); -static int modem_input_wait(struct slgt_info *info,int arg); -static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); -static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int set_break(struct tty_struct *tty, int break_state); -static int get_interface(struct slgt_info *info, int __user *if_mode); -static int set_interface(struct slgt_info *info, int if_mode); -static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int get_xsync(struct slgt_info *info, int __user *if_mode); -static int set_xsync(struct slgt_info *info, int if_mode); -static int get_xctrl(struct slgt_info *info, int __user *if_mode); -static int set_xctrl(struct slgt_info *info, int if_mode); - -/* - * driver functions - */ -static void add_device(struct slgt_info *info); -static void device_init(int adapter_num, struct pci_dev *pdev); -static int claim_resources(struct slgt_info *info); -static void release_resources(struct slgt_info *info); - -/* - * DEBUG OUTPUT CODE - */ -#ifndef DBGINFO -#define DBGINFO(fmt) -#endif -#ifndef DBGERR -#define DBGERR(fmt) -#endif -#ifndef DBGBH -#define DBGBH(fmt) -#endif -#ifndef DBGISR -#define DBGISR(fmt) -#endif - -#ifdef DBGDATA -static void trace_block(struct slgt_info *info, const char *data, int count, const char *label) -{ - int i; - int linecount; - printk("%s %s data:\n",info->device_name, label); - while(count) { - linecount = (count > 16) ? 16 : count; - for(i=0; i < linecount; i++) - printk("%02X ",(unsigned char)data[i]); - for(;i<17;i++) - printk(" "); - for(i=0;i=040 && data[i]<=0176) - printk("%c",data[i]); - else - printk("."); - } - printk("\n"); - data += linecount; - count -= linecount; - } -} -#else -#define DBGDATA(info, buf, size, label) -#endif - -#ifdef DBGTBUF -static void dump_tbufs(struct slgt_info *info) -{ - int i; - printk("tbuf_current=%d\n", info->tbuf_current); - for (i=0 ; i < info->tbuf_count ; i++) { - printk("%d: count=%04X status=%04X\n", - i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status)); - } -} -#else -#define DBGTBUF(info) -#endif - -#ifdef DBGRBUF -static void dump_rbufs(struct slgt_info *info) -{ - int i; - printk("rbuf_current=%d\n", info->rbuf_current); - for (i=0 ; i < info->rbuf_count ; i++) { - printk("%d: count=%04X status=%04X\n", - i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status)); - } -} -#else -#define DBGRBUF(info) -#endif - -static inline int sanity_check(struct slgt_info *info, char *devname, const char *name) -{ -#ifdef SANITY_CHECK - if (!info) { - printk("null struct slgt_info for (%s) in %s\n", devname, name); - return 1; - } - if (info->magic != MGSL_MAGIC) { - printk("bad magic number struct slgt_info (%s) in %s\n", devname, name); - return 1; - } -#else - if (!info) - return 1; -#endif - return 0; -} - -/** - * line discipline callback wrappers - * - * The wrappers maintain line discipline references - * while calling into the line discipline. - * - * ldisc_receive_buf - pass receive data to line discipline - */ -static void ldisc_receive_buf(struct tty_struct *tty, - const __u8 *data, char *flags, int count) -{ - struct tty_ldisc *ld; - if (!tty) - return; - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, data, flags, count); - tty_ldisc_deref(ld); - } -} - -/* tty callbacks */ - -static int open(struct tty_struct *tty, struct file *filp) -{ - struct slgt_info *info; - int retval, line; - unsigned long flags; - - line = tty->index; - if ((line < 0) || (line >= slgt_device_count)) { - DBGERR(("%s: open with invalid line #%d.\n", driver_name, line)); - return -ENODEV; - } - - info = slgt_device_list; - while(info && info->line != line) - info = info->next_device; - if (sanity_check(info, tty->name, "open")) - return -ENODEV; - if (info->init_error) { - DBGERR(("%s init error=%d\n", info->device_name, info->init_error)); - return -ENODEV; - } - - tty->driver_data = info; - info->port.tty = tty; - - DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); - - /* If port is closing, signal caller to try again */ - if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - - mutex_lock(&info->port.mutex); - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - spin_lock_irqsave(&info->netlock, flags); - if (info->netcount) { - retval = -EBUSY; - spin_unlock_irqrestore(&info->netlock, flags); - mutex_unlock(&info->port.mutex); - goto cleanup; - } - info->port.count++; - spin_unlock_irqrestore(&info->netlock, flags); - - if (info->port.count == 1) { - /* 1st open on this device, init hardware */ - retval = startup(info); - if (retval < 0) { - mutex_unlock(&info->port.mutex); - goto cleanup; - } - } - mutex_unlock(&info->port.mutex); - retval = block_til_ready(tty, filp, info); - if (retval) { - DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval)); - goto cleanup; - } - - retval = 0; - -cleanup: - if (retval) { - if (tty->count == 1) - info->port.tty = NULL; /* tty layer will release tty struct */ - if(info->port.count) - info->port.count--; - } - - DBGINFO(("%s open rc=%d\n", info->device_name, retval)); - return retval; -} - -static void close(struct tty_struct *tty, struct file *filp) -{ - struct slgt_info *info = tty->driver_data; - - if (sanity_check(info, tty->name, "close")) - return; - DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); - - if (tty_port_close_start(&info->port, tty, filp) == 0) - goto cleanup; - - mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) - wait_until_sent(tty, info->timeout); - flush_buffer(tty); - tty_ldisc_flush(tty); - - shutdown(info); - mutex_unlock(&info->port.mutex); - - tty_port_close_end(&info->port, tty); - info->port.tty = NULL; -cleanup: - DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); -} - -static void hangup(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "hangup")) - return; - DBGINFO(("%s hangup\n", info->device_name)); - - flush_buffer(tty); - - mutex_lock(&info->port.mutex); - shutdown(info); - - spin_lock_irqsave(&info->port.lock, flags); - info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - spin_unlock_irqrestore(&info->port.lock, flags); - mutex_unlock(&info->port.mutex); - - wake_up_interruptible(&info->port.open_wait); -} - -static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - DBGINFO(("%s set_termios\n", tty->driver->name)); - - change_params(info); - - /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { - info->signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { - info->signals |= SerialSignal_RTS; - } - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - tx_release(tty); - } -} - -static void update_tx_timer(struct slgt_info *info) -{ - /* - * use worst case speed of 1200bps to calculate transmit timeout - * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) - */ - if (info->params.mode == MGSL_MODE_HDLC) { - int timeout = (tbuf_bytes(info) * 7) + 1000; - mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); - } -} - -static int write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - int ret = 0; - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "write")) - return -EIO; - - DBGINFO(("%s write count=%d\n", info->device_name, count)); - - if (!info->tx_buf || (count > info->max_frame_size)) - return -EIO; - - if (!count || tty->stopped || tty->hw_stopped) - return 0; - - spin_lock_irqsave(&info->lock, flags); - - if (info->tx_count) { - /* send accumulated data from send_char() */ - if (!tx_load(info, info->tx_buf, info->tx_count)) - goto cleanup; - info->tx_count = 0; - } - - if (tx_load(info, buf, count)) - ret = count; - -cleanup: - spin_unlock_irqrestore(&info->lock, flags); - DBGINFO(("%s write rc=%d\n", info->device_name, ret)); - return ret; -} - -static int put_char(struct tty_struct *tty, unsigned char ch) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - int ret = 0; - - if (sanity_check(info, tty->name, "put_char")) - return 0; - DBGINFO(("%s put_char(%d)\n", info->device_name, ch)); - if (!info->tx_buf) - return 0; - spin_lock_irqsave(&info->lock,flags); - if (info->tx_count < info->max_frame_size) { - info->tx_buf[info->tx_count++] = ch; - ret = 1; - } - spin_unlock_irqrestore(&info->lock,flags); - return ret; -} - -static void send_xchar(struct tty_struct *tty, char ch) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "send_xchar")) - return; - DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch)); - info->x_char = ch; - if (ch) { - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_enabled) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -static void wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct slgt_info *info = tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (!info ) - return; - if (sanity_check(info, tty->name, "wait_until_sent")) - return; - DBGINFO(("%s wait_until_sent entry\n", info->device_name)); - if (!(info->port.flags & ASYNC_INITIALIZED)) - goto exit; - - orig_jiffies = jiffies; - - /* Set check interval to 1/5 of estimated time to - * send a character, and make it at least 1. The check - * interval should also be less than the timeout. - * Note: use tight timings here to satisfy the NIST-PCTS. - */ - - if (info->params.data_rate) { - char_time = info->timeout/(32 * 5); - if (!char_time) - char_time++; - } else - char_time = 1; - - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - - while (info->tx_active) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } -exit: - DBGINFO(("%s wait_until_sent exit\n", info->device_name)); -} - -static int write_room(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - int ret; - - if (sanity_check(info, tty->name, "write_room")) - return 0; - ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; - DBGINFO(("%s write_room=%d\n", info->device_name, ret)); - return ret; -} - -static void flush_chars(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "flush_chars")) - return; - DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count)); - - if (info->tx_count <= 0 || tty->stopped || - tty->hw_stopped || !info->tx_buf) - return; - - DBGINFO(("%s flush_chars start transmit\n", info->device_name)); - - spin_lock_irqsave(&info->lock,flags); - if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock,flags); -} - -static void flush_buffer(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "flush_buffer")) - return; - DBGINFO(("%s flush_buffer\n", info->device_name)); - - spin_lock_irqsave(&info->lock, flags); - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock, flags); - - tty_wakeup(tty); -} - -/* - * throttle (stop) transmitter - */ -static void tx_hold(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_hold")) - return; - DBGINFO(("%s tx_hold\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC) - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); -} - -/* - * release (start) transmitter - */ -static void tx_release(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_release")) - return; - DBGINFO(("%s tx_release\n", info->device_name)); - spin_lock_irqsave(&info->lock, flags); - if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock, flags); -} - -/* - * Service an IOCTL request - * - * Arguments - * - * tty pointer to tty instance data - * cmd IOCTL command code - * arg command argument/context - * - * Return 0 if success, otherwise error code - */ -static int ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct slgt_info *info = tty->driver_data; - void __user *argp = (void __user *)arg; - int ret; - - if (sanity_check(info, tty->name, "ioctl")) - return -ENODEV; - DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case MGSL_IOCWAITEVENT: - return wait_mgsl_event(info, argp); - case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); - case MGSL_IOCSGPIO: - return set_gpio(info, argp); - case MGSL_IOCGGPIO: - return get_gpio(info, argp); - case MGSL_IOCWAITGPIO: - return wait_gpio(info, argp); - case MGSL_IOCGXSYNC: - return get_xsync(info, argp); - case MGSL_IOCSXSYNC: - return set_xsync(info, (int)arg); - case MGSL_IOCGXCTRL: - return get_xctrl(info, argp); - case MGSL_IOCSXCTRL: - return set_xctrl(info, (int)arg); - } - mutex_lock(&info->port.mutex); - switch (cmd) { - case MGSL_IOCGPARAMS: - ret = get_params(info, argp); - break; - case MGSL_IOCSPARAMS: - ret = set_params(info, argp); - break; - case MGSL_IOCGTXIDLE: - ret = get_txidle(info, argp); - break; - case MGSL_IOCSTXIDLE: - ret = set_txidle(info, (int)arg); - break; - case MGSL_IOCTXENABLE: - ret = tx_enable(info, (int)arg); - break; - case MGSL_IOCRXENABLE: - ret = rx_enable(info, (int)arg); - break; - case MGSL_IOCTXABORT: - ret = tx_abort(info); - break; - case MGSL_IOCGSTATS: - ret = get_stats(info, argp); - break; - case MGSL_IOCGIF: - ret = get_interface(info, argp); - break; - case MGSL_IOCSIF: - ret = set_interface(info,(int)arg); - break; - default: - ret = -ENOIOCTLCMD; - } - mutex_unlock(&info->port.mutex); - return ret; -} - -static int get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) - -{ - struct slgt_info *info = tty->driver_data; - struct mgsl_icount cnow; /* kernel counter temps */ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->lock,flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - -/* - * support for 32 bit ioctl calls on 64 bit systems - */ -#ifdef CONFIG_COMPAT -static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) -{ - struct MGSL_PARAMS32 tmp_params; - - DBGINFO(("%s get_params32\n", info->device_name)); - memset(&tmp_params, 0, sizeof(tmp_params)); - tmp_params.mode = (compat_ulong_t)info->params.mode; - tmp_params.loopback = info->params.loopback; - tmp_params.flags = info->params.flags; - tmp_params.encoding = info->params.encoding; - tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; - tmp_params.addr_filter = info->params.addr_filter; - tmp_params.crc_type = info->params.crc_type; - tmp_params.preamble_length = info->params.preamble_length; - tmp_params.preamble = info->params.preamble; - tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; - tmp_params.data_bits = info->params.data_bits; - tmp_params.stop_bits = info->params.stop_bits; - tmp_params.parity = info->params.parity; - if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) - return -EFAULT; - return 0; -} - -static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) -{ - struct MGSL_PARAMS32 tmp_params; - - DBGINFO(("%s set_params32\n", info->device_name)); - if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) - return -EFAULT; - - spin_lock(&info->lock); - if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) { - info->base_clock = tmp_params.clock_speed; - } else { - info->params.mode = tmp_params.mode; - info->params.loopback = tmp_params.loopback; - info->params.flags = tmp_params.flags; - info->params.encoding = tmp_params.encoding; - info->params.clock_speed = tmp_params.clock_speed; - info->params.addr_filter = tmp_params.addr_filter; - info->params.crc_type = tmp_params.crc_type; - info->params.preamble_length = tmp_params.preamble_length; - info->params.preamble = tmp_params.preamble; - info->params.data_rate = tmp_params.data_rate; - info->params.data_bits = tmp_params.data_bits; - info->params.stop_bits = tmp_params.stop_bits; - info->params.parity = tmp_params.parity; - } - spin_unlock(&info->lock); - - program_hw(info); - - return 0; -} - -static long slgt_compat_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct slgt_info *info = tty->driver_data; - int rc = -ENOIOCTLCMD; - - if (sanity_check(info, tty->name, "compat_ioctl")) - return -ENODEV; - DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); - - switch (cmd) { - - case MGSL_IOCSPARAMS32: - rc = set_params32(info, compat_ptr(arg)); - break; - - case MGSL_IOCGPARAMS32: - rc = get_params32(info, compat_ptr(arg)); - break; - - case MGSL_IOCGPARAMS: - case MGSL_IOCSPARAMS: - case MGSL_IOCGTXIDLE: - case MGSL_IOCGSTATS: - case MGSL_IOCWAITEVENT: - case MGSL_IOCGIF: - case MGSL_IOCSGPIO: - case MGSL_IOCGGPIO: - case MGSL_IOCWAITGPIO: - case MGSL_IOCGXSYNC: - case MGSL_IOCGXCTRL: - case MGSL_IOCSTXIDLE: - case MGSL_IOCTXENABLE: - case MGSL_IOCRXENABLE: - case MGSL_IOCTXABORT: - case TIOCMIWAIT: - case MGSL_IOCSIF: - case MGSL_IOCSXSYNC: - case MGSL_IOCSXCTRL: - rc = ioctl(tty, cmd, arg); - break; - } - - DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); - return rc; -} -#else -#define slgt_compat_ioctl NULL -#endif /* ifdef CONFIG_COMPAT */ - -/* - * proc fs support - */ -static inline void line_info(struct seq_file *m, struct slgt_info *info) -{ - char stat_buf[30]; - unsigned long flags; - - seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", - info->device_name, info->phys_reg_addr, - info->irq_level, info->max_frame_size); - - /* output current serial signal states */ - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (info->signals & SerialSignal_RTS) - strcat(stat_buf, "|RTS"); - if (info->signals & SerialSignal_CTS) - strcat(stat_buf, "|CTS"); - if (info->signals & SerialSignal_DTR) - strcat(stat_buf, "|DTR"); - if (info->signals & SerialSignal_DSR) - strcat(stat_buf, "|DSR"); - if (info->signals & SerialSignal_DCD) - strcat(stat_buf, "|CD"); - if (info->signals & SerialSignal_RI) - strcat(stat_buf, "|RI"); - - if (info->params.mode != MGSL_MODE_ASYNC) { - seq_printf(m, "\tHDLC txok:%d rxok:%d", - info->icount.txok, info->icount.rxok); - if (info->icount.txunder) - seq_printf(m, " txunder:%d", info->icount.txunder); - if (info->icount.txabort) - seq_printf(m, " txabort:%d", info->icount.txabort); - if (info->icount.rxshort) - seq_printf(m, " rxshort:%d", info->icount.rxshort); - if (info->icount.rxlong) - seq_printf(m, " rxlong:%d", info->icount.rxlong); - if (info->icount.rxover) - seq_printf(m, " rxover:%d", info->icount.rxover); - if (info->icount.rxcrc) - seq_printf(m, " rxcrc:%d", info->icount.rxcrc); - } else { - seq_printf(m, "\tASYNC tx:%d rx:%d", - info->icount.tx, info->icount.rx); - if (info->icount.frame) - seq_printf(m, " fe:%d", info->icount.frame); - if (info->icount.parity) - seq_printf(m, " pe:%d", info->icount.parity); - if (info->icount.brk) - seq_printf(m, " brk:%d", info->icount.brk); - if (info->icount.overrun) - seq_printf(m, " oe:%d", info->icount.overrun); - } - - /* Append serial signal status to end */ - seq_printf(m, " %s\n", stat_buf+1); - - seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", - info->tx_active,info->bh_requested,info->bh_running, - info->pending_bh); -} - -/* Called to print information about devices - */ -static int synclink_gt_proc_show(struct seq_file *m, void *v) -{ - struct slgt_info *info; - - seq_puts(m, "synclink_gt driver\n"); - - info = slgt_device_list; - while( info ) { - line_info(m, info); - info = info->next_device; - } - return 0; -} - -static int synclink_gt_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, synclink_gt_proc_show, NULL); -} - -static const struct file_operations synclink_gt_proc_fops = { - .owner = THIS_MODULE, - .open = synclink_gt_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* - * return count of bytes in transmit buffer - */ -static int chars_in_buffer(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - int count; - if (sanity_check(info, tty->name, "chars_in_buffer")) - return 0; - count = tbuf_bytes(info); - DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count)); - return count; -} - -/* - * signal remote device to throttle send data (our receive data) - */ -static void throttle(struct tty_struct * tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "throttle")) - return; - DBGINFO(("%s throttle\n", info->device_name)); - if (I_IXOFF(tty)) - send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->lock,flags); - info->signals &= ~SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* - * signal remote device to stop throttling send data (our receive data) - */ -static void unthrottle(struct tty_struct * tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "unthrottle")) - return; - DBGINFO(("%s unthrottle\n", info->device_name)); - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - send_xchar(tty, START_CHAR(tty)); - } - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->lock,flags); - info->signals |= SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* - * set or clear transmit break condition - * break_state -1=set break condition, 0=clear - */ -static int set_break(struct tty_struct *tty, int break_state) -{ - struct slgt_info *info = tty->driver_data; - unsigned short value; - unsigned long flags; - - if (sanity_check(info, tty->name, "set_break")) - return -EINVAL; - DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); - - spin_lock_irqsave(&info->lock,flags); - value = rd_reg16(info, TCR); - if (break_state == -1) - value |= BIT6; - else - value &= ~BIT6; - wr_reg16(info, TCR, value); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -#if SYNCLINK_GENERIC_HDLC - -/** - * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) - * set encoding and frame check sequence (FCS) options - * - * dev pointer to network device structure - * encoding serial encoding setting - * parity FCS setting - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, - unsigned short parity) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned char new_encoding; - unsigned short new_crctype; - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - DBGINFO(("%s hdlcdev_attach\n", info->device_name)); - - switch (encoding) - { - case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; - case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; - case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; - case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; - case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; - default: return -EINVAL; - } - - switch (parity) - { - case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; - case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; - case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; - default: return -EINVAL; - } - - info->params.encoding = new_encoding; - info->params.crc_type = new_crctype; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - - return 0; -} - -/** - * called by generic HDLC layer to send frame - * - * skb socket buffer containing HDLC frame - * dev pointer to network device structure - */ -static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlc_xmit\n", dev->name)); - - if (!skb->len) - return NETDEV_TX_OK; - - /* stop sending until this frame completes */ - netif_stop_queue(dev); - - /* update network statistics */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - /* save start time for transmit timeout detection */ - dev->trans_start = jiffies; - - spin_lock_irqsave(&info->lock, flags); - tx_load(info, skb->data, skb->len); - spin_unlock_irqrestore(&info->lock, flags); - - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/** - * called by network layer when interface enabled - * claim resources and initialize hardware - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_open(struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - int rc; - unsigned long flags; - - if (!try_module_get(THIS_MODULE)) - return -EBUSY; - - DBGINFO(("%s hdlcdev_open\n", dev->name)); - - /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) - return rc; - - /* arbitrate between network and tty opens */ - spin_lock_irqsave(&info->netlock, flags); - if (info->port.count != 0 || info->netcount != 0) { - DBGINFO(("%s hdlc_open busy\n", dev->name)); - spin_unlock_irqrestore(&info->netlock, flags); - return -EBUSY; - } - info->netcount=1; - spin_unlock_irqrestore(&info->netlock, flags); - - /* claim resources and init adapter */ - if ((rc = startup(info)) != 0) { - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - - /* assert DTR and RTS, apply hardware settings */ - info->signals |= SerialSignal_RTS + SerialSignal_DTR; - program_hw(info); - - /* enable network layer transmit */ - dev->trans_start = jiffies; - netif_start_queue(dev); - - /* inform generic HDLC layer of current DCD status */ - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - if (info->signals & SerialSignal_DCD) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - return 0; -} - -/** - * called by network layer when interface is disabled - * shutdown hardware and release resources - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_close(struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlcdev_close\n", dev->name)); - - netif_stop_queue(dev); - - /* shutdown adapter and release resources */ - shutdown(info); - - hdlc_close(dev); - - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - - module_put(THIS_MODULE); - return 0; -} - -/** - * called by network layer to process IOCTL call to network device - * - * dev pointer to network device structure - * ifr pointer to network interface request structure - * cmd IOCTL command code - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - const size_t size = sizeof(sync_serial_settings); - sync_serial_settings new_line; - sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; - struct slgt_info *info = dev_to_port(dev); - unsigned int flags; - - DBGINFO(("%s hdlcdev_ioctl\n", dev->name)); - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - if (cmd != SIOCWANDEV) - return hdlc_ioctl(dev, ifr, cmd); - - memset(&new_line, 0, sizeof(new_line)); - - switch(ifr->ifr_settings.type) { - case IF_GET_IFACE: /* return current sync_serial_settings */ - - ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; - if (ifr->ifr_settings.size < size) { - ifr->ifr_settings.size = size; /* data size wanted */ - return -ENOBUFS; - } - - flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - - switch (flags){ - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; - case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; - default: new_line.clock_type = CLOCK_DEFAULT; - } - - new_line.clock_rate = info->params.clock_speed; - new_line.loopback = info->params.loopback ? 1:0; - - if (copy_to_user(line, &new_line, size)) - return -EFAULT; - return 0; - - case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&new_line, line, size)) - return -EFAULT; - - switch (new_line.clock_type) - { - case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; - case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; - case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; - case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; - case CLOCK_DEFAULT: flags = info->params.flags & - (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; - default: return -EINVAL; - } - - if (new_line.loopback != 0 && new_line.loopback != 1) - return -EINVAL; - - info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - info->params.flags |= flags; - - info->params.loopback = new_line.loopback; - - if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) - info->params.clock_speed = new_line.clock_rate; - else - info->params.clock_speed = 0; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - return 0; - - default: - return hdlc_ioctl(dev, ifr, cmd); - } -} - -/** - * called by network layer when transmit timeout is detected - * - * dev pointer to network device structure - */ -static void hdlcdev_tx_timeout(struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); - - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - - spin_lock_irqsave(&info->lock,flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); - - netif_wake_queue(dev); -} - -/** - * called by device driver when transmit completes - * reenable network layer transmit if stopped - * - * info pointer to device instance information - */ -static void hdlcdev_tx_done(struct slgt_info *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); -} - -/** - * called by device driver when frame received - * pass frame to network layer - * - * info pointer to device instance information - * buf pointer to buffer contianing frame data - * size count of data bytes in buf - */ -static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - struct net_device *dev = info->netdev; - - DBGINFO(("%s hdlcdev_rx\n", dev->name)); - - if (skb == NULL) { - DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); - dev->stats.rx_dropped++; - return; - } - - memcpy(skb_put(skb, size), buf, size); - - skb->protocol = hdlc_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - netif_rx(skb); -} - -static const struct net_device_ops hdlcdev_ops = { - .ndo_open = hdlcdev_open, - .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_do_ioctl = hdlcdev_ioctl, - .ndo_tx_timeout = hdlcdev_tx_timeout, -}; - -/** - * called by device driver when adding device instance - * do generic HDLC initialization - * - * info pointer to device instance information - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_init(struct slgt_info *info) -{ - int rc; - struct net_device *dev; - hdlc_device *hdlc; - - /* allocate and initialize network and HDLC layer objects */ - - if (!(dev = alloc_hdlcdev(info))) { - printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); - return -ENOMEM; - } - - /* for network layer reporting purposes only */ - dev->mem_start = info->phys_reg_addr; - dev->mem_end = info->phys_reg_addr + SLGT_REG_SIZE - 1; - dev->irq = info->irq_level; - - /* network layer callbacks and settings */ - dev->netdev_ops = &hdlcdev_ops; - dev->watchdog_timeo = 10 * HZ; - dev->tx_queue_len = 50; - - /* generic HDLC layer callbacks and settings */ - hdlc = dev_to_hdlc(dev); - hdlc->attach = hdlcdev_attach; - hdlc->xmit = hdlcdev_xmit; - - /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { - printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); - free_netdev(dev); - return rc; - } - - info->netdev = dev; - return 0; -} - -/** - * called by device driver when removing device instance - * do generic HDLC cleanup - * - * info pointer to device instance information - */ -static void hdlcdev_exit(struct slgt_info *info) -{ - unregister_hdlc_device(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; -} - -#endif /* ifdef CONFIG_HDLC */ - -/* - * get async data from rx DMA buffers - */ -static void rx_async(struct slgt_info *info) -{ - struct tty_struct *tty = info->port.tty; - struct mgsl_icount *icount = &info->icount; - unsigned int start, end; - unsigned char *p; - unsigned char status; - struct slgt_desc *bufs = info->rbufs; - int i, count; - int chars = 0; - int stat; - unsigned char ch; - - start = end = info->rbuf_current; - - while(desc_complete(bufs[end])) { - count = desc_count(bufs[end]) - info->rbuf_index; - p = bufs[end].buf + info->rbuf_index; - - DBGISR(("%s rx_async count=%d\n", info->device_name, count)); - DBGDATA(info, p, count, "rx"); - - for(i=0 ; i < count; i+=2, p+=2) { - ch = *p; - icount->rx++; - - stat = 0; - - if ((status = *(p+1) & (BIT1 + BIT0))) { - if (status & BIT1) - icount->parity++; - else if (status & BIT0) - icount->frame++; - /* discard char if tty control flags say so */ - if (status & info->ignore_status_mask) - continue; - if (status & BIT1) - stat = TTY_PARITY; - else if (status & BIT0) - stat = TTY_FRAME; - } - if (tty) { - tty_insert_flip_char(tty, ch, stat); - chars++; - } - } - - if (i < count) { - /* receive buffer not completed */ - info->rbuf_index += i; - mod_timer(&info->rx_timer, jiffies + 1); - break; - } - - info->rbuf_index = 0; - free_rbufs(info, end, end); - - if (++end == info->rbuf_count) - end = 0; - - /* if entire list searched then no frame available */ - if (end == start) - break; - } - - if (tty && chars) - tty_flip_buffer_push(tty); -} - -/* - * return next bottom half action to perform - */ -static int bh_action(struct slgt_info *info) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&info->lock,flags); - - if (info->pending_bh & BH_RECEIVE) { - info->pending_bh &= ~BH_RECEIVE; - rc = BH_RECEIVE; - } else if (info->pending_bh & BH_TRANSMIT) { - info->pending_bh &= ~BH_TRANSMIT; - rc = BH_TRANSMIT; - } else if (info->pending_bh & BH_STATUS) { - info->pending_bh &= ~BH_STATUS; - rc = BH_STATUS; - } else { - /* Mark BH routine as complete */ - info->bh_running = false; - info->bh_requested = false; - rc = 0; - } - - spin_unlock_irqrestore(&info->lock,flags); - - return rc; -} - -/* - * perform bottom half processing - */ -static void bh_handler(struct work_struct *work) -{ - struct slgt_info *info = container_of(work, struct slgt_info, task); - int action; - - if (!info) - return; - info->bh_running = true; - - while((action = bh_action(info))) { - switch (action) { - case BH_RECEIVE: - DBGBH(("%s bh receive\n", info->device_name)); - switch(info->params.mode) { - case MGSL_MODE_ASYNC: - rx_async(info); - break; - case MGSL_MODE_HDLC: - while(rx_get_frame(info)); - break; - case MGSL_MODE_RAW: - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - case MGSL_MODE_XSYNC: - while(rx_get_buf(info)); - break; - } - /* restart receiver if rx DMA buffers exhausted */ - if (info->rx_restart) - rx_start(info); - break; - case BH_TRANSMIT: - bh_transmit(info); - break; - case BH_STATUS: - DBGBH(("%s bh status\n", info->device_name)); - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - break; - default: - DBGBH(("%s unknown action\n", info->device_name)); - break; - } - } - DBGBH(("%s bh_handler exit\n", info->device_name)); -} - -static void bh_transmit(struct slgt_info *info) -{ - struct tty_struct *tty = info->port.tty; - - DBGBH(("%s bh_transmit\n", info->device_name)); - if (tty) - tty_wakeup(tty); -} - -static void dsr_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT3) { - info->signals |= SerialSignal_DSR; - info->input_signal_events.dsr_up++; - } else { - info->signals &= ~SerialSignal_DSR; - info->input_signal_events.dsr_down++; - } - DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_DSR); - return; - } - info->icount.dsr++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -static void cts_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT2) { - info->signals |= SerialSignal_CTS; - info->input_signal_events.cts_up++; - } else { - info->signals &= ~SerialSignal_CTS; - info->input_signal_events.cts_down++; - } - DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_CTS); - return; - } - info->icount.cts++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; - - if (info->port.flags & ASYNC_CTS_FLOW) { - if (info->port.tty) { - if (info->port.tty->hw_stopped) { - if (info->signals & SerialSignal_CTS) { - info->port.tty->hw_stopped = 0; - info->pending_bh |= BH_TRANSMIT; - return; - } - } else { - if (!(info->signals & SerialSignal_CTS)) - info->port.tty->hw_stopped = 1; - } - } - } -} - -static void dcd_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT1) { - info->signals |= SerialSignal_DCD; - info->input_signal_events.dcd_up++; - } else { - info->signals &= ~SerialSignal_DCD; - info->input_signal_events.dcd_down++; - } - DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_DCD); - return; - } - info->icount.dcd++; -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) { - if (info->signals & SerialSignal_DCD) - netif_carrier_on(info->netdev); - else - netif_carrier_off(info->netdev); - } -#endif - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; - - if (info->port.flags & ASYNC_CHECK_CD) { - if (info->signals & SerialSignal_DCD) - wake_up_interruptible(&info->port.open_wait); - else { - if (info->port.tty) - tty_hangup(info->port.tty); - } - } -} - -static void ri_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT0) { - info->signals |= SerialSignal_RI; - info->input_signal_events.ri_up++; - } else { - info->signals &= ~SerialSignal_RI; - info->input_signal_events.ri_down++; - } - DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_RI); - return; - } - info->icount.rng++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -static void isr_rxdata(struct slgt_info *info) -{ - unsigned int count = info->rbuf_fill_count; - unsigned int i = info->rbuf_fill_index; - unsigned short reg; - - while (rd_reg16(info, SSR) & IRQ_RXDATA) { - reg = rd_reg16(info, RDR); - DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg)); - if (desc_complete(info->rbufs[i])) { - /* all buffers full */ - rx_stop(info); - info->rx_restart = 1; - continue; - } - info->rbufs[i].buf[count++] = (unsigned char)reg; - /* async mode saves status byte to buffer for each data byte */ - if (info->params.mode == MGSL_MODE_ASYNC) - info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8); - if (count == info->rbuf_fill_level || (reg & BIT10)) { - /* buffer full or end of frame */ - set_desc_count(info->rbufs[i], count); - set_desc_status(info->rbufs[i], BIT15 | (reg >> 8)); - info->rbuf_fill_count = count = 0; - if (++i == info->rbuf_count) - i = 0; - info->pending_bh |= BH_RECEIVE; - } - } - - info->rbuf_fill_index = i; - info->rbuf_fill_count = count; -} - -static void isr_serial(struct slgt_info *info) -{ - unsigned short status = rd_reg16(info, SSR); - - DBGISR(("%s isr_serial status=%04X\n", info->device_name, status)); - - wr_reg16(info, SSR, status); /* clear pending */ - - info->irq_occurred = true; - - if (info->params.mode == MGSL_MODE_ASYNC) { - if (status & IRQ_TXIDLE) { - if (info->tx_active) - isr_txeom(info, status); - } - if (info->rx_pio && (status & IRQ_RXDATA)) - isr_rxdata(info); - if ((status & IRQ_RXBREAK) && (status & RXBREAK)) { - info->icount.brk++; - /* process break detection if tty control allows */ - if (info->port.tty) { - if (!(status & info->ignore_status_mask)) { - if (info->read_status_mask & MASK_BREAK) { - tty_insert_flip_char(info->port.tty, 0, TTY_BREAK); - if (info->port.flags & ASYNC_SAK) - do_SAK(info->port.tty); - } - } - } - } - } else { - if (status & (IRQ_TXIDLE + IRQ_TXUNDER)) - isr_txeom(info, status); - if (info->rx_pio && (status & IRQ_RXDATA)) - isr_rxdata(info); - if (status & IRQ_RXIDLE) { - if (status & RXIDLE) - info->icount.rxidle++; - else - info->icount.exithunt++; - wake_up_interruptible(&info->event_wait_q); - } - - if (status & IRQ_RXOVER) - rx_start(info); - } - - if (status & IRQ_DSR) - dsr_change(info, status); - if (status & IRQ_CTS) - cts_change(info, status); - if (status & IRQ_DCD) - dcd_change(info, status); - if (status & IRQ_RI) - ri_change(info, status); -} - -static void isr_rdma(struct slgt_info *info) -{ - unsigned int status = rd_reg32(info, RDCSR); - - DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status)); - - /* RDCSR (rx DMA control/status) - * - * 31..07 reserved - * 06 save status byte to DMA buffer - * 05 error - * 04 eol (end of list) - * 03 eob (end of buffer) - * 02 IRQ enable - * 01 reset - * 00 enable - */ - wr_reg32(info, RDCSR, status); /* clear pending */ - - if (status & (BIT5 + BIT4)) { - DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name)); - info->rx_restart = true; - } - info->pending_bh |= BH_RECEIVE; -} - -static void isr_tdma(struct slgt_info *info) -{ - unsigned int status = rd_reg32(info, TDCSR); - - DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status)); - - /* TDCSR (tx DMA control/status) - * - * 31..06 reserved - * 05 error - * 04 eol (end of list) - * 03 eob (end of buffer) - * 02 IRQ enable - * 01 reset - * 00 enable - */ - wr_reg32(info, TDCSR, status); /* clear pending */ - - if (status & (BIT5 + BIT4 + BIT3)) { - // another transmit buffer has completed - // run bottom half to get more send data from user - info->pending_bh |= BH_TRANSMIT; - } -} - -/* - * return true if there are unsent tx DMA buffers, otherwise false - * - * if there are unsent buffers then info->tbuf_start - * is set to index of first unsent buffer - */ -static bool unsent_tbufs(struct slgt_info *info) -{ - unsigned int i = info->tbuf_current; - bool rc = false; - - /* - * search backwards from last loaded buffer (precedes tbuf_current) - * for first unsent buffer (desc_count > 0) - */ - - do { - if (i) - i--; - else - i = info->tbuf_count - 1; - if (!desc_count(info->tbufs[i])) - break; - info->tbuf_start = i; - rc = true; - } while (i != info->tbuf_current); - - return rc; -} - -static void isr_txeom(struct slgt_info *info, unsigned short status) -{ - DBGISR(("%s txeom status=%04x\n", info->device_name, status)); - - slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); - tdma_reset(info); - if (status & IRQ_TXUNDER) { - unsigned short val = rd_reg16(info, TCR); - wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, TCR, val); /* clear reset bit */ - } - - if (info->tx_active) { - if (info->params.mode != MGSL_MODE_ASYNC) { - if (status & IRQ_TXUNDER) - info->icount.txunder++; - else if (status & IRQ_TXIDLE) - info->icount.txok++; - } - - if (unsent_tbufs(info)) { - tx_start(info); - update_tx_timer(info); - return; - } - info->tx_active = false; - - del_timer(&info->tx_timer); - - if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { - info->signals &= ~SerialSignal_RTS; - info->drop_rts_on_tx_done = false; - set_signals(info); - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { - tx_stop(info); - return; - } - info->pending_bh |= BH_TRANSMIT; - } - } -} - -static void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state) -{ - struct cond_wait *w, *prev; - - /* wake processes waiting for specific transitions */ - for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) { - if (w->data & changed) { - w->data = state; - wake_up_interruptible(&w->q); - if (prev != NULL) - prev->next = w->next; - else - info->gpio_wait_q = w->next; - } else - prev = w; - } -} - -/* interrupt service routine - * - * irq interrupt number - * dev_id device ID supplied during interrupt registration - */ -static irqreturn_t slgt_interrupt(int dummy, void *dev_id) -{ - struct slgt_info *info = dev_id; - unsigned int gsr; - unsigned int i; - - DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); - - while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { - DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); - info->irq_occurred = true; - for(i=0; i < info->port_count ; i++) { - if (info->port_array[i] == NULL) - continue; - spin_lock(&info->port_array[i]->lock); - if (gsr & (BIT8 << i)) - isr_serial(info->port_array[i]); - if (gsr & (BIT16 << (i*2))) - isr_rdma(info->port_array[i]); - if (gsr & (BIT17 << (i*2))) - isr_tdma(info->port_array[i]); - spin_unlock(&info->port_array[i]->lock); - } - } - - if (info->gpio_present) { - unsigned int state; - unsigned int changed; - spin_lock(&info->lock); - while ((changed = rd_reg32(info, IOSR)) != 0) { - DBGISR(("%s iosr=%08x\n", info->device_name, changed)); - /* read latched state of GPIO signals */ - state = rd_reg32(info, IOVR); - /* clear pending GPIO interrupt bits */ - wr_reg32(info, IOSR, changed); - for (i=0 ; i < info->port_count ; i++) { - if (info->port_array[i] != NULL) - isr_gpio(info->port_array[i], changed, state); - } - } - spin_unlock(&info->lock); - } - - for(i=0; i < info->port_count ; i++) { - struct slgt_info *port = info->port_array[i]; - if (port == NULL) - continue; - spin_lock(&port->lock); - if ((port->port.count || port->netcount) && - port->pending_bh && !port->bh_running && - !port->bh_requested) { - DBGISR(("%s bh queued\n", port->device_name)); - schedule_work(&port->task); - port->bh_requested = true; - } - spin_unlock(&port->lock); - } - - DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); - return IRQ_HANDLED; -} - -static int startup(struct slgt_info *info) -{ - DBGINFO(("%s startup\n", info->device_name)); - - if (info->port.flags & ASYNC_INITIALIZED) - return 0; - - if (!info->tx_buf) { - info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); - if (!info->tx_buf) { - DBGERR(("%s can't allocate tx buffer\n", info->device_name)); - return -ENOMEM; - } - } - - info->pending_bh = 0; - - memset(&info->icount, 0, sizeof(info->icount)); - - /* program hardware for current parameters */ - change_params(info); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags |= ASYNC_INITIALIZED; - - return 0; -} - -/* - * called by close() and hangup() to shutdown hardware - */ -static void shutdown(struct slgt_info *info) -{ - unsigned long flags; - - if (!(info->port.flags & ASYNC_INITIALIZED)) - return; - - DBGINFO(("%s shutdown\n", info->device_name)); - - /* clear status wait queue because status changes */ - /* can't happen after shutting down the hardware */ - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - del_timer_sync(&info->tx_timer); - del_timer_sync(&info->rx_timer); - - kfree(info->tx_buf); - info->tx_buf = NULL; - - spin_lock_irqsave(&info->lock,flags); - - tx_stop(info); - rx_stop(info); - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { - info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); - set_signals(info); - } - - flush_cond_wait(&info->gpio_wait_q); - - spin_unlock_irqrestore(&info->lock,flags); - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags &= ~ASYNC_INITIALIZED; -} - -static void program_hw(struct slgt_info *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - - rx_stop(info); - tx_stop(info); - - if (info->params.mode != MGSL_MODE_ASYNC || - info->netcount) - sync_mode(info); - else - async_mode(info); - - set_signals(info); - - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - - slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI); - get_signals(info); - - if (info->netcount || - (info->port.tty && info->port.tty->termios->c_cflag & CREAD)) - rx_start(info); - - spin_unlock_irqrestore(&info->lock,flags); -} - -/* - * reconfigure adapter based on new parameters - */ -static void change_params(struct slgt_info *info) -{ - unsigned cflag; - int bits_per_char; - - if (!info->port.tty || !info->port.tty->termios) - return; - DBGINFO(("%s change_params\n", info->device_name)); - - cflag = info->port.tty->termios->c_cflag; - - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ - if (cflag & CBAUD) - info->signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - - /* byte size and parity */ - - switch (cflag & CSIZE) { - case CS5: info->params.data_bits = 5; break; - case CS6: info->params.data_bits = 6; break; - case CS7: info->params.data_bits = 7; break; - case CS8: info->params.data_bits = 8; break; - default: info->params.data_bits = 7; break; - } - - info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1; - - if (cflag & PARENB) - info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN; - else - info->params.parity = ASYNC_PARITY_NONE; - - /* calculate number of jiffies to transmit a full - * FIFO (32 bytes) at specified data rate - */ - bits_per_char = info->params.data_bits + - info->params.stop_bits + 1; - - info->params.data_rate = tty_get_baud_rate(info->port.tty); - - if (info->params.data_rate) { - info->timeout = (32*HZ*bits_per_char) / - info->params.data_rate; - } - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; - - /* process tty input control flags */ - - info->read_status_mask = IRQ_RXOVER; - if (I_INPCK(info->port.tty)) - info->read_status_mask |= MASK_PARITY | MASK_FRAMING; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= MASK_BREAK; - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask |= MASK_BREAK; - /* If ignoring parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= MASK_OVERRUN; - } - - program_hw(info); -} - -static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount) -{ - DBGINFO(("%s get_stats\n", info->device_name)); - if (!user_icount) { - memset(&info->icount, 0, sizeof(info->icount)); - } else { - if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount))) - return -EFAULT; - } - return 0; -} - -static int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params) -{ - DBGINFO(("%s get_params\n", info->device_name)); - if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS))) - return -EFAULT; - return 0; -} - -static int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params) -{ - unsigned long flags; - MGSL_PARAMS tmp_params; - - DBGINFO(("%s set_params\n", info->device_name)); - if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS))) - return -EFAULT; - - spin_lock_irqsave(&info->lock, flags); - if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) - info->base_clock = tmp_params.clock_speed; - else - memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS)); - spin_unlock_irqrestore(&info->lock, flags); - - program_hw(info); - - return 0; -} - -static int get_txidle(struct slgt_info *info, int __user *idle_mode) -{ - DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode)); - if (put_user(info->idle_mode, idle_mode)) - return -EFAULT; - return 0; -} - -static int set_txidle(struct slgt_info *info, int idle_mode) -{ - unsigned long flags; - DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode)); - spin_lock_irqsave(&info->lock,flags); - info->idle_mode = idle_mode; - if (info->params.mode != MGSL_MODE_ASYNC) - tx_set_idle(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int tx_enable(struct slgt_info *info, int enable) -{ - unsigned long flags; - DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable)); - spin_lock_irqsave(&info->lock,flags); - if (enable) { - if (!info->tx_enabled) - tx_start(info); - } else { - if (info->tx_enabled) - tx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* - * abort transmit HDLC frame - */ -static int tx_abort(struct slgt_info *info) -{ - unsigned long flags; - DBGINFO(("%s tx_abort\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - tdma_reset(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int rx_enable(struct slgt_info *info, int enable) -{ - unsigned long flags; - unsigned int rbuf_fill_level; - DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); - spin_lock_irqsave(&info->lock,flags); - /* - * enable[31..16] = receive DMA buffer fill level - * 0 = noop (leave fill level unchanged) - * fill level must be multiple of 4 and <= buffer size - */ - rbuf_fill_level = ((unsigned int)enable) >> 16; - if (rbuf_fill_level) { - if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { - spin_unlock_irqrestore(&info->lock, flags); - return -EINVAL; - } - info->rbuf_fill_level = rbuf_fill_level; - if (rbuf_fill_level < 128) - info->rx_pio = 1; /* PIO mode */ - else - info->rx_pio = 0; /* DMA mode */ - rx_stop(info); /* restart receiver to use new fill level */ - } - - /* - * enable[1..0] = receiver enable command - * 0 = disable - * 1 = enable - * 2 = enable or force hunt mode if already enabled - */ - enable &= 3; - if (enable) { - if (!info->rx_enabled) - rx_start(info); - else if (enable == 2) { - /* force hunt mode (write 1 to RCR[3]) */ - wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); - } - } else { - if (info->rx_enabled) - rx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* - * wait for specified event to occur - */ -static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr) -{ - unsigned long flags; - int s; - int rc=0; - struct mgsl_icount cprev, cnow; - int events; - int mask; - struct _input_signal_events oldsigs, newsigs; - DECLARE_WAITQUEUE(wait, current); - - if (get_user(mask, mask_ptr)) - return -EFAULT; - - DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask)); - - spin_lock_irqsave(&info->lock,flags); - - /* return immediately if state matches requested events */ - get_signals(info); - s = info->signals; - - events = mask & - ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + - ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + - ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + - ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); - if (events) { - spin_unlock_irqrestore(&info->lock,flags); - goto exit; - } - - /* save current irq counts */ - cprev = info->icount; - oldsigs = info->input_signal_events; - - /* enable hunt and idle irqs if needed */ - if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { - unsigned short val = rd_reg16(info, SCR); - if (!(val & IRQ_RXIDLE)) - wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE)); - } - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&info->event_wait_q, &wait); - - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - newsigs = info->input_signal_events; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (newsigs.dsr_up == oldsigs.dsr_up && - newsigs.dsr_down == oldsigs.dsr_down && - newsigs.dcd_up == oldsigs.dcd_up && - newsigs.dcd_down == oldsigs.dcd_down && - newsigs.cts_up == oldsigs.cts_up && - newsigs.cts_down == oldsigs.cts_down && - newsigs.ri_up == oldsigs.ri_up && - newsigs.ri_down == oldsigs.ri_down && - cnow.exithunt == cprev.exithunt && - cnow.rxidle == cprev.rxidle) { - rc = -EIO; - break; - } - - events = mask & - ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + - (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + - (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + - (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + - (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + - (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + - (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + - (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + - (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + - (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); - if (events) - break; - - cprev = cnow; - oldsigs = newsigs; - } - - remove_wait_queue(&info->event_wait_q, &wait); - set_current_state(TASK_RUNNING); - - - if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { - spin_lock_irqsave(&info->lock,flags); - if (!waitqueue_active(&info->event_wait_q)) { - /* disable enable exit hunt mode/idle rcvd IRQs */ - wr_reg16(info, SCR, - (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE)); - } - spin_unlock_irqrestore(&info->lock,flags); - } -exit: - if (rc == 0) - rc = put_user(events, mask_ptr); - return rc; -} - -static int get_interface(struct slgt_info *info, int __user *if_mode) -{ - DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode)); - if (put_user(info->if_mode, if_mode)) - return -EFAULT; - return 0; -} - -static int set_interface(struct slgt_info *info, int if_mode) -{ - unsigned long flags; - unsigned short val; - - DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode)); - spin_lock_irqsave(&info->lock,flags); - info->if_mode = if_mode; - - msc_set_vcr(info); - - /* TCR (tx control) 07 1=RTS driver control */ - val = rd_reg16(info, TCR); - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - else - val &= ~BIT7; - wr_reg16(info, TCR, val); - - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int get_xsync(struct slgt_info *info, int __user *xsync) -{ - DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); - if (put_user(info->xsync, xsync)) - return -EFAULT; - return 0; -} - -/* - * set extended sync pattern (1 to 4 bytes) for extended sync mode - * - * sync pattern is contained in least significant bytes of value - * most significant byte of sync pattern is oldest (1st sent/detected) - */ -static int set_xsync(struct slgt_info *info, int xsync) -{ - unsigned long flags; - - DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); - spin_lock_irqsave(&info->lock, flags); - info->xsync = xsync; - wr_reg32(info, XSR, xsync); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int get_xctrl(struct slgt_info *info, int __user *xctrl) -{ - DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); - if (put_user(info->xctrl, xctrl)) - return -EFAULT; - return 0; -} - -/* - * set extended control options - * - * xctrl[31:19] reserved, must be zero - * xctrl[18:17] extended sync pattern length in bytes - * 00 = 1 byte in xsr[7:0] - * 01 = 2 bytes in xsr[15:0] - * 10 = 3 bytes in xsr[23:0] - * 11 = 4 bytes in xsr[31:0] - * xctrl[16] 1 = enable terminal count, 0=disabled - * xctrl[15:0] receive terminal count for fixed length packets - * value is count minus one (0 = 1 byte packet) - * when terminal count is reached, receiver - * automatically returns to hunt mode and receive - * FIFO contents are flushed to DMA buffers with - * end of frame (EOF) status - */ -static int set_xctrl(struct slgt_info *info, int xctrl) -{ - unsigned long flags; - - DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); - spin_lock_irqsave(&info->lock, flags); - info->xctrl = xctrl; - wr_reg32(info, XCR, xctrl); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -/* - * set general purpose IO pin state and direction - * - * user_gpio fields: - * state each bit indicates a pin state - * smask set bit indicates pin state to set - * dir each bit indicates a pin direction (0=input, 1=output) - * dmask set bit indicates pin direction to set - */ -static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - unsigned long flags; - struct gpio_desc gpio; - __u32 data; - - if (!info->gpio_present) - return -EINVAL; - if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n", - info->device_name, gpio.state, gpio.smask, - gpio.dir, gpio.dmask)); - - spin_lock_irqsave(&info->port_array[0]->lock, flags); - if (gpio.dmask) { - data = rd_reg32(info, IODR); - data |= gpio.dmask & gpio.dir; - data &= ~(gpio.dmask & ~gpio.dir); - wr_reg32(info, IODR, data); - } - if (gpio.smask) { - data = rd_reg32(info, IOVR); - data |= gpio.smask & gpio.state; - data &= ~(gpio.smask & ~gpio.state); - wr_reg32(info, IOVR, data); - } - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - - return 0; -} - -/* - * get general purpose IO pin state and direction - */ -static int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - struct gpio_desc gpio; - if (!info->gpio_present) - return -EINVAL; - gpio.state = rd_reg32(info, IOVR); - gpio.smask = 0xffffffff; - gpio.dir = rd_reg32(info, IODR); - gpio.dmask = 0xffffffff; - if (copy_to_user(user_gpio, &gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s get_gpio state=%08x dir=%08x\n", - info->device_name, gpio.state, gpio.dir)); - return 0; -} - -/* - * conditional wait facility - */ -static void init_cond_wait(struct cond_wait *w, unsigned int data) -{ - init_waitqueue_head(&w->q); - init_waitqueue_entry(&w->wait, current); - w->data = data; -} - -static void add_cond_wait(struct cond_wait **head, struct cond_wait *w) -{ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&w->q, &w->wait); - w->next = *head; - *head = w; -} - -static void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw) -{ - struct cond_wait *w, *prev; - remove_wait_queue(&cw->q, &cw->wait); - set_current_state(TASK_RUNNING); - for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) { - if (w == cw) { - if (prev != NULL) - prev->next = w->next; - else - *head = w->next; - break; - } - } -} - -static void flush_cond_wait(struct cond_wait **head) -{ - while (*head != NULL) { - wake_up_interruptible(&(*head)->q); - *head = (*head)->next; - } -} - -/* - * wait for general purpose I/O pin(s) to enter specified state - * - * user_gpio fields: - * state - bit indicates target pin state - * smask - set bit indicates watched pin - * - * The wait ends when at least one watched pin enters the specified - * state. When 0 (no error) is returned, user_gpio->state is set to the - * state of all GPIO pins when the wait ends. - * - * Note: Each pin may be a dedicated input, dedicated output, or - * configurable input/output. The number and configuration of pins - * varies with the specific adapter model. Only input pins (dedicated - * or configured) can be monitored with this function. - */ -static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - unsigned long flags; - int rc = 0; - struct gpio_desc gpio; - struct cond_wait wait; - u32 state; - - if (!info->gpio_present) - return -EINVAL; - if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n", - info->device_name, gpio.state, gpio.smask)); - /* ignore output pins identified by set IODR bit */ - if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0) - return -EINVAL; - init_cond_wait(&wait, gpio.smask); - - spin_lock_irqsave(&info->port_array[0]->lock, flags); - /* enable interrupts for watched pins */ - wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); - /* get current pin states */ - state = rd_reg32(info, IOVR); - - if (gpio.smask & ~(state ^ gpio.state)) { - /* already in target state */ - gpio.state = state; - } else { - /* wait for target state */ - add_cond_wait(&info->gpio_wait_q, &wait); - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - schedule(); - if (signal_pending(current)) - rc = -ERESTARTSYS; - else - gpio.state = wait.data; - spin_lock_irqsave(&info->port_array[0]->lock, flags); - remove_cond_wait(&info->gpio_wait_q, &wait); - } - - /* disable all GPIO interrupts if no waiting processes */ - if (info->gpio_wait_q == NULL) - wr_reg32(info, IOER, 0); - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - - if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) - rc = -EFAULT; - return rc; -} - -static int modem_input_wait(struct slgt_info *info,int arg) -{ - unsigned long flags; - int rc; - struct mgsl_icount cprev, cnow; - DECLARE_WAITQUEUE(wait, current); - - /* save current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cprev = info->icount; - add_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get new irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; - break; - } - - /* check for change in caller specified modem input */ - if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || - (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || - (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || - (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { - rc = 0; - break; - } - - cprev = cnow; - } - remove_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_RUNNING); - return rc; -} - -/* - * return state of serial control and status signals - */ -static int tiocmget(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0); - - DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result)); - return result; -} - -/* - * set modem control signals (DTR/RTS) - * - * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit - * TIOCMSET = set/clear signal values - * value bit mask for command - */ -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear)); - - if (set & TIOCM_RTS) - info->signals |= SerialSignal_RTS; - if (set & TIOCM_DTR) - info->signals |= SerialSignal_DTR; - if (clear & TIOCM_RTS) - info->signals &= ~SerialSignal_RTS; - if (clear & TIOCM_DTR) - info->signals &= ~SerialSignal_DTR; - - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int carrier_raised(struct tty_port *port) -{ - unsigned long flags; - struct slgt_info *info = container_of(port, struct slgt_info, port); - - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - return (info->signals & SerialSignal_DCD) ? 1 : 0; -} - -static void dtr_rts(struct tty_port *port, int on) -{ - unsigned long flags; - struct slgt_info *info = container_of(port, struct slgt_info, port); - - spin_lock_irqsave(&info->lock,flags); - if (on) - info->signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); -} - - -/* - * block current process until the device is ready to open - */ -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct slgt_info *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - bool do_clocal = false; - bool extra_count = false; - unsigned long flags; - int cd; - struct tty_port *port = &info->port; - - DBGINFO(("%s block_til_ready\n", tty->driver->name)); - - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ - /* nonblock mode is set or port is not enabled */ - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = true; - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&info->lock, flags); - if (!tty_hung_up_p(filp)) { - extra_count = true; - port->count--; - } - spin_unlock_irqrestore(&info->lock, flags); - port->blocked_open++; - - while (1) { - if ((tty->termios->c_cflag & CBAUD)) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - cd = tty_port_carrier_raised(port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd )) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); - schedule(); - tty_lock(); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - if (extra_count) - port->count++; - port->blocked_open--; - - if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; - - DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); - return retval; -} - -static int alloc_tmp_rbuf(struct slgt_info *info) -{ - info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); - if (info->tmp_rbuf == NULL) - return -ENOMEM; - return 0; -} - -static void free_tmp_rbuf(struct slgt_info *info) -{ - kfree(info->tmp_rbuf); - info->tmp_rbuf = NULL; -} - -/* - * allocate DMA descriptor lists. - */ -static int alloc_desc(struct slgt_info *info) -{ - unsigned int i; - unsigned int pbufs; - - /* allocate memory to hold descriptor lists */ - info->bufs = pci_alloc_consistent(info->pdev, DESC_LIST_SIZE, &info->bufs_dma_addr); - if (info->bufs == NULL) - return -ENOMEM; - - memset(info->bufs, 0, DESC_LIST_SIZE); - - info->rbufs = (struct slgt_desc*)info->bufs; - info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count; - - pbufs = (unsigned int)info->bufs_dma_addr; - - /* - * Build circular lists of descriptors - */ - - for (i=0; i < info->rbuf_count; i++) { - /* physical address of this descriptor */ - info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc)); - - /* physical address of next descriptor */ - if (i == info->rbuf_count - 1) - info->rbufs[i].next = cpu_to_le32(pbufs); - else - info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc))); - set_desc_count(info->rbufs[i], DMABUFSIZE); - } - - for (i=0; i < info->tbuf_count; i++) { - /* physical address of this descriptor */ - info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc)); - - /* physical address of next descriptor */ - if (i == info->tbuf_count - 1) - info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc)); - else - info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc))); - } - - return 0; -} - -static void free_desc(struct slgt_info *info) -{ - if (info->bufs != NULL) { - pci_free_consistent(info->pdev, DESC_LIST_SIZE, info->bufs, info->bufs_dma_addr); - info->bufs = NULL; - info->rbufs = NULL; - info->tbufs = NULL; - } -} - -static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) -{ - int i; - for (i=0; i < count; i++) { - if ((bufs[i].buf = pci_alloc_consistent(info->pdev, DMABUFSIZE, &bufs[i].buf_dma_addr)) == NULL) - return -ENOMEM; - bufs[i].pbuf = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr); - } - return 0; -} - -static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) -{ - int i; - for (i=0; i < count; i++) { - if (bufs[i].buf == NULL) - continue; - pci_free_consistent(info->pdev, DMABUFSIZE, bufs[i].buf, bufs[i].buf_dma_addr); - bufs[i].buf = NULL; - } -} - -static int alloc_dma_bufs(struct slgt_info *info) -{ - info->rbuf_count = 32; - info->tbuf_count = 32; - - if (alloc_desc(info) < 0 || - alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 || - alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 || - alloc_tmp_rbuf(info) < 0) { - DBGERR(("%s DMA buffer alloc fail\n", info->device_name)); - return -ENOMEM; - } - reset_rbufs(info); - return 0; -} - -static void free_dma_bufs(struct slgt_info *info) -{ - if (info->bufs) { - free_bufs(info, info->rbufs, info->rbuf_count); - free_bufs(info, info->tbufs, info->tbuf_count); - free_desc(info); - } - free_tmp_rbuf(info); -} - -static int claim_resources(struct slgt_info *info) -{ - if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) { - DBGERR(("%s reg addr conflict, addr=%08X\n", - info->device_name, info->phys_reg_addr)); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->reg_addr_requested = true; - - info->reg_addr = ioremap_nocache(info->phys_reg_addr, SLGT_REG_SIZE); - if (!info->reg_addr) { - DBGERR(("%s cant map device registers, addr=%08X\n", - info->device_name, info->phys_reg_addr)); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - return 0; - -errout: - release_resources(info); - return -ENODEV; -} - -static void release_resources(struct slgt_info *info) -{ - if (info->irq_requested) { - free_irq(info->irq_level, info); - info->irq_requested = false; - } - - if (info->reg_addr_requested) { - release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE); - info->reg_addr_requested = false; - } - - if (info->reg_addr) { - iounmap(info->reg_addr); - info->reg_addr = NULL; - } -} - -/* Add the specified device instance data structure to the - * global linked list of devices and increment the device count. - */ -static void add_device(struct slgt_info *info) -{ - char *devstr; - - info->next_device = NULL; - info->line = slgt_device_count; - sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line); - - if (info->line < MAX_DEVICES) { - if (maxframe[info->line]) - info->max_frame_size = maxframe[info->line]; - } - - slgt_device_count++; - - if (!slgt_device_list) - slgt_device_list = info; - else { - struct slgt_info *current_dev = slgt_device_list; - while(current_dev->next_device) - current_dev = current_dev->next_device; - current_dev->next_device = info; - } - - if (info->max_frame_size < 4096) - info->max_frame_size = 4096; - else if (info->max_frame_size > 65535) - info->max_frame_size = 65535; - - switch(info->pdev->device) { - case SYNCLINK_GT_DEVICE_ID: - devstr = "GT"; - break; - case SYNCLINK_GT2_DEVICE_ID: - devstr = "GT2"; - break; - case SYNCLINK_GT4_DEVICE_ID: - devstr = "GT4"; - break; - case SYNCLINK_AC_DEVICE_ID: - devstr = "AC"; - info->params.mode = MGSL_MODE_ASYNC; - break; - default: - devstr = "(unknown model)"; - } - printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n", - devstr, info->device_name, info->phys_reg_addr, - info->irq_level, info->max_frame_size); - -#if SYNCLINK_GENERIC_HDLC - hdlcdev_init(info); -#endif -} - -static const struct tty_port_operations slgt_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* - * allocate device instance structure, return NULL on failure - */ -static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) -{ - struct slgt_info *info; - - info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL); - - if (!info) { - DBGERR(("%s device alloc failed adapter=%d port=%d\n", - driver_name, adapter_num, port_num)); - } else { - tty_port_init(&info->port); - info->port.ops = &slgt_port_ops; - info->magic = MGSL_MAGIC; - INIT_WORK(&info->task, bh_handler); - info->max_frame_size = 4096; - info->base_clock = 14745600; - info->rbuf_fill_level = DMABUFSIZE; - info->port.close_delay = 5*HZ/10; - info->port.closing_wait = 30*HZ; - init_waitqueue_head(&info->status_event_wait_q); - init_waitqueue_head(&info->event_wait_q); - spin_lock_init(&info->netlock); - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; - info->adapter_num = adapter_num; - info->port_num = port_num; - - setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info); - setup_timer(&info->rx_timer, rx_timeout, (unsigned long)info); - - /* Copy configuration info to device instance data */ - info->pdev = pdev; - info->irq_level = pdev->irq; - info->phys_reg_addr = pci_resource_start(pdev,0); - - info->bus_type = MGSL_BUS_TYPE_PCI; - info->irq_flags = IRQF_SHARED; - - info->init_error = -1; /* assume error, set to 0 on successful init */ - } - - return info; -} - -static void device_init(int adapter_num, struct pci_dev *pdev) -{ - struct slgt_info *port_array[SLGT_MAX_PORTS]; - int i; - int port_count = 1; - - if (pdev->device == SYNCLINK_GT2_DEVICE_ID) - port_count = 2; - else if (pdev->device == SYNCLINK_GT4_DEVICE_ID) - port_count = 4; - - /* allocate device instances for all ports */ - for (i=0; i < port_count; ++i) { - port_array[i] = alloc_dev(adapter_num, i, pdev); - if (port_array[i] == NULL) { - for (--i; i >= 0; --i) - kfree(port_array[i]); - return; - } - } - - /* give copy of port_array to all ports and add to device list */ - for (i=0; i < port_count; ++i) { - memcpy(port_array[i]->port_array, port_array, sizeof(port_array)); - add_device(port_array[i]); - port_array[i]->port_count = port_count; - spin_lock_init(&port_array[i]->lock); - } - - /* Allocate and claim adapter resources */ - if (!claim_resources(port_array[0])) { - - alloc_dma_bufs(port_array[0]); - - /* copy resource information from first port to others */ - for (i = 1; i < port_count; ++i) { - port_array[i]->irq_level = port_array[0]->irq_level; - port_array[i]->reg_addr = port_array[0]->reg_addr; - alloc_dma_bufs(port_array[i]); - } - - if (request_irq(port_array[0]->irq_level, - slgt_interrupt, - port_array[0]->irq_flags, - port_array[0]->device_name, - port_array[0]) < 0) { - DBGERR(("%s request_irq failed IRQ=%d\n", - port_array[0]->device_name, - port_array[0]->irq_level)); - } else { - port_array[0]->irq_requested = true; - adapter_test(port_array[0]); - for (i=1 ; i < port_count ; i++) { - port_array[i]->init_error = port_array[0]->init_error; - port_array[i]->gpio_present = port_array[0]->gpio_present; - } - } - } - - for (i=0; i < port_count; ++i) - tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev)); -} - -static int __devinit init_one(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - if (pci_enable_device(dev)) { - printk("error enabling pci device %p\n", dev); - return -EIO; - } - pci_set_master(dev); - device_init(slgt_device_count, dev); - return 0; -} - -static void __devexit remove_one(struct pci_dev *dev) -{ -} - -static const struct tty_operations ops = { - .open = open, - .close = close, - .write = write, - .put_char = put_char, - .flush_chars = flush_chars, - .write_room = write_room, - .chars_in_buffer = chars_in_buffer, - .flush_buffer = flush_buffer, - .ioctl = ioctl, - .compat_ioctl = slgt_compat_ioctl, - .throttle = throttle, - .unthrottle = unthrottle, - .send_xchar = send_xchar, - .break_ctl = set_break, - .wait_until_sent = wait_until_sent, - .set_termios = set_termios, - .stop = tx_hold, - .start = tx_release, - .hangup = hangup, - .tiocmget = tiocmget, - .tiocmset = tiocmset, - .get_icount = get_icount, - .proc_fops = &synclink_gt_proc_fops, -}; - -static void slgt_cleanup(void) -{ - int rc; - struct slgt_info *info; - struct slgt_info *tmp; - - printk(KERN_INFO "unload %s\n", driver_name); - - if (serial_driver) { - for (info=slgt_device_list ; info != NULL ; info=info->next_device) - tty_unregister_device(serial_driver, info->line); - if ((rc = tty_unregister_driver(serial_driver))) - DBGERR(("tty_unregister_driver error=%d\n", rc)); - put_tty_driver(serial_driver); - } - - /* reset devices */ - info = slgt_device_list; - while(info) { - reset_port(info); - info = info->next_device; - } - - /* release devices */ - info = slgt_device_list; - while(info) { -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - free_dma_bufs(info); - free_tmp_rbuf(info); - if (info->port_num == 0) - release_resources(info); - tmp = info; - info = info->next_device; - kfree(tmp); - } - - if (pci_registered) - pci_unregister_driver(&pci_driver); -} - -/* - * Driver initialization entry point. - */ -static int __init slgt_init(void) -{ - int rc; - - printk(KERN_INFO "%s\n", driver_name); - - serial_driver = alloc_tty_driver(MAX_DEVICES); - if (!serial_driver) { - printk("%s can't allocate tty driver\n", driver_name); - return -ENOMEM; - } - - /* Initialize the tty_driver structure */ - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = tty_driver_name; - serial_driver->name = tty_dev_prefix; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - 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->init_termios.c_ispeed = 9600; - serial_driver->init_termios.c_ospeed = 9600; - serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(serial_driver, &ops); - if ((rc = tty_register_driver(serial_driver)) < 0) { - DBGERR(("%s can't register serial driver\n", driver_name)); - put_tty_driver(serial_driver); - serial_driver = NULL; - goto error; - } - - printk(KERN_INFO "%s, tty major#%d\n", - driver_name, serial_driver->major); - - slgt_device_count = 0; - if ((rc = pci_register_driver(&pci_driver)) < 0) { - printk("%s pci_register_driver error=%d\n", driver_name, rc); - goto error; - } - pci_registered = true; - - if (!slgt_device_list) - printk("%s no devices found\n",driver_name); - - return 0; - -error: - slgt_cleanup(); - return rc; -} - -static void __exit slgt_exit(void) -{ - slgt_cleanup(); -} - -module_init(slgt_init); -module_exit(slgt_exit); - -/* - * register access routines - */ - -#define CALC_REGADDR() \ - unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \ - if (addr >= 0x80) \ - reg_addr += (info->port_num) * 32; \ - else if (addr >= 0x40) \ - reg_addr += (info->port_num) * 16; - -static __u8 rd_reg8(struct slgt_info *info, unsigned int addr) -{ - CALC_REGADDR(); - return readb((void __iomem *)reg_addr); -} - -static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value) -{ - CALC_REGADDR(); - writeb(value, (void __iomem *)reg_addr); -} - -static __u16 rd_reg16(struct slgt_info *info, unsigned int addr) -{ - CALC_REGADDR(); - return readw((void __iomem *)reg_addr); -} - -static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value) -{ - CALC_REGADDR(); - writew(value, (void __iomem *)reg_addr); -} - -static __u32 rd_reg32(struct slgt_info *info, unsigned int addr) -{ - CALC_REGADDR(); - return readl((void __iomem *)reg_addr); -} - -static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value) -{ - CALC_REGADDR(); - writel(value, (void __iomem *)reg_addr); -} - -static void rdma_reset(struct slgt_info *info) -{ - unsigned int i; - - /* set reset bit */ - wr_reg32(info, RDCSR, BIT1); - - /* wait for enable bit cleared */ - for(i=0 ; i < 1000 ; i++) - if (!(rd_reg32(info, RDCSR) & BIT0)) - break; -} - -static void tdma_reset(struct slgt_info *info) -{ - unsigned int i; - - /* set reset bit */ - wr_reg32(info, TDCSR, BIT1); - - /* wait for enable bit cleared */ - for(i=0 ; i < 1000 ; i++) - if (!(rd_reg32(info, TDCSR) & BIT0)) - break; -} - -/* - * enable internal loopback - * TxCLK and RxCLK are generated from BRG - * and TxD is looped back to RxD internally. - */ -static void enable_loopback(struct slgt_info *info) -{ - /* SCR (serial control) BIT2=looopback enable */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2)); - - if (info->params.mode != MGSL_MODE_ASYNC) { - /* CCR (clock control) - * 07..05 tx clock source (010 = BRG) - * 04..02 rx clock source (010 = BRG) - * 01 auxclk enable (0 = disable) - * 00 BRG enable (1 = enable) - * - * 0100 1001 - */ - wr_reg8(info, CCR, 0x49); - - /* set speed if available, otherwise use default */ - if (info->params.clock_speed) - set_rate(info, info->params.clock_speed); - else - set_rate(info, 3686400); - } -} - -/* - * set baud rate generator to specified rate - */ -static void set_rate(struct slgt_info *info, u32 rate) -{ - unsigned int div; - unsigned int osc = info->base_clock; - - /* div = osc/rate - 1 - * - * Round div up if osc/rate is not integer to - * force to next slowest rate. - */ - - if (rate) { - div = osc/rate; - if (!(osc % rate) && div) - div--; - wr_reg16(info, BDR, (unsigned short)div); - } -} - -static void rx_stop(struct slgt_info *info) -{ - unsigned short val; - - /* disable and reset receiver */ - val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, RCR, val); /* clear reset bit */ - - slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE); - - /* clear pending rx interrupts */ - wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER); - - rdma_reset(info); - - info->rx_enabled = false; - info->rx_restart = false; -} - -static void rx_start(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA); - - /* clear pending rx overrun IRQ */ - wr_reg16(info, SSR, IRQ_RXOVER); - - /* reset and disable receiver */ - val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, RCR, val); /* clear reset bit */ - - rdma_reset(info); - reset_rbufs(info); - - if (info->rx_pio) { - /* rx request when rx FIFO not empty */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14)); - slgt_irq_on(info, IRQ_RXDATA); - if (info->params.mode == MGSL_MODE_ASYNC) { - /* enable saving of rx status */ - wr_reg32(info, RDCSR, BIT6); - } - } else { - /* rx request when rx FIFO half full */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14)); - /* set 1st descriptor address */ - wr_reg32(info, RDDAR, info->rbufs[0].pdesc); - - if (info->params.mode != MGSL_MODE_ASYNC) { - /* enable rx DMA and DMA interrupt */ - wr_reg32(info, RDCSR, (BIT2 + BIT0)); - } else { - /* enable saving of rx status, rx DMA and DMA interrupt */ - wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0)); - } - } - - slgt_irq_on(info, IRQ_RXOVER); - - /* enable receiver */ - wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1)); - - info->rx_restart = false; - info->rx_enabled = true; -} - -static void tx_start(struct slgt_info *info) -{ - if (!info->tx_enabled) { - wr_reg16(info, TCR, - (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); - info->tx_enabled = true; - } - - if (desc_count(info->tbufs[info->tbuf_start])) { - info->drop_rts_on_tx_done = false; - - if (info->params.mode != MGSL_MODE_ASYNC) { - if (info->params.flags & HDLC_FLAG_AUTO_RTS) { - get_signals(info); - if (!(info->signals & SerialSignal_RTS)) { - info->signals |= SerialSignal_RTS; - set_signals(info); - info->drop_rts_on_tx_done = true; - } - } - - slgt_irq_off(info, IRQ_TXDATA); - slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); - /* clear tx idle and underrun status bits */ - wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); - } else { - slgt_irq_off(info, IRQ_TXDATA); - slgt_irq_on(info, IRQ_TXIDLE); - /* clear tx idle status bit */ - wr_reg16(info, SSR, IRQ_TXIDLE); - } - /* set 1st descriptor address and start DMA */ - wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); - wr_reg32(info, TDCSR, BIT2 + BIT0); - info->tx_active = true; - } -} - -static void tx_stop(struct slgt_info *info) -{ - unsigned short val; - - del_timer(&info->tx_timer); - - tdma_reset(info); - - /* reset and disable transmitter */ - val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ - - slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); - - /* clear tx idle and underrun status bit */ - wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); - - reset_tbufs(info); - - info->tx_enabled = false; - info->tx_active = false; -} - -static void reset_port(struct slgt_info *info) -{ - if (!info->reg_addr) - return; - - tx_stop(info); - rx_stop(info); - - info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); - set_signals(info); - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); -} - -static void reset_adapter(struct slgt_info *info) -{ - int i; - for (i=0; i < info->port_count; ++i) { - if (info->port_array[i]) - reset_port(info->port_array[i]); - } -} - -static void async_mode(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - tx_stop(info); - rx_stop(info); - - /* TCR (tx control) - * - * 15..13 mode, 010=async - * 12..10 encoding, 000=NRZ - * 09 parity enable - * 08 1=odd parity, 0=even parity - * 07 1=RTS driver control - * 06 1=break enable - * 05..04 character length - * 00=5 bits - * 01=6 bits - * 10=7 bits - * 11=8 bits - * 03 0=1 stop bit, 1=2 stop bits - * 02 reset - * 01 enable - * 00 auto-CTS enable - */ - val = 0x4000; - - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - - if (info->params.parity != ASYNC_PARITY_NONE) { - val |= BIT9; - if (info->params.parity == ASYNC_PARITY_ODD) - val |= BIT8; - } - - switch (info->params.data_bits) - { - case 6: val |= BIT4; break; - case 7: val |= BIT5; break; - case 8: val |= BIT5 + BIT4; break; - } - - if (info->params.stop_bits != 1) - val |= BIT3; - - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - val |= BIT0; - - wr_reg16(info, TCR, val); - - /* RCR (rx control) - * - * 15..13 mode, 010=async - * 12..10 encoding, 000=NRZ - * 09 parity enable - * 08 1=odd parity, 0=even parity - * 07..06 reserved, must be 0 - * 05..04 character length - * 00=5 bits - * 01=6 bits - * 10=7 bits - * 11=8 bits - * 03 reserved, must be zero - * 02 reset - * 01 enable - * 00 auto-DCD enable - */ - val = 0x4000; - - if (info->params.parity != ASYNC_PARITY_NONE) { - val |= BIT9; - if (info->params.parity == ASYNC_PARITY_ODD) - val |= BIT8; - } - - switch (info->params.data_bits) - { - case 6: val |= BIT4; break; - case 7: val |= BIT5; break; - case 8: val |= BIT5 + BIT4; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT0; - - wr_reg16(info, RCR, val); - - /* CCR (clock control) - * - * 07..05 011 = tx clock source is BRG/16 - * 04..02 010 = rx clock source is BRG - * 01 0 = auxclk disabled - * 00 1 = BRG enabled - * - * 0110 1001 - */ - wr_reg8(info, CCR, 0x69); - - msc_set_vcr(info); - - /* SCR (serial control) - * - * 15 1=tx req on FIFO half empty - * 14 1=rx req on FIFO half full - * 13 tx data IRQ enable - * 12 tx idle IRQ enable - * 11 rx break on IRQ enable - * 10 rx data IRQ enable - * 09 rx break off IRQ enable - * 08 overrun IRQ enable - * 07 DSR IRQ enable - * 06 CTS IRQ enable - * 05 DCD IRQ enable - * 04 RI IRQ enable - * 03 0=16x sampling, 1=8x sampling - * 02 1=txd->rxd internal loopback enable - * 01 reserved, must be zero - * 00 1=master IRQ enable - */ - val = BIT15 + BIT14 + BIT0; - /* JCR[8] : 1 = x8 async mode feature available */ - if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate && - ((info->base_clock < (info->params.data_rate * 16)) || - (info->base_clock % (info->params.data_rate * 16)))) { - /* use 8x sampling */ - val |= BIT3; - set_rate(info, info->params.data_rate * 8); - } else { - /* use 16x sampling */ - set_rate(info, info->params.data_rate * 16); - } - wr_reg16(info, SCR, val); - - slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER); - - if (info->params.loopback) - enable_loopback(info); -} - -static void sync_mode(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - tx_stop(info); - rx_stop(info); - - /* TCR (tx control) - * - * 15..13 mode - * 000=HDLC/SDLC - * 001=raw bit synchronous - * 010=asynchronous/isochronous - * 011=monosync byte synchronous - * 100=bisync byte synchronous - * 101=xsync byte synchronous - * 12..10 encoding - * 09 CRC enable - * 08 CRC32 - * 07 1=RTS driver control - * 06 preamble enable - * 05..04 preamble length - * 03 share open/close flag - * 02 reset - * 01 enable - * 00 auto-CTS enable - */ - val = BIT2; - - switch(info->params.mode) { - case MGSL_MODE_XSYNC: - val |= BIT15 + BIT13; - break; - case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; - case MGSL_MODE_BISYNC: val |= BIT15; break; - case MGSL_MODE_RAW: val |= BIT13; break; - } - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - - switch(info->params.encoding) - { - case HDLC_ENCODING_NRZB: val |= BIT10; break; - case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; - case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; - case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; - case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; - case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; - } - - switch (info->params.crc_type & HDLC_CRC_MASK) - { - case HDLC_CRC_16_CCITT: val |= BIT9; break; - case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; - } - - if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) - val |= BIT6; - - switch (info->params.preamble_length) - { - case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break; - case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break; - case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - val |= BIT0; - - wr_reg16(info, TCR, val); - - /* TPR (transmit preamble) */ - - switch (info->params.preamble) - { - case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; - case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; - case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break; - case HDLC_PREAMBLE_PATTERN_10: val = 0x55; break; - case HDLC_PREAMBLE_PATTERN_01: val = 0xaa; break; - default: val = 0x7e; break; - } - wr_reg8(info, TPR, (unsigned char)val); - - /* RCR (rx control) - * - * 15..13 mode - * 000=HDLC/SDLC - * 001=raw bit synchronous - * 010=asynchronous/isochronous - * 011=monosync byte synchronous - * 100=bisync byte synchronous - * 101=xsync byte synchronous - * 12..10 encoding - * 09 CRC enable - * 08 CRC32 - * 07..03 reserved, must be 0 - * 02 reset - * 01 enable - * 00 auto-DCD enable - */ - val = 0; - - switch(info->params.mode) { - case MGSL_MODE_XSYNC: - val |= BIT15 + BIT13; - break; - case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; - case MGSL_MODE_BISYNC: val |= BIT15; break; - case MGSL_MODE_RAW: val |= BIT13; break; - } - - switch(info->params.encoding) - { - case HDLC_ENCODING_NRZB: val |= BIT10; break; - case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; - case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; - case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; - case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; - case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; - } - - switch (info->params.crc_type & HDLC_CRC_MASK) - { - case HDLC_CRC_16_CCITT: val |= BIT9; break; - case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT0; - - wr_reg16(info, RCR, val); - - /* CCR (clock control) - * - * 07..05 tx clock source - * 04..02 rx clock source - * 01 auxclk enable - * 00 BRG enable - */ - val = 0; - - if (info->params.flags & HDLC_FLAG_TXC_BRG) - { - // when RxC source is DPLL, BRG generates 16X DPLL - // reference clock, so take TxC from BRG/16 to get - // transmit clock at actual data rate - if (info->params.flags & HDLC_FLAG_RXC_DPLL) - val |= BIT6 + BIT5; /* 011, txclk = BRG/16 */ - else - val |= BIT6; /* 010, txclk = BRG */ - } - else if (info->params.flags & HDLC_FLAG_TXC_DPLL) - val |= BIT7; /* 100, txclk = DPLL Input */ - else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN) - val |= BIT5; /* 001, txclk = RXC Input */ - - if (info->params.flags & HDLC_FLAG_RXC_BRG) - val |= BIT3; /* 010, rxclk = BRG */ - else if (info->params.flags & HDLC_FLAG_RXC_DPLL) - val |= BIT4; /* 100, rxclk = DPLL */ - else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN) - val |= BIT2; /* 001, rxclk = TXC Input */ - - if (info->params.clock_speed) - val |= BIT1 + BIT0; - - wr_reg8(info, CCR, (unsigned char)val); - - if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL)) - { - // program DPLL mode - switch(info->params.encoding) - { - case HDLC_ENCODING_BIPHASE_MARK: - case HDLC_ENCODING_BIPHASE_SPACE: - val = BIT7; break; - case HDLC_ENCODING_BIPHASE_LEVEL: - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: - val = BIT7 + BIT6; break; - default: val = BIT6; // NRZ encodings - } - wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val)); - - // DPLL requires a 16X reference clock from BRG - set_rate(info, info->params.clock_speed * 16); - } - else - set_rate(info, info->params.clock_speed); - - tx_set_idle(info); - - msc_set_vcr(info); - - /* SCR (serial control) - * - * 15 1=tx req on FIFO half empty - * 14 1=rx req on FIFO half full - * 13 tx data IRQ enable - * 12 tx idle IRQ enable - * 11 underrun IRQ enable - * 10 rx data IRQ enable - * 09 rx idle IRQ enable - * 08 overrun IRQ enable - * 07 DSR IRQ enable - * 06 CTS IRQ enable - * 05 DCD IRQ enable - * 04 RI IRQ enable - * 03 reserved, must be zero - * 02 1=txd->rxd internal loopback enable - * 01 reserved, must be zero - * 00 1=master IRQ enable - */ - wr_reg16(info, SCR, BIT15 + BIT14 + BIT0); - - if (info->params.loopback) - enable_loopback(info); -} - -/* - * set transmit idle mode - */ -static void tx_set_idle(struct slgt_info *info) -{ - unsigned char val; - unsigned short tcr; - - /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits - * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits - */ - tcr = rd_reg16(info, TCR); - if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) { - /* disable preamble, set idle size to 16 bits */ - tcr = (tcr & ~(BIT6 + BIT5)) | BIT4; - /* MSB of 16 bit idle specified in tx preamble register (TPR) */ - wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff)); - } else if (!(tcr & BIT6)) { - /* preamble is disabled, set idle size to 8 bits */ - tcr &= ~(BIT5 + BIT4); - } - wr_reg16(info, TCR, tcr); - - if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) { - /* LSB of custom tx idle specified in tx idle register */ - val = (unsigned char)(info->idle_mode & 0xff); - } else { - /* standard 8 bit idle patterns */ - switch(info->idle_mode) - { - case HDLC_TXIDLE_FLAGS: val = 0x7e; break; - case HDLC_TXIDLE_ALT_ZEROS_ONES: - case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break; - case HDLC_TXIDLE_ZEROS: - case HDLC_TXIDLE_SPACE: val = 0x00; break; - default: val = 0xff; - } - } - - wr_reg8(info, TIR, val); -} - -/* - * get state of V24 status (input) signals - */ -static void get_signals(struct slgt_info *info) -{ - unsigned short status = rd_reg16(info, SSR); - - /* clear all serial signals except DTR and RTS */ - info->signals &= SerialSignal_DTR + SerialSignal_RTS; - - if (status & BIT3) - info->signals |= SerialSignal_DSR; - if (status & BIT2) - info->signals |= SerialSignal_CTS; - if (status & BIT1) - info->signals |= SerialSignal_DCD; - if (status & BIT0) - info->signals |= SerialSignal_RI; -} - -/* - * set V.24 Control Register based on current configuration - */ -static void msc_set_vcr(struct slgt_info *info) -{ - unsigned char val = 0; - - /* VCR (V.24 control) - * - * 07..04 serial IF select - * 03 DTR - * 02 RTS - * 01 LL - * 00 RL - */ - - switch(info->if_mode & MGSL_INTERFACE_MASK) - { - case MGSL_INTERFACE_RS232: - val |= BIT5; /* 0010 */ - break; - case MGSL_INTERFACE_V35: - val |= BIT7 + BIT6 + BIT5; /* 1110 */ - break; - case MGSL_INTERFACE_RS422: - val |= BIT6; /* 0100 */ - break; - } - - if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) - val |= BIT4; - if (info->signals & SerialSignal_DTR) - val |= BIT3; - if (info->signals & SerialSignal_RTS) - val |= BIT2; - if (info->if_mode & MGSL_INTERFACE_LL) - val |= BIT1; - if (info->if_mode & MGSL_INTERFACE_RL) - val |= BIT0; - wr_reg8(info, VCR, val); -} - -/* - * set state of V24 control (output) signals - */ -static void set_signals(struct slgt_info *info) -{ - unsigned char val = rd_reg8(info, VCR); - if (info->signals & SerialSignal_DTR) - val |= BIT3; - else - val &= ~BIT3; - if (info->signals & SerialSignal_RTS) - val |= BIT2; - else - val &= ~BIT2; - wr_reg8(info, VCR, val); -} - -/* - * free range of receive DMA buffers (i to last) - */ -static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last) -{ - int done = 0; - - while(!done) { - /* reset current buffer for reuse */ - info->rbufs[i].status = 0; - set_desc_count(info->rbufs[i], info->rbuf_fill_level); - if (i == last) - done = 1; - if (++i == info->rbuf_count) - i = 0; - } - info->rbuf_current = i; -} - -/* - * mark all receive DMA buffers as free - */ -static void reset_rbufs(struct slgt_info *info) -{ - free_rbufs(info, 0, info->rbuf_count - 1); - info->rbuf_fill_index = 0; - info->rbuf_fill_count = 0; -} - -/* - * pass receive HDLC frame to upper layer - * - * return true if frame available, otherwise false - */ -static bool rx_get_frame(struct slgt_info *info) -{ - unsigned int start, end; - unsigned short status; - unsigned int framesize = 0; - unsigned long flags; - struct tty_struct *tty = info->port.tty; - unsigned char addr_field = 0xff; - unsigned int crc_size = 0; - - switch (info->params.crc_type & HDLC_CRC_MASK) { - case HDLC_CRC_16_CCITT: crc_size = 2; break; - case HDLC_CRC_32_CCITT: crc_size = 4; break; - } - -check_again: - - framesize = 0; - addr_field = 0xff; - start = end = info->rbuf_current; - - for (;;) { - if (!desc_complete(info->rbufs[end])) - goto cleanup; - - if (framesize == 0 && info->params.addr_filter != 0xff) - addr_field = info->rbufs[end].buf[0]; - - framesize += desc_count(info->rbufs[end]); - - if (desc_eof(info->rbufs[end])) - break; - - if (++end == info->rbuf_count) - end = 0; - - if (end == info->rbuf_current) { - if (info->rx_enabled){ - spin_lock_irqsave(&info->lock,flags); - rx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } - goto cleanup; - } - } - - /* status - * - * 15 buffer complete - * 14..06 reserved - * 05..04 residue - * 02 eof (end of frame) - * 01 CRC error - * 00 abort - */ - status = desc_status(info->rbufs[end]); - - /* ignore CRC bit if not using CRC (bit is undefined) */ - if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE) - status &= ~BIT1; - - if (framesize == 0 || - (addr_field != 0xff && addr_field != info->params.addr_filter)) { - free_rbufs(info, start, end); - goto check_again; - } - - if (framesize < (2 + crc_size) || status & BIT0) { - info->icount.rxshort++; - framesize = 0; - } else if (status & BIT1) { - info->icount.rxcrc++; - if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) - framesize = 0; - } - -#if SYNCLINK_GENERIC_HDLC - if (framesize == 0) { - info->netdev->stats.rx_errors++; - info->netdev->stats.rx_frame_errors++; - } -#endif - - DBGBH(("%s rx frame status=%04X size=%d\n", - info->device_name, status, framesize)); - DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); - - if (framesize) { - if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { - framesize -= crc_size; - crc_size = 0; - } - - if (framesize > info->max_frame_size + crc_size) - info->icount.rxlong++; - else { - /* copy dma buffer(s) to contiguous temp buffer */ - int copy_count = framesize; - int i = start; - unsigned char *p = info->tmp_rbuf; - info->tmp_rbuf_count = framesize; - - info->icount.rxok++; - - while(copy_count) { - int partial_count = min_t(int, copy_count, info->rbuf_fill_level); - memcpy(p, info->rbufs[i].buf, partial_count); - p += partial_count; - copy_count -= partial_count; - if (++i == info->rbuf_count) - i = 0; - } - - if (info->params.crc_type & HDLC_CRC_RETURN_EX) { - *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK; - framesize++; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_rx(info,info->tmp_rbuf, framesize); - else -#endif - ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize); - } - } - free_rbufs(info, start, end); - return true; - -cleanup: - return false; -} - -/* - * pass receive buffer (RAW synchronous mode) to tty layer - * return true if buffer available, otherwise false - */ -static bool rx_get_buf(struct slgt_info *info) -{ - unsigned int i = info->rbuf_current; - unsigned int count; - - if (!desc_complete(info->rbufs[i])) - return false; - count = desc_count(info->rbufs[i]); - switch(info->params.mode) { - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - case MGSL_MODE_XSYNC: - /* ignore residue in byte synchronous modes */ - if (desc_residue(info->rbufs[i])) - count--; - break; - } - DBGDATA(info, info->rbufs[i].buf, count, "rx"); - DBGINFO(("rx_get_buf size=%d\n", count)); - if (count) - ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, - info->flag_buf, count); - free_rbufs(info, i, i); - return true; -} - -static void reset_tbufs(struct slgt_info *info) -{ - unsigned int i; - info->tbuf_current = 0; - for (i=0 ; i < info->tbuf_count ; i++) { - info->tbufs[i].status = 0; - info->tbufs[i].count = 0; - } -} - -/* - * return number of free transmit DMA buffers - */ -static unsigned int free_tbuf_count(struct slgt_info *info) -{ - unsigned int count = 0; - unsigned int i = info->tbuf_current; - - do - { - if (desc_count(info->tbufs[i])) - break; /* buffer in use */ - ++count; - if (++i == info->tbuf_count) - i=0; - } while (i != info->tbuf_current); - - /* if tx DMA active, last zero count buffer is in use */ - if (count && (rd_reg32(info, TDCSR) & BIT0)) - --count; - - return count; -} - -/* - * return number of bytes in unsent transmit DMA buffers - * and the serial controller tx FIFO - */ -static unsigned int tbuf_bytes(struct slgt_info *info) -{ - unsigned int total_count = 0; - unsigned int i = info->tbuf_current; - unsigned int reg_value; - unsigned int count; - unsigned int active_buf_count = 0; - - /* - * Add descriptor counts for all tx DMA buffers. - * If count is zero (cleared by DMA controller after read), - * the buffer is complete or is actively being read from. - * - * Record buf_count of last buffer with zero count starting - * from current ring position. buf_count is mirror - * copy of count and is not cleared by serial controller. - * If DMA controller is active, that buffer is actively - * being read so add to total. - */ - do { - count = desc_count(info->tbufs[i]); - if (count) - total_count += count; - else if (!total_count) - active_buf_count = info->tbufs[i].buf_count; - if (++i == info->tbuf_count) - i = 0; - } while (i != info->tbuf_current); - - /* read tx DMA status register */ - reg_value = rd_reg32(info, TDCSR); - - /* if tx DMA active, last zero count buffer is in use */ - if (reg_value & BIT0) - total_count += active_buf_count; - - /* add tx FIFO count = reg_value[15..8] */ - total_count += (reg_value >> 8) & 0xff; - - /* if transmitter active add one byte for shift register */ - if (info->tx_active) - total_count++; - - return total_count; -} - -/* - * load data into transmit DMA buffer ring and start transmitter if needed - * return true if data accepted, otherwise false (buffers full) - */ -static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size) -{ - unsigned short count; - unsigned int i; - struct slgt_desc *d; - - /* check required buffer space */ - if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) - return false; - - DBGDATA(info, buf, size, "tx"); - - /* - * copy data to one or more DMA buffers in circular ring - * tbuf_start = first buffer for this data - * tbuf_current = next free buffer - * - * Copy all data before making data visible to DMA controller by - * setting descriptor count of the first buffer. - * This prevents an active DMA controller from reading the first DMA - * buffers of a frame and stopping before the final buffers are filled. - */ - - info->tbuf_start = i = info->tbuf_current; - - while (size) { - d = &info->tbufs[i]; - - count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); - memcpy(d->buf, buf, count); - - size -= count; - buf += count; - - /* - * set EOF bit for last buffer of HDLC frame or - * for every buffer in raw mode - */ - if ((!size && info->params.mode == MGSL_MODE_HDLC) || - info->params.mode == MGSL_MODE_RAW) - set_desc_eof(*d, 1); - else - set_desc_eof(*d, 0); - - /* set descriptor count for all but first buffer */ - if (i != info->tbuf_start) - set_desc_count(*d, count); - d->buf_count = count; - - if (++i == info->tbuf_count) - i = 0; - } - - info->tbuf_current = i; - - /* set first buffer count to make new data visible to DMA controller */ - d = &info->tbufs[info->tbuf_start]; - set_desc_count(*d, d->buf_count); - - /* start transmitter if needed and update transmit timeout */ - if (!info->tx_active) - tx_start(info); - update_tx_timer(info); - - return true; -} - -static int register_test(struct slgt_info *info) -{ - static unsigned short patterns[] = - {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696}; - static unsigned int count = ARRAY_SIZE(patterns); - unsigned int i; - int rc = 0; - - for (i=0 ; i < count ; i++) { - wr_reg16(info, TIR, patterns[i]); - wr_reg16(info, BDR, patterns[(i+1)%count]); - if ((rd_reg16(info, TIR) != patterns[i]) || - (rd_reg16(info, BDR) != patterns[(i+1)%count])) { - rc = -ENODEV; - break; - } - } - info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0; - info->init_error = rc ? 0 : DiagStatus_AddressFailure; - return rc; -} - -static int irq_test(struct slgt_info *info) -{ - unsigned long timeout; - unsigned long flags; - struct tty_struct *oldtty = info->port.tty; - u32 speed = info->params.data_rate; - - info->params.data_rate = 921600; - info->port.tty = NULL; - - spin_lock_irqsave(&info->lock, flags); - async_mode(info); - slgt_irq_on(info, IRQ_TXIDLE); - - /* enable transmitter */ - wr_reg16(info, TCR, - (unsigned short)(rd_reg16(info, TCR) | BIT1)); - - /* write one byte and wait for tx idle */ - wr_reg16(info, TDR, 0); - - /* assume failure */ - info->init_error = DiagStatus_IrqFailure; - info->irq_occurred = false; - - spin_unlock_irqrestore(&info->lock, flags); - - timeout=100; - while(timeout-- && !info->irq_occurred) - msleep_interruptible(10); - - spin_lock_irqsave(&info->lock,flags); - reset_port(info); - spin_unlock_irqrestore(&info->lock,flags); - - info->params.data_rate = speed; - info->port.tty = oldtty; - - info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure; - return info->irq_occurred ? 0 : -ENODEV; -} - -static int loopback_test_rx(struct slgt_info *info) -{ - unsigned char *src, *dest; - int count; - - if (desc_complete(info->rbufs[0])) { - count = desc_count(info->rbufs[0]); - src = info->rbufs[0].buf; - dest = info->tmp_rbuf; - - for( ; count ; count-=2, src+=2) { - /* src=data byte (src+1)=status byte */ - if (!(*(src+1) & (BIT9 + BIT8))) { - *dest = *src; - dest++; - info->tmp_rbuf_count++; - } - } - DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx"); - return 1; - } - return 0; -} - -static int loopback_test(struct slgt_info *info) -{ -#define TESTFRAMESIZE 20 - - unsigned long timeout; - u16 count = TESTFRAMESIZE; - unsigned char buf[TESTFRAMESIZE]; - int rc = -ENODEV; - unsigned long flags; - - struct tty_struct *oldtty = info->port.tty; - MGSL_PARAMS params; - - memcpy(¶ms, &info->params, sizeof(params)); - - info->params.mode = MGSL_MODE_ASYNC; - info->params.data_rate = 921600; - info->params.loopback = 1; - info->port.tty = NULL; - - /* build and send transmit frame */ - for (count = 0; count < TESTFRAMESIZE; ++count) - buf[count] = (unsigned char)count; - - info->tmp_rbuf_count = 0; - memset(info->tmp_rbuf, 0, TESTFRAMESIZE); - - /* program hardware for HDLC and enabled receiver */ - spin_lock_irqsave(&info->lock,flags); - async_mode(info); - rx_start(info); - tx_load(info, buf, count); - spin_unlock_irqrestore(&info->lock, flags); - - /* wait for receive complete */ - for (timeout = 100; timeout; --timeout) { - msleep_interruptible(10); - if (loopback_test_rx(info)) { - rc = 0; - break; - } - } - - /* verify received frame length and contents */ - if (!rc && (info->tmp_rbuf_count != count || - memcmp(buf, info->tmp_rbuf, count))) { - rc = -ENODEV; - } - - spin_lock_irqsave(&info->lock,flags); - reset_adapter(info); - spin_unlock_irqrestore(&info->lock,flags); - - memcpy(&info->params, ¶ms, sizeof(info->params)); - info->port.tty = oldtty; - - info->init_error = rc ? DiagStatus_DmaFailure : 0; - return rc; -} - -static int adapter_test(struct slgt_info *info) -{ - DBGINFO(("testing %s\n", info->device_name)); - if (register_test(info) < 0) { - printk("register test failure %s addr=%08X\n", - info->device_name, info->phys_reg_addr); - } else if (irq_test(info) < 0) { - printk("IRQ test failure %s IRQ=%d\n", - info->device_name, info->irq_level); - } else if (loopback_test(info) < 0) { - printk("loopback test failure %s\n", info->device_name); - } - return info->init_error; -} - -/* - * transmit timeout handler - */ -static void tx_timeout(unsigned long context) -{ - struct slgt_info *info = (struct slgt_info*)context; - unsigned long flags; - - DBGINFO(("%s tx_timeout\n", info->device_name)); - if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { - info->icount.txtimeout++; - } - spin_lock_irqsave(&info->lock,flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - bh_transmit(info); -} - -/* - * receive buffer polling timer - */ -static void rx_timeout(unsigned long context) -{ - struct slgt_info *info = (struct slgt_info*)context; - unsigned long flags; - - DBGINFO(("%s rx_timeout\n", info->device_name)); - spin_lock_irqsave(&info->lock, flags); - info->pending_bh |= BH_RECEIVE; - spin_unlock_irqrestore(&info->lock, flags); - bh_handler(&info->task); -} - diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c deleted file mode 100644 index 327343694473..000000000000 --- a/drivers/char/synclinkmp.c +++ /dev/null @@ -1,5600 +0,0 @@ -/* - * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $ - * - * Device driver for Microgate SyncLink Multiport - * high speed multiprotocol serial adapter. - * - * written by Paul Fulghum for Microgate Corporation - * paulkf@microgate.com - * - * Microgate and SyncLink are trademarks of Microgate Corporation - * - * Derived from serial.c written by Theodore Ts'o and Linus Torvalds - * This code is released under the GNU General Public License (GPL) - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) -#if defined(__i386__) -# define BREAKPOINT() asm(" int $3"); -#else -# define BREAKPOINT() { } -#endif - -#define MAX_DEVICES 12 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE)) -#define SYNCLINK_GENERIC_HDLC 1 -#else -#define SYNCLINK_GENERIC_HDLC 0 -#endif - -#define GET_USER(error,value,addr) error = get_user(value,addr) -#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 -#define PUT_USER(error,value,addr) error = put_user(value,addr) -#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 - -#include - -static MGSL_PARAMS default_params = { - MGSL_MODE_HDLC, /* unsigned long mode */ - 0, /* unsigned char loopback; */ - HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ - HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ - 0, /* unsigned long clock_speed; */ - 0xff, /* unsigned char addr_filter; */ - HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ - HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ - HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ - 9600, /* unsigned long data_rate; */ - 8, /* unsigned char data_bits; */ - 1, /* unsigned char stop_bits; */ - ASYNC_PARITY_NONE /* unsigned char parity; */ -}; - -/* size in bytes of DMA data buffers */ -#define SCABUFSIZE 1024 -#define SCA_MEM_SIZE 0x40000 -#define SCA_BASE_SIZE 512 -#define SCA_REG_SIZE 16 -#define SCA_MAX_PORTS 4 -#define SCAMAXDESC 128 - -#define BUFFERLISTSIZE 4096 - -/* SCA-I style DMA buffer descriptor */ -typedef struct _SCADESC -{ - u16 next; /* lower l6 bits of next descriptor addr */ - u16 buf_ptr; /* lower 16 bits of buffer addr */ - u8 buf_base; /* upper 8 bits of buffer addr */ - u8 pad1; - u16 length; /* length of buffer */ - u8 status; /* status of buffer */ - u8 pad2; -} SCADESC, *PSCADESC; - -typedef struct _SCADESC_EX -{ - /* device driver bookkeeping section */ - char *virt_addr; /* virtual address of data buffer */ - u16 phys_entry; /* lower 16-bits of physical address of this descriptor */ -} SCADESC_EX, *PSCADESC_EX; - -/* The queue of BH actions to be performed */ - -#define BH_RECEIVE 1 -#define BH_TRANSMIT 2 -#define BH_STATUS 4 - -#define IO_PIN_SHUTDOWN_LIMIT 100 - -struct _input_signal_events { - int ri_up; - int ri_down; - int dsr_up; - int dsr_down; - int dcd_up; - int dcd_down; - int cts_up; - int cts_down; -}; - -/* - * Device instance data structure - */ -typedef struct _synclinkmp_info { - void *if_ptr; /* General purpose pointer (used by SPPP) */ - int magic; - struct tty_port port; - int line; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ - - struct mgsl_icount icount; - - int timeout; - int x_char; /* xon/xoff character */ - u16 read_status_mask1; /* break detection (SR1 indications) */ - u16 read_status_mask2; /* parity/framing/overun (SR2 indications) */ - unsigned char ignore_status_mask1; /* break detection (SR1 indications) */ - unsigned char ignore_status_mask2; /* parity/framing/overun (SR2 indications) */ - unsigned char *tx_buf; - int tx_put; - int tx_get; - int tx_count; - - wait_queue_head_t status_event_wait_q; - wait_queue_head_t event_wait_q; - struct timer_list tx_timer; /* HDLC transmit timeout timer */ - struct _synclinkmp_info *next_device; /* device list link */ - struct timer_list status_timer; /* input signal status check timer */ - - spinlock_t lock; /* spinlock for synchronizing with ISR */ - struct work_struct task; /* task structure for scheduling bh */ - - u32 max_frame_size; /* as set by device config */ - - u32 pending_bh; - - bool bh_running; /* Protection from multiple */ - int isr_overflow; - bool bh_requested; - - int dcd_chkcount; /* check counts to prevent */ - int cts_chkcount; /* too many IRQs if a signal */ - int dsr_chkcount; /* is floating */ - int ri_chkcount; - - char *buffer_list; /* virtual address of Rx & Tx buffer lists */ - unsigned long buffer_list_phys; - - unsigned int rx_buf_count; /* count of total allocated Rx buffers */ - SCADESC *rx_buf_list; /* list of receive buffer entries */ - SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */ - unsigned int current_rx_buf; - - unsigned int tx_buf_count; /* count of total allocated Tx buffers */ - SCADESC *tx_buf_list; /* list of transmit buffer entries */ - SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */ - unsigned int last_tx_buf; - - unsigned char *tmp_rx_buf; - unsigned int tmp_rx_buf_count; - - bool rx_enabled; - bool rx_overflow; - - bool tx_enabled; - bool tx_active; - u32 idle_mode; - - unsigned char ie0_value; - unsigned char ie1_value; - unsigned char ie2_value; - unsigned char ctrlreg_value; - unsigned char old_signals; - - char device_name[25]; /* device instance name */ - - int port_count; - int adapter_num; - int port_num; - - struct _synclinkmp_info *port_array[SCA_MAX_PORTS]; - - unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ - - unsigned int irq_level; /* interrupt level */ - unsigned long irq_flags; - bool irq_requested; /* true if IRQ requested */ - - MGSL_PARAMS params; /* communications parameters */ - - unsigned char serial_signals; /* current serial signal states */ - - bool irq_occurred; /* for diagnostics use */ - unsigned int init_error; /* Initialization startup error */ - - u32 last_mem_alloc; - unsigned char* memory_base; /* shared memory address (PCI only) */ - u32 phys_memory_base; - int shared_mem_requested; - - unsigned char* sca_base; /* HD64570 SCA Memory address */ - u32 phys_sca_base; - u32 sca_offset; - bool sca_base_requested; - - unsigned char* lcr_base; /* local config registers (PCI only) */ - u32 phys_lcr_base; - u32 lcr_offset; - int lcr_mem_requested; - - unsigned char* statctrl_base; /* status/control register memory */ - u32 phys_statctrl_base; - u32 statctrl_offset; - bool sca_statctrl_requested; - - u32 misc_ctrl_value; - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; - bool drop_rts_on_tx_done; - - struct _input_signal_events input_signal_events; - - /* SPPP/Cisco HDLC device parts */ - int netcount; - spinlock_t netlock; - -#if SYNCLINK_GENERIC_HDLC - struct net_device *netdev; -#endif - -} SLMP_INFO; - -#define MGSL_MAGIC 0x5401 - -/* - * define serial signal status change macros - */ -#define MISCSTATUS_DCD_LATCHED (SerialSignal_DCD<<8) /* indicates change in DCD */ -#define MISCSTATUS_RI_LATCHED (SerialSignal_RI<<8) /* indicates change in RI */ -#define MISCSTATUS_CTS_LATCHED (SerialSignal_CTS<<8) /* indicates change in CTS */ -#define MISCSTATUS_DSR_LATCHED (SerialSignal_DSR<<8) /* change in DSR */ - -/* Common Register macros */ -#define LPR 0x00 -#define PABR0 0x02 -#define PABR1 0x03 -#define WCRL 0x04 -#define WCRM 0x05 -#define WCRH 0x06 -#define DPCR 0x08 -#define DMER 0x09 -#define ISR0 0x10 -#define ISR1 0x11 -#define ISR2 0x12 -#define IER0 0x14 -#define IER1 0x15 -#define IER2 0x16 -#define ITCR 0x18 -#define INTVR 0x1a -#define IMVR 0x1c - -/* MSCI Register macros */ -#define TRB 0x20 -#define TRBL 0x20 -#define TRBH 0x21 -#define SR0 0x22 -#define SR1 0x23 -#define SR2 0x24 -#define SR3 0x25 -#define FST 0x26 -#define IE0 0x28 -#define IE1 0x29 -#define IE2 0x2a -#define FIE 0x2b -#define CMD 0x2c -#define MD0 0x2e -#define MD1 0x2f -#define MD2 0x30 -#define CTL 0x31 -#define SA0 0x32 -#define SA1 0x33 -#define IDL 0x34 -#define TMC 0x35 -#define RXS 0x36 -#define TXS 0x37 -#define TRC0 0x38 -#define TRC1 0x39 -#define RRC 0x3a -#define CST0 0x3c -#define CST1 0x3d - -/* Timer Register Macros */ -#define TCNT 0x60 -#define TCNTL 0x60 -#define TCNTH 0x61 -#define TCONR 0x62 -#define TCONRL 0x62 -#define TCONRH 0x63 -#define TMCS 0x64 -#define TEPR 0x65 - -/* DMA Controller Register macros */ -#define DARL 0x80 -#define DARH 0x81 -#define DARB 0x82 -#define BAR 0x80 -#define BARL 0x80 -#define BARH 0x81 -#define BARB 0x82 -#define SAR 0x84 -#define SARL 0x84 -#define SARH 0x85 -#define SARB 0x86 -#define CPB 0x86 -#define CDA 0x88 -#define CDAL 0x88 -#define CDAH 0x89 -#define EDA 0x8a -#define EDAL 0x8a -#define EDAH 0x8b -#define BFL 0x8c -#define BFLL 0x8c -#define BFLH 0x8d -#define BCR 0x8e -#define BCRL 0x8e -#define BCRH 0x8f -#define DSR 0x90 -#define DMR 0x91 -#define FCT 0x93 -#define DIR 0x94 -#define DCMD 0x95 - -/* combine with timer or DMA register address */ -#define TIMER0 0x00 -#define TIMER1 0x08 -#define TIMER2 0x10 -#define TIMER3 0x18 -#define RXDMA 0x00 -#define TXDMA 0x20 - -/* SCA Command Codes */ -#define NOOP 0x00 -#define TXRESET 0x01 -#define TXENABLE 0x02 -#define TXDISABLE 0x03 -#define TXCRCINIT 0x04 -#define TXCRCEXCL 0x05 -#define TXEOM 0x06 -#define TXABORT 0x07 -#define MPON 0x08 -#define TXBUFCLR 0x09 -#define RXRESET 0x11 -#define RXENABLE 0x12 -#define RXDISABLE 0x13 -#define RXCRCINIT 0x14 -#define RXREJECT 0x15 -#define SEARCHMP 0x16 -#define RXCRCEXCL 0x17 -#define RXCRCCALC 0x18 -#define CHRESET 0x21 -#define HUNT 0x31 - -/* DMA command codes */ -#define SWABORT 0x01 -#define FEICLEAR 0x02 - -/* IE0 */ -#define TXINTE BIT7 -#define RXINTE BIT6 -#define TXRDYE BIT1 -#define RXRDYE BIT0 - -/* IE1 & SR1 */ -#define UDRN BIT7 -#define IDLE BIT6 -#define SYNCD BIT4 -#define FLGD BIT4 -#define CCTS BIT3 -#define CDCD BIT2 -#define BRKD BIT1 -#define ABTD BIT1 -#define GAPD BIT1 -#define BRKE BIT0 -#define IDLD BIT0 - -/* IE2 & SR2 */ -#define EOM BIT7 -#define PMP BIT6 -#define SHRT BIT6 -#define PE BIT5 -#define ABT BIT5 -#define FRME BIT4 -#define RBIT BIT4 -#define OVRN BIT3 -#define CRCE BIT2 - - -/* - * Global linked list of SyncLink devices - */ -static SLMP_INFO *synclinkmp_device_list = NULL; -static int synclinkmp_adapter_count = -1; -static int synclinkmp_device_count = 0; - -/* - * Set this param to non-zero to load eax with the - * .text section address and breakpoint on module load. - * This is useful for use with gdb and add-symbol-file command. - */ -static int break_on_load = 0; - -/* - * Driver major number, defaults to zero to get auto - * assigned major number. May be forced as module parameter. - */ -static int ttymajor = 0; - -/* - * Array of user specified options for ISA adapters. - */ -static int debug_level = 0; -static int maxframe[MAX_DEVICES] = {0,}; - -module_param(break_on_load, bool, 0); -module_param(ttymajor, int, 0); -module_param(debug_level, int, 0); -module_param_array(maxframe, int, NULL, 0); - -static char *driver_name = "SyncLink MultiPort driver"; -static char *driver_version = "$Revision: 4.38 $"; - -static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent); -static void synclinkmp_remove_one(struct pci_dev *dev); - -static struct pci_device_id synclinkmp_pci_tbl[] = { - { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl); - -MODULE_LICENSE("GPL"); - -static struct pci_driver synclinkmp_pci_driver = { - .name = "synclinkmp", - .id_table = synclinkmp_pci_tbl, - .probe = synclinkmp_init_one, - .remove = __devexit_p(synclinkmp_remove_one), -}; - - -static struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - - -/* tty callbacks */ - -static int open(struct tty_struct *tty, struct file * filp); -static void close(struct tty_struct *tty, struct file * filp); -static void hangup(struct tty_struct *tty); -static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); - -static int write(struct tty_struct *tty, const unsigned char *buf, int count); -static int put_char(struct tty_struct *tty, unsigned char ch); -static void send_xchar(struct tty_struct *tty, char ch); -static void wait_until_sent(struct tty_struct *tty, int timeout); -static int write_room(struct tty_struct *tty); -static void flush_chars(struct tty_struct *tty); -static void flush_buffer(struct tty_struct *tty); -static void tx_hold(struct tty_struct *tty); -static void tx_release(struct tty_struct *tty); - -static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int chars_in_buffer(struct tty_struct *tty); -static void throttle(struct tty_struct * tty); -static void unthrottle(struct tty_struct * tty); -static int set_break(struct tty_struct *tty, int break_state); - -#if SYNCLINK_GENERIC_HDLC -#define dev_to_port(D) (dev_to_hdlc(D)->priv) -static void hdlcdev_tx_done(SLMP_INFO *info); -static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size); -static int hdlcdev_init(SLMP_INFO *info); -static void hdlcdev_exit(SLMP_INFO *info); -#endif - -/* ioctl handlers */ - -static int get_stats(SLMP_INFO *info, struct mgsl_icount __user *user_icount); -static int get_params(SLMP_INFO *info, MGSL_PARAMS __user *params); -static int set_params(SLMP_INFO *info, MGSL_PARAMS __user *params); -static int get_txidle(SLMP_INFO *info, int __user *idle_mode); -static int set_txidle(SLMP_INFO *info, int idle_mode); -static int tx_enable(SLMP_INFO *info, int enable); -static int tx_abort(SLMP_INFO *info); -static int rx_enable(SLMP_INFO *info, int enable); -static int modem_input_wait(SLMP_INFO *info,int arg); -static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); -static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int set_break(struct tty_struct *tty, int break_state); - -static void add_device(SLMP_INFO *info); -static void device_init(int adapter_num, struct pci_dev *pdev); -static int claim_resources(SLMP_INFO *info); -static void release_resources(SLMP_INFO *info); - -static int startup(SLMP_INFO *info); -static int block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info); -static int carrier_raised(struct tty_port *port); -static void shutdown(SLMP_INFO *info); -static void program_hw(SLMP_INFO *info); -static void change_params(SLMP_INFO *info); - -static bool init_adapter(SLMP_INFO *info); -static bool register_test(SLMP_INFO *info); -static bool irq_test(SLMP_INFO *info); -static bool loopback_test(SLMP_INFO *info); -static int adapter_test(SLMP_INFO *info); -static bool memory_test(SLMP_INFO *info); - -static void reset_adapter(SLMP_INFO *info); -static void reset_port(SLMP_INFO *info); -static void async_mode(SLMP_INFO *info); -static void hdlc_mode(SLMP_INFO *info); - -static void rx_stop(SLMP_INFO *info); -static void rx_start(SLMP_INFO *info); -static void rx_reset_buffers(SLMP_INFO *info); -static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last); -static bool rx_get_frame(SLMP_INFO *info); - -static void tx_start(SLMP_INFO *info); -static void tx_stop(SLMP_INFO *info); -static void tx_load_fifo(SLMP_INFO *info); -static void tx_set_idle(SLMP_INFO *info); -static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count); - -static void get_signals(SLMP_INFO *info); -static void set_signals(SLMP_INFO *info); -static void enable_loopback(SLMP_INFO *info, int enable); -static void set_rate(SLMP_INFO *info, u32 data_rate); - -static int bh_action(SLMP_INFO *info); -static void bh_handler(struct work_struct *work); -static void bh_receive(SLMP_INFO *info); -static void bh_transmit(SLMP_INFO *info); -static void bh_status(SLMP_INFO *info); -static void isr_timer(SLMP_INFO *info); -static void isr_rxint(SLMP_INFO *info); -static void isr_rxrdy(SLMP_INFO *info); -static void isr_txint(SLMP_INFO *info); -static void isr_txrdy(SLMP_INFO *info); -static void isr_rxdmaok(SLMP_INFO *info); -static void isr_rxdmaerror(SLMP_INFO *info); -static void isr_txdmaok(SLMP_INFO *info); -static void isr_txdmaerror(SLMP_INFO *info); -static void isr_io_pin(SLMP_INFO *info, u16 status); - -static int alloc_dma_bufs(SLMP_INFO *info); -static void free_dma_bufs(SLMP_INFO *info); -static int alloc_buf_list(SLMP_INFO *info); -static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count); -static int alloc_tmp_rx_buf(SLMP_INFO *info); -static void free_tmp_rx_buf(SLMP_INFO *info); - -static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count); -static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit); -static void tx_timeout(unsigned long context); -static void status_timeout(unsigned long context); - -static unsigned char read_reg(SLMP_INFO *info, unsigned char addr); -static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val); -static u16 read_reg16(SLMP_INFO *info, unsigned char addr); -static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val); -static unsigned char read_status_reg(SLMP_INFO * info); -static void write_control_reg(SLMP_INFO * info); - - -static unsigned char rx_active_fifo_level = 16; // rx request FIFO activation level in bytes -static unsigned char tx_active_fifo_level = 16; // tx request FIFO activation level in bytes -static unsigned char tx_negate_fifo_level = 32; // tx request FIFO negation level in bytes - -static u32 misc_ctrl_value = 0x007e4040; -static u32 lcr1_brdr_value = 0x00800028; - -static u32 read_ahead_count = 8; - -/* DPCR, DMA Priority Control - * - * 07..05 Not used, must be 0 - * 04 BRC, bus release condition: 0=all transfers complete - * 1=release after 1 xfer on all channels - * 03 CCC, channel change condition: 0=every cycle - * 1=after each channel completes all xfers - * 02..00 PR<2..0>, priority 100=round robin - * - * 00000100 = 0x00 - */ -static unsigned char dma_priority = 0x04; - -// Number of bytes that can be written to shared RAM -// in a single write operation -static u32 sca_pci_load_interval = 64; - -/* - * 1st function defined in .text section. Calling this function in - * init_module() followed by a breakpoint allows a remote debugger - * (gdb) to get the .text address for the add-symbol-file command. - * This allows remote debugging of dynamically loadable modules. - */ -static void* synclinkmp_get_text_ptr(void); -static void* synclinkmp_get_text_ptr(void) {return synclinkmp_get_text_ptr;} - -static inline int sanity_check(SLMP_INFO *info, - char *name, const char *routine) -{ -#ifdef SANITY_CHECK - static const char *badmagic = - "Warning: bad magic number for synclinkmp_struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null synclinkmp_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != MGSL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#else - if (!info) - return 1; -#endif - return 0; -} - -/** - * line discipline callback wrappers - * - * The wrappers maintain line discipline references - * while calling into the line discipline. - * - * ldisc_receive_buf - pass receive data to line discipline - */ - -static void ldisc_receive_buf(struct tty_struct *tty, - const __u8 *data, char *flags, int count) -{ - struct tty_ldisc *ld; - if (!tty) - return; - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, data, flags, count); - tty_ldisc_deref(ld); - } -} - -/* tty callbacks */ - -/* Called when a port is opened. Init and enable port. - */ -static int open(struct tty_struct *tty, struct file *filp) -{ - SLMP_INFO *info; - int retval, line; - unsigned long flags; - - line = tty->index; - if ((line < 0) || (line >= synclinkmp_device_count)) { - printk("%s(%d): open with invalid line #%d.\n", - __FILE__,__LINE__,line); - return -ENODEV; - } - - info = synclinkmp_device_list; - while(info && info->line != line) - info = info->next_device; - if (sanity_check(info, tty->name, "open")) - return -ENODEV; - if ( info->init_error ) { - printk("%s(%d):%s device is not allocated, init error=%d\n", - __FILE__,__LINE__,info->device_name,info->init_error); - return -ENODEV; - } - - tty->driver_data = info; - info->port.tty = tty; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s open(), old ref count = %d\n", - __FILE__,__LINE__,tty->driver->name, info->port.count); - - /* If port is closing, signal caller to try again */ - if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - spin_lock_irqsave(&info->netlock, flags); - if (info->netcount) { - retval = -EBUSY; - spin_unlock_irqrestore(&info->netlock, flags); - goto cleanup; - } - info->port.count++; - spin_unlock_irqrestore(&info->netlock, flags); - - if (info->port.count == 1) { - /* 1st open on this device, init hardware */ - retval = startup(info); - if (retval < 0) - goto cleanup; - } - - retval = block_til_ready(tty, filp, info); - if (retval) { - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s block_til_ready() returned %d\n", - __FILE__,__LINE__, info->device_name, retval); - goto cleanup; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s open() success\n", - __FILE__,__LINE__, info->device_name); - retval = 0; - -cleanup: - if (retval) { - if (tty->count == 1) - info->port.tty = NULL; /* tty layer will release tty struct */ - if(info->port.count) - info->port.count--; - } - - return retval; -} - -/* Called when port is closed. Wait for remaining data to be - * sent. Disable port and free resources. - */ -static void close(struct tty_struct *tty, struct file *filp) -{ - SLMP_INFO * info = tty->driver_data; - - if (sanity_check(info, tty->name, "close")) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s close() entry, count=%d\n", - __FILE__,__LINE__, info->device_name, info->port.count); - - if (tty_port_close_start(&info->port, tty, filp) == 0) - goto cleanup; - - mutex_lock(&info->port.mutex); - if (info->port.flags & ASYNC_INITIALIZED) - wait_until_sent(tty, info->timeout); - - flush_buffer(tty); - tty_ldisc_flush(tty); - shutdown(info); - mutex_unlock(&info->port.mutex); - - tty_port_close_end(&info->port, tty); - info->port.tty = NULL; -cleanup: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__, - tty->driver->name, info->port.count); -} - -/* Called by tty_hangup() when a hangup is signaled. - * This is the same as closing all open descriptors for the port. - */ -static void hangup(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s hangup()\n", - __FILE__,__LINE__, info->device_name ); - - if (sanity_check(info, tty->name, "hangup")) - return; - - mutex_lock(&info->port.mutex); - flush_buffer(tty); - shutdown(info); - - spin_lock_irqsave(&info->port.lock, flags); - info->port.count = 0; - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - spin_unlock_irqrestore(&info->port.lock, flags); - mutex_unlock(&info->port.mutex); - - wake_up_interruptible(&info->port.open_wait); -} - -/* Set new termios settings - */ -static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__, - tty->driver->name ); - - change_params(info); - - /* Handle transition to B0 status */ - if (old_termios->c_cflag & CBAUD && - !(tty->termios->c_cflag & CBAUD)) { - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - tty->termios->c_cflag & CBAUD) { - info->serial_signals |= SerialSignal_DTR; - if (!(tty->termios->c_cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) { - info->serial_signals |= SerialSignal_RTS; - } - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle turning off CRTSCTS */ - if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - tx_release(tty); - } -} - -/* Send a block of data - * - * Arguments: - * - * tty pointer to tty information structure - * buf pointer to buffer containing send data - * count size of send data in bytes - * - * Return Value: number of characters written - */ -static int write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s write() count=%d\n", - __FILE__,__LINE__,info->device_name,count); - - if (sanity_check(info, tty->name, "write")) - goto cleanup; - - if (!info->tx_buf) - goto cleanup; - - if (info->params.mode == MGSL_MODE_HDLC) { - if (count > info->max_frame_size) { - ret = -EIO; - goto cleanup; - } - if (info->tx_active) - goto cleanup; - if (info->tx_count) { - /* send accumulated data from send_char() calls */ - /* as frame and wait before accepting more data. */ - tx_load_dma_buffer(info, info->tx_buf, info->tx_count); - goto start; - } - ret = info->tx_count = count; - tx_load_dma_buffer(info, buf, count); - goto start; - } - - for (;;) { - c = min_t(int, count, - min(info->max_frame_size - info->tx_count - 1, - info->max_frame_size - info->tx_put)); - if (c <= 0) - break; - - memcpy(info->tx_buf + info->tx_put, buf, c); - - spin_lock_irqsave(&info->lock,flags); - info->tx_put += c; - if (info->tx_put >= info->max_frame_size) - info->tx_put -= info->max_frame_size; - info->tx_count += c; - spin_unlock_irqrestore(&info->lock,flags); - - buf += c; - count -= c; - ret += c; - } - - if (info->params.mode == MGSL_MODE_HDLC) { - if (count) { - ret = info->tx_count = 0; - goto cleanup; - } - tx_load_dma_buffer(info, info->tx_buf, info->tx_count); - } -start: - if (info->tx_count && !tty->stopped && !tty->hw_stopped) { - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } - -cleanup: - if (debug_level >= DEBUG_LEVEL_INFO) - printk( "%s(%d):%s write() returning=%d\n", - __FILE__,__LINE__,info->device_name,ret); - return ret; -} - -/* Add a character to the transmit buffer. - */ -static int put_char(struct tty_struct *tty, unsigned char ch) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - int ret = 0; - - if ( debug_level >= DEBUG_LEVEL_INFO ) { - printk( "%s(%d):%s put_char(%d)\n", - __FILE__,__LINE__,info->device_name,ch); - } - - if (sanity_check(info, tty->name, "put_char")) - return 0; - - if (!info->tx_buf) - return 0; - - spin_lock_irqsave(&info->lock,flags); - - if ( (info->params.mode != MGSL_MODE_HDLC) || - !info->tx_active ) { - - if (info->tx_count < info->max_frame_size - 1) { - info->tx_buf[info->tx_put++] = ch; - if (info->tx_put >= info->max_frame_size) - info->tx_put -= info->max_frame_size; - info->tx_count++; - ret = 1; - } - } - - spin_unlock_irqrestore(&info->lock,flags); - return ret; -} - -/* Send a high-priority XON/XOFF character - */ -static void send_xchar(struct tty_struct *tty, char ch) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s send_xchar(%d)\n", - __FILE__,__LINE__, info->device_name, ch ); - - if (sanity_check(info, tty->name, "send_xchar")) - return; - - info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_enabled) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* Wait until the transmitter is empty. - */ -static void wait_until_sent(struct tty_struct *tty, int timeout) -{ - SLMP_INFO * info = tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (!info ) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s wait_until_sent() entry\n", - __FILE__,__LINE__, info->device_name ); - - if (sanity_check(info, tty->name, "wait_until_sent")) - return; - - if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags)) - goto exit; - - orig_jiffies = jiffies; - - /* Set check interval to 1/5 of estimated time to - * send a character, and make it at least 1. The check - * interval should also be less than the timeout. - * Note: use tight timings here to satisfy the NIST-PCTS. - */ - - if ( info->params.data_rate ) { - char_time = info->timeout/(32 * 5); - if (!char_time) - char_time++; - } else - char_time = 1; - - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - - if ( info->params.mode == MGSL_MODE_HDLC ) { - while (info->tx_active) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } else { - /* - * TODO: determine if there is something similar to USC16C32 - * TXSTATUS_ALL_SENT status - */ - while ( info->tx_active && info->tx_enabled) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - } - -exit: - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s wait_until_sent() exit\n", - __FILE__,__LINE__, info->device_name ); -} - -/* Return the count of free bytes in transmit buffer - */ -static int write_room(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - int ret; - - if (sanity_check(info, tty->name, "write_room")) - return 0; - - if (info->params.mode == MGSL_MODE_HDLC) { - ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; - } else { - ret = info->max_frame_size - info->tx_count - 1; - if (ret < 0) - ret = 0; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s write_room()=%d\n", - __FILE__, __LINE__, info->device_name, ret); - - return ret; -} - -/* enable transmitter and send remaining buffered characters - */ -static void flush_chars(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s flush_chars() entry tx_count=%d\n", - __FILE__,__LINE__,info->device_name,info->tx_count); - - if (sanity_check(info, tty->name, "flush_chars")) - return; - - if (info->tx_count <= 0 || tty->stopped || tty->hw_stopped || - !info->tx_buf) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s flush_chars() entry, starting transmitter\n", - __FILE__,__LINE__,info->device_name ); - - spin_lock_irqsave(&info->lock,flags); - - if (!info->tx_active) { - if ( (info->params.mode == MGSL_MODE_HDLC) && - info->tx_count ) { - /* operating in synchronous (frame oriented) mode */ - /* copy data from circular tx_buf to */ - /* transmit DMA buffer. */ - tx_load_dma_buffer(info, - info->tx_buf,info->tx_count); - } - tx_start(info); - } - - spin_unlock_irqrestore(&info->lock,flags); -} - -/* Discard all data in the send buffer - */ -static void flush_buffer(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s flush_buffer() entry\n", - __FILE__,__LINE__, info->device_name ); - - if (sanity_check(info, tty->name, "flush_buffer")) - return; - - spin_lock_irqsave(&info->lock,flags); - info->tx_count = info->tx_put = info->tx_get = 0; - del_timer(&info->tx_timer); - spin_unlock_irqrestore(&info->lock,flags); - - tty_wakeup(tty); -} - -/* throttle (stop) transmitter - */ -static void tx_hold(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_hold")) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):%s tx_hold()\n", - __FILE__,__LINE__,info->device_name); - - spin_lock_irqsave(&info->lock,flags); - if (info->tx_enabled) - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); -} - -/* release (start) transmitter - */ -static void tx_release(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_release")) - return; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):%s tx_release()\n", - __FILE__,__LINE__,info->device_name); - - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_enabled) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); -} - -/* Service an IOCTL request - * - * Arguments: - * - * tty pointer to tty instance data - * cmd IOCTL command code - * arg command argument/context - * - * Return Value: 0 if success, otherwise error code - */ -static int ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - SLMP_INFO *info = tty->driver_data; - void __user *argp = (void __user *)arg; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s ioctl() cmd=%08X\n", __FILE__,__LINE__, - info->device_name, cmd ); - - if (sanity_check(info, tty->name, "ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCMIWAIT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case MGSL_IOCGPARAMS: - return get_params(info, argp); - case MGSL_IOCSPARAMS: - return set_params(info, argp); - case MGSL_IOCGTXIDLE: - return get_txidle(info, argp); - case MGSL_IOCSTXIDLE: - return set_txidle(info, (int)arg); - case MGSL_IOCTXENABLE: - return tx_enable(info, (int)arg); - case MGSL_IOCRXENABLE: - return rx_enable(info, (int)arg); - case MGSL_IOCTXABORT: - return tx_abort(info); - case MGSL_IOCGSTATS: - return get_stats(info, argp); - case MGSL_IOCWAITEVENT: - return wait_mgsl_event(info, argp); - case MGSL_IOCLOOPTXDONE: - return 0; // TODO: Not supported, need to document - /* Wait for modem input (DCD,RI,DSR,CTS) change - * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS) - */ - case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - SLMP_INFO *info = tty->driver_data; - struct mgsl_icount cnow; /* kernel counter temps */ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->lock,flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - -/* - * /proc fs routines.... - */ - -static inline void line_info(struct seq_file *m, SLMP_INFO *info) -{ - char stat_buf[30]; - unsigned long flags; - - seq_printf(m, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n" - "\tIRQ=%d MaxFrameSize=%u\n", - info->device_name, - info->phys_sca_base, - info->phys_memory_base, - info->phys_statctrl_base, - info->phys_lcr_base, - info->irq_level, - info->max_frame_size ); - - /* output current serial signal states */ - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (info->serial_signals & SerialSignal_RTS) - strcat(stat_buf, "|RTS"); - if (info->serial_signals & SerialSignal_CTS) - strcat(stat_buf, "|CTS"); - if (info->serial_signals & SerialSignal_DTR) - strcat(stat_buf, "|DTR"); - if (info->serial_signals & SerialSignal_DSR) - strcat(stat_buf, "|DSR"); - if (info->serial_signals & SerialSignal_DCD) - strcat(stat_buf, "|CD"); - if (info->serial_signals & SerialSignal_RI) - strcat(stat_buf, "|RI"); - - if (info->params.mode == MGSL_MODE_HDLC) { - seq_printf(m, "\tHDLC txok:%d rxok:%d", - info->icount.txok, info->icount.rxok); - if (info->icount.txunder) - seq_printf(m, " txunder:%d", info->icount.txunder); - if (info->icount.txabort) - seq_printf(m, " txabort:%d", info->icount.txabort); - if (info->icount.rxshort) - seq_printf(m, " rxshort:%d", info->icount.rxshort); - if (info->icount.rxlong) - seq_printf(m, " rxlong:%d", info->icount.rxlong); - if (info->icount.rxover) - seq_printf(m, " rxover:%d", info->icount.rxover); - if (info->icount.rxcrc) - seq_printf(m, " rxlong:%d", info->icount.rxcrc); - } else { - seq_printf(m, "\tASYNC tx:%d rx:%d", - info->icount.tx, info->icount.rx); - if (info->icount.frame) - seq_printf(m, " fe:%d", info->icount.frame); - if (info->icount.parity) - seq_printf(m, " pe:%d", info->icount.parity); - if (info->icount.brk) - seq_printf(m, " brk:%d", info->icount.brk); - if (info->icount.overrun) - seq_printf(m, " oe:%d", info->icount.overrun); - } - - /* Append serial signal status to end */ - seq_printf(m, " %s\n", stat_buf+1); - - seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", - info->tx_active,info->bh_requested,info->bh_running, - info->pending_bh); -} - -/* Called to print information about devices - */ -static int synclinkmp_proc_show(struct seq_file *m, void *v) -{ - SLMP_INFO *info; - - seq_printf(m, "synclinkmp driver:%s\n", driver_version); - - info = synclinkmp_device_list; - while( info ) { - line_info(m, info); - info = info->next_device; - } - return 0; -} - -static int synclinkmp_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, synclinkmp_proc_show, NULL); -} - -static const struct file_operations synclinkmp_proc_fops = { - .owner = THIS_MODULE, - .open = synclinkmp_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* Return the count of bytes in transmit buffer - */ -static int chars_in_buffer(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - - if (sanity_check(info, tty->name, "chars_in_buffer")) - return 0; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s chars_in_buffer()=%d\n", - __FILE__, __LINE__, info->device_name, info->tx_count); - - return info->tx_count; -} - -/* Signal remote device to throttle send data (our receive data) - */ -static void throttle(struct tty_struct * tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s throttle() entry\n", - __FILE__,__LINE__, info->device_name ); - - if (sanity_check(info, tty->name, "throttle")) - return; - - if (I_IXOFF(tty)) - send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->lock,flags); - info->serial_signals &= ~SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* Signal remote device to stop throttling send data (our receive data) - */ -static void unthrottle(struct tty_struct * tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s unthrottle() entry\n", - __FILE__,__LINE__, info->device_name ); - - if (sanity_check(info, tty->name, "unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) { - spin_lock_irqsave(&info->lock,flags); - info->serial_signals |= SerialSignal_RTS; - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* set or clear transmit break condition - * break_state -1=set break condition, 0=clear - */ -static int set_break(struct tty_struct *tty, int break_state) -{ - unsigned char RegValue; - SLMP_INFO * info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s set_break(%d)\n", - __FILE__,__LINE__, info->device_name, break_state); - - if (sanity_check(info, tty->name, "set_break")) - return -EINVAL; - - spin_lock_irqsave(&info->lock,flags); - RegValue = read_reg(info, CTL); - if (break_state == -1) - RegValue |= BIT3; - else - RegValue &= ~BIT3; - write_reg(info, CTL, RegValue); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -#if SYNCLINK_GENERIC_HDLC - -/** - * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) - * set encoding and frame check sequence (FCS) options - * - * dev pointer to network device structure - * encoding serial encoding setting - * parity FCS setting - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, - unsigned short parity) -{ - SLMP_INFO *info = dev_to_port(dev); - unsigned char new_encoding; - unsigned short new_crctype; - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - switch (encoding) - { - case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; - case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; - case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; - case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; - case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; - default: return -EINVAL; - } - - switch (parity) - { - case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; - case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; - case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; - default: return -EINVAL; - } - - info->params.encoding = new_encoding; - info->params.crc_type = new_crctype; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - - return 0; -} - -/** - * called by generic HDLC layer to send frame - * - * skb socket buffer containing HDLC frame - * dev pointer to network device structure - */ -static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - SLMP_INFO *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name); - - /* stop sending until this frame completes */ - netif_stop_queue(dev); - - /* copy data to device buffers */ - info->tx_count = skb->len; - tx_load_dma_buffer(info, skb->data, skb->len); - - /* update network statistics */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - - /* save start time for transmit timeout detection */ - dev->trans_start = jiffies; - - /* start hardware transmitter if necessary */ - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - - return NETDEV_TX_OK; -} - -/** - * called by network layer when interface enabled - * claim resources and initialize hardware - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_open(struct net_device *dev) -{ - SLMP_INFO *info = dev_to_port(dev); - int rc; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); - - /* generic HDLC layer open processing */ - if ((rc = hdlc_open(dev))) - return rc; - - /* arbitrate between network and tty opens */ - spin_lock_irqsave(&info->netlock, flags); - if (info->port.count != 0 || info->netcount != 0) { - printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); - spin_unlock_irqrestore(&info->netlock, flags); - return -EBUSY; - } - info->netcount=1; - spin_unlock_irqrestore(&info->netlock, flags); - - /* claim resources and init adapter */ - if ((rc = startup(info)) != 0) { - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - - /* assert DTR and RTS, apply hardware settings */ - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - program_hw(info); - - /* enable network layer transmit */ - dev->trans_start = jiffies; - netif_start_queue(dev); - - /* inform generic HDLC layer of current DCD status */ - spin_lock_irqsave(&info->lock, flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock, flags); - if (info->serial_signals & SerialSignal_DCD) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - return 0; -} - -/** - * called by network layer when interface is disabled - * shutdown hardware and release resources - * - * dev pointer to network device structure - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_close(struct net_device *dev) -{ - SLMP_INFO *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name); - - netif_stop_queue(dev); - - /* shutdown adapter and release resources */ - shutdown(info); - - hdlc_close(dev); - - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - - return 0; -} - -/** - * called by network layer to process IOCTL call to network device - * - * dev pointer to network device structure - * ifr pointer to network interface request structure - * cmd IOCTL command code - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - const size_t size = sizeof(sync_serial_settings); - sync_serial_settings new_line; - sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; - SLMP_INFO *info = dev_to_port(dev); - unsigned int flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name); - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - if (cmd != SIOCWANDEV) - return hdlc_ioctl(dev, ifr, cmd); - - switch(ifr->ifr_settings.type) { - case IF_GET_IFACE: /* return current sync_serial_settings */ - - ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; - if (ifr->ifr_settings.size < size) { - ifr->ifr_settings.size = size; /* data size wanted */ - return -ENOBUFS; - } - - flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - - switch (flags){ - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; - case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; - default: new_line.clock_type = CLOCK_DEFAULT; - } - - new_line.clock_rate = info->params.clock_speed; - new_line.loopback = info->params.loopback ? 1:0; - - if (copy_to_user(line, &new_line, size)) - return -EFAULT; - return 0; - - case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&new_line, line, size)) - return -EFAULT; - - switch (new_line.clock_type) - { - case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; - case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; - case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; - case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; - case CLOCK_DEFAULT: flags = info->params.flags & - (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; - default: return -EINVAL; - } - - if (new_line.loopback != 0 && new_line.loopback != 1) - return -EINVAL; - - info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - info->params.flags |= flags; - - info->params.loopback = new_line.loopback; - - if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) - info->params.clock_speed = new_line.clock_rate; - else - info->params.clock_speed = 0; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - return 0; - - default: - return hdlc_ioctl(dev, ifr, cmd); - } -} - -/** - * called by network layer when transmit timeout is detected - * - * dev pointer to network device structure - */ -static void hdlcdev_tx_timeout(struct net_device *dev) -{ - SLMP_INFO *info = dev_to_port(dev); - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_tx_timeout(%s)\n",dev->name); - - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - - spin_lock_irqsave(&info->lock,flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); - - netif_wake_queue(dev); -} - -/** - * called by device driver when transmit completes - * reenable network layer transmit if stopped - * - * info pointer to device instance information - */ -static void hdlcdev_tx_done(SLMP_INFO *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); -} - -/** - * called by device driver when frame received - * pass frame to network layer - * - * info pointer to device instance information - * buf pointer to buffer contianing frame data - * size count of data bytes in buf - */ -static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - struct net_device *dev = info->netdev; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_rx(%s)\n",dev->name); - - if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", - dev->name); - dev->stats.rx_dropped++; - return; - } - - memcpy(skb_put(skb, size), buf, size); - - skb->protocol = hdlc_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - netif_rx(skb); -} - -static const struct net_device_ops hdlcdev_ops = { - .ndo_open = hdlcdev_open, - .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_do_ioctl = hdlcdev_ioctl, - .ndo_tx_timeout = hdlcdev_tx_timeout, -}; - -/** - * called by device driver when adding device instance - * do generic HDLC initialization - * - * info pointer to device instance information - * - * returns 0 if success, otherwise error code - */ -static int hdlcdev_init(SLMP_INFO *info) -{ - int rc; - struct net_device *dev; - hdlc_device *hdlc; - - /* allocate and initialize network and HDLC layer objects */ - - if (!(dev = alloc_hdlcdev(info))) { - printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); - return -ENOMEM; - } - - /* for network layer reporting purposes only */ - dev->mem_start = info->phys_sca_base; - dev->mem_end = info->phys_sca_base + SCA_BASE_SIZE - 1; - dev->irq = info->irq_level; - - /* network layer callbacks and settings */ - dev->netdev_ops = &hdlcdev_ops; - dev->watchdog_timeo = 10 * HZ; - dev->tx_queue_len = 50; - - /* generic HDLC layer callbacks and settings */ - hdlc = dev_to_hdlc(dev); - hdlc->attach = hdlcdev_attach; - hdlc->xmit = hdlcdev_xmit; - - /* register objects with HDLC layer */ - if ((rc = register_hdlc_device(dev))) { - printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); - free_netdev(dev); - return rc; - } - - info->netdev = dev; - return 0; -} - -/** - * called by device driver when removing device instance - * do generic HDLC cleanup - * - * info pointer to device instance information - */ -static void hdlcdev_exit(SLMP_INFO *info) -{ - unregister_hdlc_device(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; -} - -#endif /* CONFIG_HDLC */ - - -/* Return next bottom half action to perform. - * Return Value: BH action code or 0 if nothing to do. - */ -static int bh_action(SLMP_INFO *info) -{ - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&info->lock,flags); - - if (info->pending_bh & BH_RECEIVE) { - info->pending_bh &= ~BH_RECEIVE; - rc = BH_RECEIVE; - } else if (info->pending_bh & BH_TRANSMIT) { - info->pending_bh &= ~BH_TRANSMIT; - rc = BH_TRANSMIT; - } else if (info->pending_bh & BH_STATUS) { - info->pending_bh &= ~BH_STATUS; - rc = BH_STATUS; - } - - if (!rc) { - /* Mark BH routine as complete */ - info->bh_running = false; - info->bh_requested = false; - } - - spin_unlock_irqrestore(&info->lock,flags); - - return rc; -} - -/* Perform bottom half processing of work items queued by ISR. - */ -static void bh_handler(struct work_struct *work) -{ - SLMP_INFO *info = container_of(work, SLMP_INFO, task); - int action; - - if (!info) - return; - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_handler() entry\n", - __FILE__,__LINE__,info->device_name); - - info->bh_running = true; - - while((action = bh_action(info)) != 0) { - - /* Process work item */ - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_handler() work item action=%d\n", - __FILE__,__LINE__,info->device_name, action); - - switch (action) { - - case BH_RECEIVE: - bh_receive(info); - break; - case BH_TRANSMIT: - bh_transmit(info); - break; - case BH_STATUS: - bh_status(info); - break; - default: - /* unknown work item ID */ - printk("%s(%d):%s Unknown work item ID=%08X!\n", - __FILE__,__LINE__,info->device_name,action); - break; - } - } - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_handler() exit\n", - __FILE__,__LINE__,info->device_name); -} - -static void bh_receive(SLMP_INFO *info) -{ - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_receive()\n", - __FILE__,__LINE__,info->device_name); - - while( rx_get_frame(info) ); -} - -static void bh_transmit(SLMP_INFO *info) -{ - struct tty_struct *tty = info->port.tty; - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_transmit() entry\n", - __FILE__,__LINE__,info->device_name); - - if (tty) - tty_wakeup(tty); -} - -static void bh_status(SLMP_INFO *info) -{ - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s bh_status() entry\n", - __FILE__,__LINE__,info->device_name); - - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - info->dcd_chkcount = 0; - info->cts_chkcount = 0; -} - -static void isr_timer(SLMP_INFO * info) -{ - unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0; - - /* IER2<7..4> = timer<3..0> interrupt enables (0=disabled) */ - write_reg(info, IER2, 0); - - /* TMCS, Timer Control/Status Register - * - * 07 CMF, Compare match flag (read only) 1=match - * 06 ECMI, CMF Interrupt Enable: 0=disabled - * 05 Reserved, must be 0 - * 04 TME, Timer Enable - * 03..00 Reserved, must be 0 - * - * 0000 0000 - */ - write_reg(info, (unsigned char)(timer + TMCS), 0); - - info->irq_occurred = true; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_timer()\n", - __FILE__,__LINE__,info->device_name); -} - -static void isr_rxint(SLMP_INFO * info) -{ - struct tty_struct *tty = info->port.tty; - struct mgsl_icount *icount = &info->icount; - unsigned char status = read_reg(info, SR1) & info->ie1_value & (FLGD + IDLD + CDCD + BRKD); - unsigned char status2 = read_reg(info, SR2) & info->ie2_value & OVRN; - - /* clear status bits */ - if (status) - write_reg(info, SR1, status); - - if (status2) - write_reg(info, SR2, status2); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_rxint status=%02X %02x\n", - __FILE__,__LINE__,info->device_name,status,status2); - - if (info->params.mode == MGSL_MODE_ASYNC) { - if (status & BRKD) { - icount->brk++; - - /* process break detection if tty control - * is not set to ignore it - */ - if ( tty ) { - if (!(status & info->ignore_status_mask1)) { - if (info->read_status_mask1 & BRKD) { - tty_insert_flip_char(tty, 0, TTY_BREAK); - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); - } - } - } - } - } - else { - if (status & (FLGD|IDLD)) { - if (status & FLGD) - info->icount.exithunt++; - else if (status & IDLD) - info->icount.rxidle++; - wake_up_interruptible(&info->event_wait_q); - } - } - - if (status & CDCD) { - /* simulate a common modem status change interrupt - * for our handler - */ - get_signals( info ); - isr_io_pin(info, - MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD)); - } -} - -/* - * handle async rx data interrupts - */ -static void isr_rxrdy(SLMP_INFO * info) -{ - u16 status; - unsigned char DataByte; - struct tty_struct *tty = info->port.tty; - struct mgsl_icount *icount = &info->icount; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_rxrdy\n", - __FILE__,__LINE__,info->device_name); - - while((status = read_reg(info,CST0)) & BIT0) - { - int flag = 0; - bool over = false; - DataByte = read_reg(info,TRB); - - icount->rx++; - - if ( status & (PE + FRME + OVRN) ) { - printk("%s(%d):%s rxerr=%04X\n", - __FILE__,__LINE__,info->device_name,status); - - /* update error statistics */ - if (status & PE) - icount->parity++; - else if (status & FRME) - icount->frame++; - else if (status & OVRN) - icount->overrun++; - - /* discard char if tty control flags say so */ - if (status & info->ignore_status_mask2) - continue; - - status &= info->read_status_mask2; - - if ( tty ) { - if (status & PE) - flag = TTY_PARITY; - else if (status & FRME) - flag = TTY_FRAME; - if (status & OVRN) { - /* Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - over = true; - } - } - } /* end of if (error) */ - - if ( tty ) { - tty_insert_flip_char(tty, DataByte, flag); - if (over) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - } - - if ( debug_level >= DEBUG_LEVEL_ISR ) { - printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n", - __FILE__,__LINE__,info->device_name, - icount->rx,icount->brk,icount->parity, - icount->frame,icount->overrun); - } - - if ( tty ) - tty_flip_buffer_push(tty); -} - -static void isr_txeom(SLMP_INFO * info, unsigned char status) -{ - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_txeom status=%02x\n", - __FILE__,__LINE__,info->device_name,status); - - write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */ - write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */ - write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ - - if (status & UDRN) { - write_reg(info, CMD, TXRESET); - write_reg(info, CMD, TXENABLE); - } else - write_reg(info, CMD, TXBUFCLR); - - /* disable and clear tx interrupts */ - info->ie0_value &= ~TXRDYE; - info->ie1_value &= ~(IDLE + UDRN); - write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value)); - write_reg(info, SR1, (unsigned char)(UDRN + IDLE)); - - if ( info->tx_active ) { - if (info->params.mode != MGSL_MODE_ASYNC) { - if (status & UDRN) - info->icount.txunder++; - else if (status & IDLE) - info->icount.txok++; - } - - info->tx_active = false; - info->tx_count = info->tx_put = info->tx_get = 0; - - del_timer(&info->tx_timer); - - if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done ) { - info->serial_signals &= ~SerialSignal_RTS; - info->drop_rts_on_tx_done = false; - set_signals(info); - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { - tx_stop(info); - return; - } - info->pending_bh |= BH_TRANSMIT; - } - } -} - - -/* - * handle tx status interrupts - */ -static void isr_txint(SLMP_INFO * info) -{ - unsigned char status = read_reg(info, SR1) & info->ie1_value & (UDRN + IDLE + CCTS); - - /* clear status bits */ - write_reg(info, SR1, status); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_txint status=%02x\n", - __FILE__,__LINE__,info->device_name,status); - - if (status & (UDRN + IDLE)) - isr_txeom(info, status); - - if (status & CCTS) { - /* simulate a common modem status change interrupt - * for our handler - */ - get_signals( info ); - isr_io_pin(info, - MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS)); - - } -} - -/* - * handle async tx data interrupts - */ -static void isr_txrdy(SLMP_INFO * info) -{ - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_txrdy() tx_count=%d\n", - __FILE__,__LINE__,info->device_name,info->tx_count); - - if (info->params.mode != MGSL_MODE_ASYNC) { - /* disable TXRDY IRQ, enable IDLE IRQ */ - info->ie0_value &= ~TXRDYE; - info->ie1_value |= IDLE; - write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value)); - return; - } - - if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { - tx_stop(info); - return; - } - - if ( info->tx_count ) - tx_load_fifo( info ); - else { - info->tx_active = false; - info->ie0_value &= ~TXRDYE; - write_reg(info, IE0, info->ie0_value); - } - - if (info->tx_count < WAKEUP_CHARS) - info->pending_bh |= BH_TRANSMIT; -} - -static void isr_rxdmaok(SLMP_INFO * info) -{ - /* BIT7 = EOT (end of transfer) - * BIT6 = EOM (end of message/frame) - */ - unsigned char status = read_reg(info,RXDMA + DSR) & 0xc0; - - /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ - write_reg(info, RXDMA + DSR, (unsigned char)(status | 1)); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_rxdmaok(), status=%02x\n", - __FILE__,__LINE__,info->device_name,status); - - info->pending_bh |= BH_RECEIVE; -} - -static void isr_rxdmaerror(SLMP_INFO * info) -{ - /* BIT5 = BOF (buffer overflow) - * BIT4 = COF (counter overflow) - */ - unsigned char status = read_reg(info,RXDMA + DSR) & 0x30; - - /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ - write_reg(info, RXDMA + DSR, (unsigned char)(status | 1)); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_rxdmaerror(), status=%02x\n", - __FILE__,__LINE__,info->device_name,status); - - info->rx_overflow = true; - info->pending_bh |= BH_RECEIVE; -} - -static void isr_txdmaok(SLMP_INFO * info) -{ - unsigned char status_reg1 = read_reg(info, SR1); - - write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */ - write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */ - write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_txdmaok(), status=%02x\n", - __FILE__,__LINE__,info->device_name,status_reg1); - - /* program TXRDY as FIFO empty flag, enable TXRDY IRQ */ - write_reg16(info, TRC0, 0); - info->ie0_value |= TXRDYE; - write_reg(info, IE0, info->ie0_value); -} - -static void isr_txdmaerror(SLMP_INFO * info) -{ - /* BIT5 = BOF (buffer overflow) - * BIT4 = COF (counter overflow) - */ - unsigned char status = read_reg(info,TXDMA + DSR) & 0x30; - - /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ - write_reg(info, TXDMA + DSR, (unsigned char)(status | 1)); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s isr_txdmaerror(), status=%02x\n", - __FILE__,__LINE__,info->device_name,status); -} - -/* handle input serial signal changes - */ -static void isr_io_pin( SLMP_INFO *info, u16 status ) -{ - struct mgsl_icount *icount; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):isr_io_pin status=%04X\n", - __FILE__,__LINE__,status); - - if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | - MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { - icount = &info->icount; - /* update input line counters */ - if (status & MISCSTATUS_RI_LATCHED) { - icount->rng++; - if ( status & SerialSignal_RI ) - info->input_signal_events.ri_up++; - else - info->input_signal_events.ri_down++; - } - if (status & MISCSTATUS_DSR_LATCHED) { - icount->dsr++; - if ( status & SerialSignal_DSR ) - info->input_signal_events.dsr_up++; - else - info->input_signal_events.dsr_down++; - } - if (status & MISCSTATUS_DCD_LATCHED) { - if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) { - info->ie1_value &= ~CDCD; - write_reg(info, IE1, info->ie1_value); - } - icount->dcd++; - if (status & SerialSignal_DCD) { - info->input_signal_events.dcd_up++; - } else - info->input_signal_events.dcd_down++; -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) { - if (status & SerialSignal_DCD) - netif_carrier_on(info->netdev); - else - netif_carrier_off(info->netdev); - } -#endif - } - if (status & MISCSTATUS_CTS_LATCHED) - { - if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) { - info->ie1_value &= ~CCTS; - write_reg(info, IE1, info->ie1_value); - } - icount->cts++; - if ( status & SerialSignal_CTS ) - info->input_signal_events.cts_up++; - else - info->input_signal_events.cts_down++; - } - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - if ( (info->port.flags & ASYNC_CHECK_CD) && - (status & MISCSTATUS_DCD_LATCHED) ) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s CD now %s...", info->device_name, - (status & SerialSignal_DCD) ? "on" : "off"); - if (status & SerialSignal_DCD) - wake_up_interruptible(&info->port.open_wait); - else { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("doing serial hangup..."); - if (info->port.tty) - tty_hangup(info->port.tty); - } - } - - if ( (info->port.flags & ASYNC_CTS_FLOW) && - (status & MISCSTATUS_CTS_LATCHED) ) { - if ( info->port.tty ) { - if (info->port.tty->hw_stopped) { - if (status & SerialSignal_CTS) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("CTS tx start..."); - info->port.tty->hw_stopped = 0; - tx_start(info); - info->pending_bh |= BH_TRANSMIT; - return; - } - } else { - if (!(status & SerialSignal_CTS)) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("CTS tx stop..."); - info->port.tty->hw_stopped = 1; - tx_stop(info); - } - } - } - } - } - - info->pending_bh |= BH_STATUS; -} - -/* Interrupt service routine entry point. - * - * Arguments: - * irq interrupt number that caused interrupt - * dev_id device ID supplied during interrupt registration - * regs interrupted processor context - */ -static irqreturn_t synclinkmp_interrupt(int dummy, void *dev_id) -{ - SLMP_INFO *info = dev_id; - unsigned char status, status0, status1=0; - unsigned char dmastatus, dmastatus0, dmastatus1=0; - unsigned char timerstatus0, timerstatus1=0; - unsigned char shift; - unsigned int i; - unsigned short tmp; - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk(KERN_DEBUG "%s(%d): synclinkmp_interrupt(%d)entry.\n", - __FILE__, __LINE__, info->irq_level); - - spin_lock(&info->lock); - - for(;;) { - - /* get status for SCA0 (ports 0-1) */ - tmp = read_reg16(info, ISR0); /* get ISR0 and ISR1 in one read */ - status0 = (unsigned char)tmp; - dmastatus0 = (unsigned char)(tmp>>8); - timerstatus0 = read_reg(info, ISR2); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk(KERN_DEBUG "%s(%d):%s status0=%02x, dmastatus0=%02x, timerstatus0=%02x\n", - __FILE__, __LINE__, info->device_name, - status0, dmastatus0, timerstatus0); - - if (info->port_count == 4) { - /* get status for SCA1 (ports 2-3) */ - tmp = read_reg16(info->port_array[2], ISR0); - status1 = (unsigned char)tmp; - dmastatus1 = (unsigned char)(tmp>>8); - timerstatus1 = read_reg(info->port_array[2], ISR2); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s status1=%02x, dmastatus1=%02x, timerstatus1=%02x\n", - __FILE__,__LINE__,info->device_name, - status1,dmastatus1,timerstatus1); - } - - if (!status0 && !dmastatus0 && !timerstatus0 && - !status1 && !dmastatus1 && !timerstatus1) - break; - - for(i=0; i < info->port_count ; i++) { - if (info->port_array[i] == NULL) - continue; - if (i < 2) { - status = status0; - dmastatus = dmastatus0; - } else { - status = status1; - dmastatus = dmastatus1; - } - - shift = i & 1 ? 4 :0; - - if (status & BIT0 << shift) - isr_rxrdy(info->port_array[i]); - if (status & BIT1 << shift) - isr_txrdy(info->port_array[i]); - if (status & BIT2 << shift) - isr_rxint(info->port_array[i]); - if (status & BIT3 << shift) - isr_txint(info->port_array[i]); - - if (dmastatus & BIT0 << shift) - isr_rxdmaerror(info->port_array[i]); - if (dmastatus & BIT1 << shift) - isr_rxdmaok(info->port_array[i]); - if (dmastatus & BIT2 << shift) - isr_txdmaerror(info->port_array[i]); - if (dmastatus & BIT3 << shift) - isr_txdmaok(info->port_array[i]); - } - - if (timerstatus0 & (BIT5 | BIT4)) - isr_timer(info->port_array[0]); - if (timerstatus0 & (BIT7 | BIT6)) - isr_timer(info->port_array[1]); - if (timerstatus1 & (BIT5 | BIT4)) - isr_timer(info->port_array[2]); - if (timerstatus1 & (BIT7 | BIT6)) - isr_timer(info->port_array[3]); - } - - for(i=0; i < info->port_count ; i++) { - SLMP_INFO * port = info->port_array[i]; - - /* Request bottom half processing if there's something - * for it to do and the bh is not already running. - * - * Note: startup adapter diags require interrupts. - * do not request bottom half processing if the - * device is not open in a normal mode. - */ - if ( port && (port->port.count || port->netcount) && - port->pending_bh && !port->bh_running && - !port->bh_requested ) { - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk("%s(%d):%s queueing bh task.\n", - __FILE__,__LINE__,port->device_name); - schedule_work(&port->task); - port->bh_requested = true; - } - } - - spin_unlock(&info->lock); - - if ( debug_level >= DEBUG_LEVEL_ISR ) - printk(KERN_DEBUG "%s(%d):synclinkmp_interrupt(%d)exit.\n", - __FILE__, __LINE__, info->irq_level); - return IRQ_HANDLED; -} - -/* Initialize and start device. - */ -static int startup(SLMP_INFO * info) -{ - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name); - - if (info->port.flags & ASYNC_INITIALIZED) - return 0; - - if (!info->tx_buf) { - info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); - if (!info->tx_buf) { - printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", - __FILE__,__LINE__,info->device_name); - return -ENOMEM; - } - } - - info->pending_bh = 0; - - memset(&info->icount, 0, sizeof(info->icount)); - - /* program hardware for current parameters */ - reset_port(info); - - change_params(info); - - mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10)); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags |= ASYNC_INITIALIZED; - - return 0; -} - -/* Called by close() and hangup() to shutdown hardware - */ -static void shutdown(SLMP_INFO * info) -{ - unsigned long flags; - - if (!(info->port.flags & ASYNC_INITIALIZED)) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s synclinkmp_shutdown()\n", - __FILE__,__LINE__, info->device_name ); - - /* clear status wait queue because status changes */ - /* can't happen after shutting down the hardware */ - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - del_timer(&info->tx_timer); - del_timer(&info->status_timer); - - kfree(info->tx_buf); - info->tx_buf = NULL; - - spin_lock_irqsave(&info->lock,flags); - - reset_port(info); - - if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); - set_signals(info); - } - - spin_unlock_irqrestore(&info->lock,flags); - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->port.flags &= ~ASYNC_INITIALIZED; -} - -static void program_hw(SLMP_INFO *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - - rx_stop(info); - tx_stop(info); - - info->tx_count = info->tx_put = info->tx_get = 0; - - if (info->params.mode == MGSL_MODE_HDLC || info->netcount) - hdlc_mode(info); - else - async_mode(info); - - set_signals(info); - - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - - info->ie1_value |= (CDCD|CCTS); - write_reg(info, IE1, info->ie1_value); - - get_signals(info); - - if (info->netcount || (info->port.tty && info->port.tty->termios->c_cflag & CREAD) ) - rx_start(info); - - spin_unlock_irqrestore(&info->lock,flags); -} - -/* Reconfigure adapter based on new parameters - */ -static void change_params(SLMP_INFO *info) -{ - unsigned cflag; - int bits_per_char; - - if (!info->port.tty || !info->port.tty->termios) - return; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s change_params()\n", - __FILE__,__LINE__, info->device_name ); - - cflag = info->port.tty->termios->c_cflag; - - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ - if (cflag & CBAUD) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - - /* byte size and parity */ - - switch (cflag & CSIZE) { - case CS5: info->params.data_bits = 5; break; - case CS6: info->params.data_bits = 6; break; - case CS7: info->params.data_bits = 7; break; - case CS8: info->params.data_bits = 8; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: info->params.data_bits = 7; break; - } - - if (cflag & CSTOPB) - info->params.stop_bits = 2; - else - info->params.stop_bits = 1; - - info->params.parity = ASYNC_PARITY_NONE; - if (cflag & PARENB) { - if (cflag & PARODD) - info->params.parity = ASYNC_PARITY_ODD; - else - info->params.parity = ASYNC_PARITY_EVEN; -#ifdef CMSPAR - if (cflag & CMSPAR) - info->params.parity = ASYNC_PARITY_SPACE; -#endif - } - - /* calculate number of jiffies to transmit a full - * FIFO (32 bytes) at specified data rate - */ - bits_per_char = info->params.data_bits + - info->params.stop_bits + 1; - - /* if port data rate is set to 460800 or less then - * allow tty settings to override, otherwise keep the - * current data rate. - */ - if (info->params.data_rate <= 460800) { - info->params.data_rate = tty_get_baud_rate(info->port.tty); - } - - if ( info->params.data_rate ) { - info->timeout = (32*HZ*bits_per_char) / - info->params.data_rate; - } - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - if (cflag & CRTSCTS) - info->port.flags |= ASYNC_CTS_FLOW; - else - info->port.flags &= ~ASYNC_CTS_FLOW; - - if (cflag & CLOCAL) - info->port.flags &= ~ASYNC_CHECK_CD; - else - info->port.flags |= ASYNC_CHECK_CD; - - /* process tty input control flags */ - - info->read_status_mask2 = OVRN; - if (I_INPCK(info->port.tty)) - info->read_status_mask2 |= PE | FRME; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask1 |= BRKD; - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask2 |= PE | FRME; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask1 |= BRKD; - /* If ignoring parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask2 |= OVRN; - } - - program_hw(info); -} - -static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount) -{ - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s get_params()\n", - __FILE__,__LINE__, info->device_name); - - if (!user_icount) { - memset(&info->icount, 0, sizeof(info->icount)); - } else { - mutex_lock(&info->port.mutex); - COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount)); - mutex_unlock(&info->port.mutex); - if (err) - return -EFAULT; - } - - return 0; -} - -static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params) -{ - int err; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s get_params()\n", - __FILE__,__LINE__, info->device_name); - - mutex_lock(&info->port.mutex); - COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); - mutex_unlock(&info->port.mutex); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s get_params() user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - return 0; -} - -static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params) -{ - unsigned long flags; - MGSL_PARAMS tmp_params; - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s set_params\n", - __FILE__,__LINE__,info->device_name ); - COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s set_params() user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - mutex_lock(&info->port.mutex); - spin_lock_irqsave(&info->lock,flags); - memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); - spin_unlock_irqrestore(&info->lock,flags); - - change_params(info); - mutex_unlock(&info->port.mutex); - - return 0; -} - -static int get_txidle(SLMP_INFO * info, int __user *idle_mode) -{ - int err; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s get_txidle()=%d\n", - __FILE__,__LINE__, info->device_name, info->idle_mode); - - COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); - if (err) { - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s get_txidle() user buffer copy failed\n", - __FILE__,__LINE__,info->device_name); - return -EFAULT; - } - - return 0; -} - -static int set_txidle(SLMP_INFO * info, int idle_mode) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s set_txidle(%d)\n", - __FILE__,__LINE__,info->device_name, idle_mode ); - - spin_lock_irqsave(&info->lock,flags); - info->idle_mode = idle_mode; - tx_set_idle( info ); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int tx_enable(SLMP_INFO * info, int enable) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tx_enable(%d)\n", - __FILE__,__LINE__,info->device_name, enable); - - spin_lock_irqsave(&info->lock,flags); - if ( enable ) { - if ( !info->tx_enabled ) { - tx_start(info); - } - } else { - if ( info->tx_enabled ) - tx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* abort send HDLC frame - */ -static int tx_abort(SLMP_INFO * info) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tx_abort()\n", - __FILE__,__LINE__,info->device_name); - - spin_lock_irqsave(&info->lock,flags); - if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) { - info->ie1_value &= ~UDRN; - info->ie1_value |= IDLE; - write_reg(info, IE1, info->ie1_value); /* disable tx status interrupts */ - write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); /* clear pending */ - - write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ - write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ - - write_reg(info, CMD, TXABORT); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int rx_enable(SLMP_INFO * info, int enable) -{ - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s rx_enable(%d)\n", - __FILE__,__LINE__,info->device_name,enable); - - spin_lock_irqsave(&info->lock,flags); - if ( enable ) { - if ( !info->rx_enabled ) - rx_start(info); - } else { - if ( info->rx_enabled ) - rx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* wait for specified event to occur - */ -static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr) -{ - unsigned long flags; - int s; - int rc=0; - struct mgsl_icount cprev, cnow; - int events; - int mask; - struct _input_signal_events oldsigs, newsigs; - DECLARE_WAITQUEUE(wait, current); - - COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); - if (rc) { - return -EFAULT; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s wait_mgsl_event(%d)\n", - __FILE__,__LINE__,info->device_name,mask); - - spin_lock_irqsave(&info->lock,flags); - - /* return immediately if state matches requested events */ - get_signals(info); - s = info->serial_signals; - - events = mask & - ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + - ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + - ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + - ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); - if (events) { - spin_unlock_irqrestore(&info->lock,flags); - goto exit; - } - - /* save current irq counts */ - cprev = info->icount; - oldsigs = info->input_signal_events; - - /* enable hunt and idle irqs if needed */ - if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { - unsigned char oldval = info->ie1_value; - unsigned char newval = oldval + - (mask & MgslEvent_ExitHuntMode ? FLGD:0) + - (mask & MgslEvent_IdleReceived ? IDLD:0); - if ( oldval != newval ) { - info->ie1_value = newval; - write_reg(info, IE1, info->ie1_value); - } - } - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&info->event_wait_q, &wait); - - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - newsigs = info->input_signal_events; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (newsigs.dsr_up == oldsigs.dsr_up && - newsigs.dsr_down == oldsigs.dsr_down && - newsigs.dcd_up == oldsigs.dcd_up && - newsigs.dcd_down == oldsigs.dcd_down && - newsigs.cts_up == oldsigs.cts_up && - newsigs.cts_down == oldsigs.cts_down && - newsigs.ri_up == oldsigs.ri_up && - newsigs.ri_down == oldsigs.ri_down && - cnow.exithunt == cprev.exithunt && - cnow.rxidle == cprev.rxidle) { - rc = -EIO; - break; - } - - events = mask & - ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + - (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + - (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + - (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + - (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + - (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + - (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + - (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + - (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + - (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); - if (events) - break; - - cprev = cnow; - oldsigs = newsigs; - } - - remove_wait_queue(&info->event_wait_q, &wait); - set_current_state(TASK_RUNNING); - - - if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { - spin_lock_irqsave(&info->lock,flags); - if (!waitqueue_active(&info->event_wait_q)) { - /* disable enable exit hunt mode/idle rcvd IRQs */ - info->ie1_value &= ~(FLGD|IDLD); - write_reg(info, IE1, info->ie1_value); - } - spin_unlock_irqrestore(&info->lock,flags); - } -exit: - if ( rc == 0 ) - PUT_USER(rc, events, mask_ptr); - - return rc; -} - -static int modem_input_wait(SLMP_INFO *info,int arg) -{ - unsigned long flags; - int rc; - struct mgsl_icount cprev, cnow; - DECLARE_WAITQUEUE(wait, current); - - /* save current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cprev = info->icount; - add_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get new irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; - break; - } - - /* check for change in caller specified modem input */ - if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || - (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || - (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || - (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { - rc = 0; - break; - } - - cprev = cnow; - } - remove_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_RUNNING); - return rc; -} - -/* return the state of the serial control and status signals - */ -static int tiocmget(struct tty_struct *tty) -{ - SLMP_INFO *info = tty->driver_data; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmget() value=%08X\n", - __FILE__,__LINE__, info->device_name, result ); - return result; -} - -/* set modem control signals (DTR/RTS) - */ -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - SLMP_INFO *info = tty->driver_data; - unsigned long flags; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s tiocmset(%x,%x)\n", - __FILE__,__LINE__,info->device_name, set, clear); - - if (set & TIOCM_RTS) - info->serial_signals |= SerialSignal_RTS; - if (set & TIOCM_DTR) - info->serial_signals |= SerialSignal_DTR; - if (clear & TIOCM_RTS) - info->serial_signals &= ~SerialSignal_RTS; - if (clear & TIOCM_DTR) - info->serial_signals &= ~SerialSignal_DTR; - - spin_lock_irqsave(&info->lock,flags); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - return 0; -} - -static int carrier_raised(struct tty_port *port) -{ - SLMP_INFO *info = container_of(port, SLMP_INFO, port); - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; -} - -static void dtr_rts(struct tty_port *port, int on) -{ - SLMP_INFO *info = container_of(port, SLMP_INFO, port); - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - if (on) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; - else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); - set_signals(info); - spin_unlock_irqrestore(&info->lock,flags); -} - -/* Block the current process until the specified port is ready to open. - */ -static int block_til_ready(struct tty_struct *tty, struct file *filp, - SLMP_INFO *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - bool do_clocal = false; - bool extra_count = false; - unsigned long flags; - int cd; - struct tty_port *port = &info->port; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s block_til_ready()\n", - __FILE__,__LINE__, tty->driver->name ); - - if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ - /* nonblock mode is set or port is not enabled */ - /* just verify that callout device is not active */ - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = true; - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s block_til_ready() before block, count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - spin_lock_irqsave(&info->lock, flags); - if (!tty_hung_up_p(filp)) { - extra_count = true; - port->count--; - } - spin_unlock_irqrestore(&info->lock, flags); - port->blocked_open++; - - while (1) { - if (tty->termios->c_cflag & CBAUD) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - cd = tty_port_carrier_raised(port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s block_til_ready() count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - tty_unlock(); - schedule(); - tty_lock(); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - if (extra_count) - port->count++; - port->blocked_open--; - - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):%s block_til_ready() after, count=%d\n", - __FILE__,__LINE__, tty->driver->name, port->count ); - - if (!retval) - port->flags |= ASYNC_NORMAL_ACTIVE; - - return retval; -} - -static int alloc_dma_bufs(SLMP_INFO *info) -{ - unsigned short BuffersPerFrame; - unsigned short BufferCount; - - // Force allocation to start at 64K boundary for each port. - // This is necessary because *all* buffer descriptors for a port - // *must* be in the same 64K block. All descriptors on a port - // share a common 'base' address (upper 8 bits of 24 bits) programmed - // into the CBP register. - info->port_array[0]->last_mem_alloc = (SCA_MEM_SIZE/4) * info->port_num; - - /* Calculate the number of DMA buffers necessary to hold the */ - /* largest allowable frame size. Note: If the max frame size is */ - /* not an even multiple of the DMA buffer size then we need to */ - /* round the buffer count per frame up one. */ - - BuffersPerFrame = (unsigned short)(info->max_frame_size/SCABUFSIZE); - if ( info->max_frame_size % SCABUFSIZE ) - BuffersPerFrame++; - - /* calculate total number of data buffers (SCABUFSIZE) possible - * in one ports memory (SCA_MEM_SIZE/4) after allocating memory - * for the descriptor list (BUFFERLISTSIZE). - */ - BufferCount = (SCA_MEM_SIZE/4 - BUFFERLISTSIZE)/SCABUFSIZE; - - /* limit number of buffers to maximum amount of descriptors */ - if (BufferCount > BUFFERLISTSIZE/sizeof(SCADESC)) - BufferCount = BUFFERLISTSIZE/sizeof(SCADESC); - - /* use enough buffers to transmit one max size frame */ - info->tx_buf_count = BuffersPerFrame + 1; - - /* never use more than half the available buffers for transmit */ - if (info->tx_buf_count > (BufferCount/2)) - info->tx_buf_count = BufferCount/2; - - if (info->tx_buf_count > SCAMAXDESC) - info->tx_buf_count = SCAMAXDESC; - - /* use remaining buffers for receive */ - info->rx_buf_count = BufferCount - info->tx_buf_count; - - if (info->rx_buf_count > SCAMAXDESC) - info->rx_buf_count = SCAMAXDESC; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk("%s(%d):%s Allocating %d TX and %d RX DMA buffers.\n", - __FILE__,__LINE__, info->device_name, - info->tx_buf_count,info->rx_buf_count); - - if ( alloc_buf_list( info ) < 0 || - alloc_frame_bufs(info, - info->rx_buf_list, - info->rx_buf_list_ex, - info->rx_buf_count) < 0 || - alloc_frame_bufs(info, - info->tx_buf_list, - info->tx_buf_list_ex, - info->tx_buf_count) < 0 || - alloc_tmp_rx_buf(info) < 0 ) { - printk("%s(%d):%s Can't allocate DMA buffer memory\n", - __FILE__,__LINE__, info->device_name); - return -ENOMEM; - } - - rx_reset_buffers( info ); - - return 0; -} - -/* Allocate DMA buffers for the transmit and receive descriptor lists. - */ -static int alloc_buf_list(SLMP_INFO *info) -{ - unsigned int i; - - /* build list in adapter shared memory */ - info->buffer_list = info->memory_base + info->port_array[0]->last_mem_alloc; - info->buffer_list_phys = info->port_array[0]->last_mem_alloc; - info->port_array[0]->last_mem_alloc += BUFFERLISTSIZE; - - memset(info->buffer_list, 0, BUFFERLISTSIZE); - - /* Save virtual address pointers to the receive and */ - /* transmit buffer lists. (Receive 1st). These pointers will */ - /* be used by the processor to access the lists. */ - info->rx_buf_list = (SCADESC *)info->buffer_list; - - info->tx_buf_list = (SCADESC *)info->buffer_list; - info->tx_buf_list += info->rx_buf_count; - - /* Build links for circular buffer entry lists (tx and rx) - * - * Note: links are physical addresses read by the SCA device - * to determine the next buffer entry to use. - */ - - for ( i = 0; i < info->rx_buf_count; i++ ) { - /* calculate and store physical address of this buffer entry */ - info->rx_buf_list_ex[i].phys_entry = - info->buffer_list_phys + (i * sizeof(SCABUFSIZE)); - - /* calculate and store physical address of */ - /* next entry in cirular list of entries */ - info->rx_buf_list[i].next = info->buffer_list_phys; - if ( i < info->rx_buf_count - 1 ) - info->rx_buf_list[i].next += (i + 1) * sizeof(SCADESC); - - info->rx_buf_list[i].length = SCABUFSIZE; - } - - for ( i = 0; i < info->tx_buf_count; i++ ) { - /* calculate and store physical address of this buffer entry */ - info->tx_buf_list_ex[i].phys_entry = info->buffer_list_phys + - ((info->rx_buf_count + i) * sizeof(SCADESC)); - - /* calculate and store physical address of */ - /* next entry in cirular list of entries */ - - info->tx_buf_list[i].next = info->buffer_list_phys + - info->rx_buf_count * sizeof(SCADESC); - - if ( i < info->tx_buf_count - 1 ) - info->tx_buf_list[i].next += (i + 1) * sizeof(SCADESC); - } - - return 0; -} - -/* Allocate the frame DMA buffers used by the specified buffer list. - */ -static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *buf_list,SCADESC_EX *buf_list_ex,int count) -{ - int i; - unsigned long phys_addr; - - for ( i = 0; i < count; i++ ) { - buf_list_ex[i].virt_addr = info->memory_base + info->port_array[0]->last_mem_alloc; - phys_addr = info->port_array[0]->last_mem_alloc; - info->port_array[0]->last_mem_alloc += SCABUFSIZE; - - buf_list[i].buf_ptr = (unsigned short)phys_addr; - buf_list[i].buf_base = (unsigned char)(phys_addr >> 16); - } - - return 0; -} - -static void free_dma_bufs(SLMP_INFO *info) -{ - info->buffer_list = NULL; - info->rx_buf_list = NULL; - info->tx_buf_list = NULL; -} - -/* allocate buffer large enough to hold max_frame_size. - * This buffer is used to pass an assembled frame to the line discipline. - */ -static int alloc_tmp_rx_buf(SLMP_INFO *info) -{ - info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); - if (info->tmp_rx_buf == NULL) - return -ENOMEM; - return 0; -} - -static void free_tmp_rx_buf(SLMP_INFO *info) -{ - kfree(info->tmp_rx_buf); - info->tmp_rx_buf = NULL; -} - -static int claim_resources(SLMP_INFO *info) -{ - if (request_mem_region(info->phys_memory_base,SCA_MEM_SIZE,"synclinkmp") == NULL) { - printk( "%s(%d):%s mem addr conflict, Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->shared_mem_requested = true; - - if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) { - printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_lcr_base); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->lcr_mem_requested = true; - - if (request_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE,"synclinkmp") == NULL) { - printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_sca_base); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->sca_base_requested = true; - - if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE,"synclinkmp") == NULL) { - printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_statctrl_base); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->sca_statctrl_requested = true; - - info->memory_base = ioremap_nocache(info->phys_memory_base, - SCA_MEM_SIZE); - if (!info->memory_base) { - printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base ); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - - info->lcr_base = ioremap_nocache(info->phys_lcr_base, PAGE_SIZE); - if (!info->lcr_base) { - printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - info->lcr_base += info->lcr_offset; - - info->sca_base = ioremap_nocache(info->phys_sca_base, PAGE_SIZE); - if (!info->sca_base) { - printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_sca_base ); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - info->sca_base += info->sca_offset; - - info->statctrl_base = ioremap_nocache(info->phys_statctrl_base, - PAGE_SIZE); - if (!info->statctrl_base) { - printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_statctrl_base ); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - info->statctrl_base += info->statctrl_offset; - - if ( !memory_test(info) ) { - printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n", - __FILE__,__LINE__,info->device_name, info->phys_memory_base ); - info->init_error = DiagStatus_MemoryError; - goto errout; - } - - return 0; - -errout: - release_resources( info ); - return -ENODEV; -} - -static void release_resources(SLMP_INFO *info) -{ - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s release_resources() entry\n", - __FILE__,__LINE__,info->device_name ); - - if ( info->irq_requested ) { - free_irq(info->irq_level, info); - info->irq_requested = false; - } - - if ( info->shared_mem_requested ) { - release_mem_region(info->phys_memory_base,SCA_MEM_SIZE); - info->shared_mem_requested = false; - } - if ( info->lcr_mem_requested ) { - release_mem_region(info->phys_lcr_base + info->lcr_offset,128); - info->lcr_mem_requested = false; - } - if ( info->sca_base_requested ) { - release_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE); - info->sca_base_requested = false; - } - if ( info->sca_statctrl_requested ) { - release_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE); - info->sca_statctrl_requested = false; - } - - if (info->memory_base){ - iounmap(info->memory_base); - info->memory_base = NULL; - } - - if (info->sca_base) { - iounmap(info->sca_base - info->sca_offset); - info->sca_base=NULL; - } - - if (info->statctrl_base) { - iounmap(info->statctrl_base - info->statctrl_offset); - info->statctrl_base=NULL; - } - - if (info->lcr_base){ - iounmap(info->lcr_base - info->lcr_offset); - info->lcr_base = NULL; - } - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s release_resources() exit\n", - __FILE__,__LINE__,info->device_name ); -} - -/* Add the specified device instance data structure to the - * global linked list of devices and increment the device count. - */ -static void add_device(SLMP_INFO *info) -{ - info->next_device = NULL; - info->line = synclinkmp_device_count; - sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num); - - if (info->line < MAX_DEVICES) { - if (maxframe[info->line]) - info->max_frame_size = maxframe[info->line]; - } - - synclinkmp_device_count++; - - if ( !synclinkmp_device_list ) - synclinkmp_device_list = info; - else { - SLMP_INFO *current_dev = synclinkmp_device_list; - while( current_dev->next_device ) - current_dev = current_dev->next_device; - current_dev->next_device = info; - } - - if ( info->max_frame_size < 4096 ) - info->max_frame_size = 4096; - else if ( info->max_frame_size > 65535 ) - info->max_frame_size = 65535; - - printk( "SyncLink MultiPort %s: " - "Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n", - info->device_name, - info->phys_sca_base, - info->phys_memory_base, - info->phys_statctrl_base, - info->phys_lcr_base, - info->irq_level, - info->max_frame_size ); - -#if SYNCLINK_GENERIC_HDLC - hdlcdev_init(info); -#endif -} - -static const struct tty_port_operations port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* Allocate and initialize a device instance structure - * - * Return Value: pointer to SLMP_INFO if success, otherwise NULL - */ -static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) -{ - SLMP_INFO *info; - - info = kzalloc(sizeof(SLMP_INFO), - GFP_KERNEL); - - if (!info) { - printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n", - __FILE__,__LINE__, adapter_num, port_num); - } else { - tty_port_init(&info->port); - info->port.ops = &port_ops; - info->magic = MGSL_MAGIC; - INIT_WORK(&info->task, bh_handler); - info->max_frame_size = 4096; - info->port.close_delay = 5*HZ/10; - info->port.closing_wait = 30*HZ; - init_waitqueue_head(&info->status_event_wait_q); - init_waitqueue_head(&info->event_wait_q); - spin_lock_init(&info->netlock); - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; - info->adapter_num = adapter_num; - info->port_num = port_num; - - /* Copy configuration info to device instance data */ - info->irq_level = pdev->irq; - info->phys_lcr_base = pci_resource_start(pdev,0); - info->phys_sca_base = pci_resource_start(pdev,2); - info->phys_memory_base = pci_resource_start(pdev,3); - info->phys_statctrl_base = pci_resource_start(pdev,4); - - /* Because veremap only works on page boundaries we must map - * a larger area than is actually implemented for the LCR - * memory range. We map a full page starting at the page boundary. - */ - info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); - info->phys_lcr_base &= ~(PAGE_SIZE-1); - - info->sca_offset = info->phys_sca_base & (PAGE_SIZE-1); - info->phys_sca_base &= ~(PAGE_SIZE-1); - - info->statctrl_offset = info->phys_statctrl_base & (PAGE_SIZE-1); - info->phys_statctrl_base &= ~(PAGE_SIZE-1); - - info->bus_type = MGSL_BUS_TYPE_PCI; - info->irq_flags = IRQF_SHARED; - - setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info); - setup_timer(&info->status_timer, status_timeout, - (unsigned long)info); - - /* Store the PCI9050 misc control register value because a flaw - * in the PCI9050 prevents LCR registers from being read if - * BIOS assigns an LCR base address with bit 7 set. - * - * Only the misc control register is accessed for which only - * write access is needed, so set an initial value and change - * bits to the device instance data as we write the value - * to the actual misc control register. - */ - info->misc_ctrl_value = 0x087e4546; - - /* initial port state is unknown - if startup errors - * occur, init_error will be set to indicate the - * problem. Once the port is fully initialized, - * this value will be set to 0 to indicate the - * port is available. - */ - info->init_error = -1; - } - - return info; -} - -static void device_init(int adapter_num, struct pci_dev *pdev) -{ - SLMP_INFO *port_array[SCA_MAX_PORTS]; - int port; - - /* allocate device instances for up to SCA_MAX_PORTS devices */ - for ( port = 0; port < SCA_MAX_PORTS; ++port ) { - port_array[port] = alloc_dev(adapter_num,port,pdev); - if( port_array[port] == NULL ) { - for ( --port; port >= 0; --port ) - kfree(port_array[port]); - return; - } - } - - /* give copy of port_array to all ports and add to device list */ - for ( port = 0; port < SCA_MAX_PORTS; ++port ) { - memcpy(port_array[port]->port_array,port_array,sizeof(port_array)); - add_device( port_array[port] ); - spin_lock_init(&port_array[port]->lock); - } - - /* Allocate and claim adapter resources */ - if ( !claim_resources(port_array[0]) ) { - - alloc_dma_bufs(port_array[0]); - - /* copy resource information from first port to others */ - for ( port = 1; port < SCA_MAX_PORTS; ++port ) { - port_array[port]->lock = port_array[0]->lock; - port_array[port]->irq_level = port_array[0]->irq_level; - port_array[port]->memory_base = port_array[0]->memory_base; - port_array[port]->sca_base = port_array[0]->sca_base; - port_array[port]->statctrl_base = port_array[0]->statctrl_base; - port_array[port]->lcr_base = port_array[0]->lcr_base; - alloc_dma_bufs(port_array[port]); - } - - if ( request_irq(port_array[0]->irq_level, - synclinkmp_interrupt, - port_array[0]->irq_flags, - port_array[0]->device_name, - port_array[0]) < 0 ) { - printk( "%s(%d):%s Cant request interrupt, IRQ=%d\n", - __FILE__,__LINE__, - port_array[0]->device_name, - port_array[0]->irq_level ); - } - else { - port_array[0]->irq_requested = true; - adapter_test(port_array[0]); - } - } -} - -static const struct tty_operations ops = { - .open = open, - .close = close, - .write = write, - .put_char = put_char, - .flush_chars = flush_chars, - .write_room = write_room, - .chars_in_buffer = chars_in_buffer, - .flush_buffer = flush_buffer, - .ioctl = ioctl, - .throttle = throttle, - .unthrottle = unthrottle, - .send_xchar = send_xchar, - .break_ctl = set_break, - .wait_until_sent = wait_until_sent, - .set_termios = set_termios, - .stop = tx_hold, - .start = tx_release, - .hangup = hangup, - .tiocmget = tiocmget, - .tiocmset = tiocmset, - .get_icount = get_icount, - .proc_fops = &synclinkmp_proc_fops, -}; - - -static void synclinkmp_cleanup(void) -{ - int rc; - SLMP_INFO *info; - SLMP_INFO *tmp; - - printk("Unloading %s %s\n", driver_name, driver_version); - - if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) - printk("%s(%d) failed to unregister tty driver err=%d\n", - __FILE__,__LINE__,rc); - put_tty_driver(serial_driver); - } - - /* reset devices */ - info = synclinkmp_device_list; - while(info) { - reset_port(info); - info = info->next_device; - } - - /* release devices */ - info = synclinkmp_device_list; - while(info) { -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - free_dma_bufs(info); - free_tmp_rx_buf(info); - if ( info->port_num == 0 ) { - if (info->sca_base) - write_reg(info, LPR, 1); /* set low power mode */ - release_resources(info); - } - tmp = info; - info = info->next_device; - kfree(tmp); - } - - pci_unregister_driver(&synclinkmp_pci_driver); -} - -/* Driver initialization entry point. - */ - -static int __init synclinkmp_init(void) -{ - int rc; - - if (break_on_load) { - synclinkmp_get_text_ptr(); - BREAKPOINT(); - } - - printk("%s %s\n", driver_name, driver_version); - - if ((rc = pci_register_driver(&synclinkmp_pci_driver)) < 0) { - printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc); - return rc; - } - - serial_driver = alloc_tty_driver(128); - if (!serial_driver) { - rc = -ENOMEM; - goto error; - } - - /* Initialize the tty_driver structure */ - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = "synclinkmp"; - serial_driver->name = "ttySLM"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - 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->init_termios.c_ispeed = 9600; - serial_driver->init_termios.c_ospeed = 9600; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &ops); - if ((rc = tty_register_driver(serial_driver)) < 0) { - printk("%s(%d):Couldn't register serial driver\n", - __FILE__,__LINE__); - put_tty_driver(serial_driver); - serial_driver = NULL; - goto error; - } - - printk("%s %s, tty major#%d\n", - driver_name, driver_version, - serial_driver->major); - - return 0; - -error: - synclinkmp_cleanup(); - return rc; -} - -static void __exit synclinkmp_exit(void) -{ - synclinkmp_cleanup(); -} - -module_init(synclinkmp_init); -module_exit(synclinkmp_exit); - -/* Set the port for internal loopback mode. - * The TxCLK and RxCLK signals are generated from the BRG and - * the TxD is looped back to the RxD internally. - */ -static void enable_loopback(SLMP_INFO *info, int enable) -{ - if (enable) { - /* MD2 (Mode Register 2) - * 01..00 CNCT<1..0> Channel Connection 11=Local Loopback - */ - write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) | (BIT1 + BIT0))); - - /* degate external TxC clock source */ - info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); - write_control_reg(info); - - /* RXS/TXS (Rx/Tx clock source) - * 07 Reserved, must be 0 - * 06..04 Clock Source, 100=BRG - * 03..00 Clock Divisor, 0000=1 - */ - write_reg(info, RXS, 0x40); - write_reg(info, TXS, 0x40); - - } else { - /* MD2 (Mode Register 2) - * 01..00 CNCT<1..0> Channel connection, 0=normal - */ - write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) & ~(BIT1 + BIT0))); - - /* RXS/TXS (Rx/Tx clock source) - * 07 Reserved, must be 0 - * 06..04 Clock Source, 000=RxC/TxC Pin - * 03..00 Clock Divisor, 0000=1 - */ - write_reg(info, RXS, 0x00); - write_reg(info, TXS, 0x00); - } - - /* set LinkSpeed if available, otherwise default to 2Mbps */ - if (info->params.clock_speed) - set_rate(info, info->params.clock_speed); - else - set_rate(info, 3686400); -} - -/* Set the baud rate register to the desired speed - * - * data_rate data rate of clock in bits per second - * A data rate of 0 disables the AUX clock. - */ -static void set_rate( SLMP_INFO *info, u32 data_rate ) -{ - u32 TMCValue; - unsigned char BRValue; - u32 Divisor=0; - - /* fBRG = fCLK/(TMC * 2^BR) - */ - if (data_rate != 0) { - Divisor = 14745600/data_rate; - if (!Divisor) - Divisor = 1; - - TMCValue = Divisor; - - BRValue = 0; - if (TMCValue != 1 && TMCValue != 2) { - /* BRValue of 0 provides 50/50 duty cycle *only* when - * TMCValue is 1 or 2. BRValue of 1 to 9 always provides - * 50/50 duty cycle. - */ - BRValue = 1; - TMCValue >>= 1; - } - - /* while TMCValue is too big for TMC register, divide - * by 2 and increment BR exponent. - */ - for(; TMCValue > 256 && BRValue < 10; BRValue++) - TMCValue >>= 1; - - write_reg(info, TXS, - (unsigned char)((read_reg(info, TXS) & 0xf0) | BRValue)); - write_reg(info, RXS, - (unsigned char)((read_reg(info, RXS) & 0xf0) | BRValue)); - write_reg(info, TMC, (unsigned char)TMCValue); - } - else { - write_reg(info, TXS,0); - write_reg(info, RXS,0); - write_reg(info, TMC, 0); - } -} - -/* Disable receiver - */ -static void rx_stop(SLMP_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):%s rx_stop()\n", - __FILE__,__LINE__, info->device_name ); - - write_reg(info, CMD, RXRESET); - - info->ie0_value &= ~RXRDYE; - write_reg(info, IE0, info->ie0_value); /* disable Rx data interrupts */ - - write_reg(info, RXDMA + DSR, 0); /* disable Rx DMA */ - write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */ - write_reg(info, RXDMA + DIR, 0); /* disable Rx DMA interrupts */ - - info->rx_enabled = false; - info->rx_overflow = false; -} - -/* enable the receiver - */ -static void rx_start(SLMP_INFO *info) -{ - int i; - - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):%s rx_start()\n", - __FILE__,__LINE__, info->device_name ); - - write_reg(info, CMD, RXRESET); - - if ( info->params.mode == MGSL_MODE_HDLC ) { - /* HDLC, disabe IRQ on rxdata */ - info->ie0_value &= ~RXRDYE; - write_reg(info, IE0, info->ie0_value); - - /* Reset all Rx DMA buffers and program rx dma */ - write_reg(info, RXDMA + DSR, 0); /* disable Rx DMA */ - write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */ - - for (i = 0; i < info->rx_buf_count; i++) { - info->rx_buf_list[i].status = 0xff; - - // throttle to 4 shared memory writes at a time to prevent - // hogging local bus (keep latency time for DMA requests low). - if (!(i % 4)) - read_status_reg(info); - } - info->current_rx_buf = 0; - - /* set current/1st descriptor address */ - write_reg16(info, RXDMA + CDA, - info->rx_buf_list_ex[0].phys_entry); - - /* set new last rx descriptor address */ - write_reg16(info, RXDMA + EDA, - info->rx_buf_list_ex[info->rx_buf_count - 1].phys_entry); - - /* set buffer length (shared by all rx dma data buffers) */ - write_reg16(info, RXDMA + BFL, SCABUFSIZE); - - write_reg(info, RXDMA + DIR, 0x60); /* enable Rx DMA interrupts (EOM/BOF) */ - write_reg(info, RXDMA + DSR, 0xf2); /* clear Rx DMA IRQs, enable Rx DMA */ - } else { - /* async, enable IRQ on rxdata */ - info->ie0_value |= RXRDYE; - write_reg(info, IE0, info->ie0_value); - } - - write_reg(info, CMD, RXENABLE); - - info->rx_overflow = false; - info->rx_enabled = true; -} - -/* Enable the transmitter and send a transmit frame if - * one is loaded in the DMA buffers. - */ -static void tx_start(SLMP_INFO *info) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):%s tx_start() tx_count=%d\n", - __FILE__,__LINE__, info->device_name,info->tx_count ); - - if (!info->tx_enabled ) { - write_reg(info, CMD, TXRESET); - write_reg(info, CMD, TXENABLE); - info->tx_enabled = true; - } - - if ( info->tx_count ) { - - /* If auto RTS enabled and RTS is inactive, then assert */ - /* RTS and set a flag indicating that the driver should */ - /* negate RTS when the transmission completes. */ - - info->drop_rts_on_tx_done = false; - - if (info->params.mode != MGSL_MODE_ASYNC) { - - if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { - get_signals( info ); - if ( !(info->serial_signals & SerialSignal_RTS) ) { - info->serial_signals |= SerialSignal_RTS; - set_signals( info ); - info->drop_rts_on_tx_done = true; - } - } - - write_reg16(info, TRC0, - (unsigned short)(((tx_negate_fifo_level-1)<<8) + tx_active_fifo_level)); - - write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ - write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ - - /* set TX CDA (current descriptor address) */ - write_reg16(info, TXDMA + CDA, - info->tx_buf_list_ex[0].phys_entry); - - /* set TX EDA (last descriptor address) */ - write_reg16(info, TXDMA + EDA, - info->tx_buf_list_ex[info->last_tx_buf].phys_entry); - - /* enable underrun IRQ */ - info->ie1_value &= ~IDLE; - info->ie1_value |= UDRN; - write_reg(info, IE1, info->ie1_value); - write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); - - write_reg(info, TXDMA + DIR, 0x40); /* enable Tx DMA interrupts (EOM) */ - write_reg(info, TXDMA + DSR, 0xf2); /* clear Tx DMA IRQs, enable Tx DMA */ - - mod_timer(&info->tx_timer, jiffies + - msecs_to_jiffies(5000)); - } - else { - tx_load_fifo(info); - /* async, enable IRQ on txdata */ - info->ie0_value |= TXRDYE; - write_reg(info, IE0, info->ie0_value); - } - - info->tx_active = true; - } -} - -/* stop the transmitter and DMA - */ -static void tx_stop( SLMP_INFO *info ) -{ - if (debug_level >= DEBUG_LEVEL_ISR) - printk("%s(%d):%s tx_stop()\n", - __FILE__,__LINE__, info->device_name ); - - del_timer(&info->tx_timer); - - write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ - write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ - - write_reg(info, CMD, TXRESET); - - info->ie1_value &= ~(UDRN + IDLE); - write_reg(info, IE1, info->ie1_value); /* disable tx status interrupts */ - write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); /* clear pending */ - - info->ie0_value &= ~TXRDYE; - write_reg(info, IE0, info->ie0_value); /* disable tx data interrupts */ - - info->tx_enabled = false; - info->tx_active = false; -} - -/* Fill the transmit FIFO until the FIFO is full or - * there is no more data to load. - */ -static void tx_load_fifo(SLMP_INFO *info) -{ - u8 TwoBytes[2]; - - /* do nothing is now tx data available and no XON/XOFF pending */ - - if ( !info->tx_count && !info->x_char ) - return; - - /* load the Transmit FIFO until FIFOs full or all data sent */ - - while( info->tx_count && (read_reg(info,SR0) & BIT1) ) { - - /* there is more space in the transmit FIFO and */ - /* there is more data in transmit buffer */ - - if ( (info->tx_count > 1) && !info->x_char ) { - /* write 16-bits */ - TwoBytes[0] = info->tx_buf[info->tx_get++]; - if (info->tx_get >= info->max_frame_size) - info->tx_get -= info->max_frame_size; - TwoBytes[1] = info->tx_buf[info->tx_get++]; - if (info->tx_get >= info->max_frame_size) - info->tx_get -= info->max_frame_size; - - write_reg16(info, TRB, *((u16 *)TwoBytes)); - - info->tx_count -= 2; - info->icount.tx += 2; - } else { - /* only 1 byte left to transmit or 1 FIFO slot left */ - - if (info->x_char) { - /* transmit pending high priority char */ - write_reg(info, TRB, info->x_char); - info->x_char = 0; - } else { - write_reg(info, TRB, info->tx_buf[info->tx_get++]); - if (info->tx_get >= info->max_frame_size) - info->tx_get -= info->max_frame_size; - info->tx_count--; - } - info->icount.tx++; - } - } -} - -/* Reset a port to a known state - */ -static void reset_port(SLMP_INFO *info) -{ - if (info->sca_base) { - - tx_stop(info); - rx_stop(info); - - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); - set_signals(info); - - /* disable all port interrupts */ - info->ie0_value = 0; - info->ie1_value = 0; - info->ie2_value = 0; - write_reg(info, IE0, info->ie0_value); - write_reg(info, IE1, info->ie1_value); - write_reg(info, IE2, info->ie2_value); - - write_reg(info, CMD, CHRESET); - } -} - -/* Reset all the ports to a known state. - */ -static void reset_adapter(SLMP_INFO *info) -{ - int i; - - for ( i=0; i < SCA_MAX_PORTS; ++i) { - if (info->port_array[i]) - reset_port(info->port_array[i]); - } -} - -/* Program port for asynchronous communications. - */ -static void async_mode(SLMP_INFO *info) -{ - - unsigned char RegValue; - - tx_stop(info); - rx_stop(info); - - /* MD0, Mode Register 0 - * - * 07..05 PRCTL<2..0>, Protocol Mode, 000=async - * 04 AUTO, Auto-enable (RTS/CTS/DCD) - * 03 Reserved, must be 0 - * 02 CRCCC, CRC Calculation, 0=disabled - * 01..00 STOP<1..0> Stop bits (00=1,10=2) - * - * 0000 0000 - */ - RegValue = 0x00; - if (info->params.stop_bits != 1) - RegValue |= BIT1; - write_reg(info, MD0, RegValue); - - /* MD1, Mode Register 1 - * - * 07..06 BRATE<1..0>, bit rate, 00=1/1 01=1/16 10=1/32 11=1/64 - * 05..04 TXCHR<1..0>, tx char size, 00=8 bits,01=7,10=6,11=5 - * 03..02 RXCHR<1..0>, rx char size - * 01..00 PMPM<1..0>, Parity mode, 00=none 10=even 11=odd - * - * 0100 0000 - */ - RegValue = 0x40; - switch (info->params.data_bits) { - case 7: RegValue |= BIT4 + BIT2; break; - case 6: RegValue |= BIT5 + BIT3; break; - case 5: RegValue |= BIT5 + BIT4 + BIT3 + BIT2; break; - } - if (info->params.parity != ASYNC_PARITY_NONE) { - RegValue |= BIT1; - if (info->params.parity == ASYNC_PARITY_ODD) - RegValue |= BIT0; - } - write_reg(info, MD1, RegValue); - - /* MD2, Mode Register 2 - * - * 07..02 Reserved, must be 0 - * 01..00 CNCT<1..0> Channel connection, 00=normal 11=local loopback - * - * 0000 0000 - */ - RegValue = 0x00; - if (info->params.loopback) - RegValue |= (BIT1 + BIT0); - write_reg(info, MD2, RegValue); - - /* RXS, Receive clock source - * - * 07 Reserved, must be 0 - * 06..04 RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL - * 03..00 RXBR<3..0>, rate divisor, 0000=1 - */ - RegValue=BIT6; - write_reg(info, RXS, RegValue); - - /* TXS, Transmit clock source - * - * 07 Reserved, must be 0 - * 06..04 RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock - * 03..00 RXBR<3..0>, rate divisor, 0000=1 - */ - RegValue=BIT6; - write_reg(info, TXS, RegValue); - - /* Control Register - * - * 6,4,2,0 CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out - */ - info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); - write_control_reg(info); - - tx_set_idle(info); - - /* RRC Receive Ready Control 0 - * - * 07..05 Reserved, must be 0 - * 04..00 RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte - */ - write_reg(info, RRC, 0x00); - - /* TRC0 Transmit Ready Control 0 - * - * 07..05 Reserved, must be 0 - * 04..00 TRC<4..0> Tx FIFO trigger active 0x10 = 16 bytes - */ - write_reg(info, TRC0, 0x10); - - /* TRC1 Transmit Ready Control 1 - * - * 07..05 Reserved, must be 0 - * 04..00 TRC<4..0> Tx FIFO trigger inactive 0x1e = 31 bytes (full-1) - */ - write_reg(info, TRC1, 0x1e); - - /* CTL, MSCI control register - * - * 07..06 Reserved, set to 0 - * 05 UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC) - * 04 IDLC, idle control, 0=mark 1=idle register - * 03 BRK, break, 0=off 1 =on (async) - * 02 SYNCLD, sync char load enable (BSC) 1=enabled - * 01 GOP, go active on poll (LOOP mode) 1=enabled - * 00 RTS, RTS output control, 0=active 1=inactive - * - * 0001 0001 - */ - RegValue = 0x10; - if (!(info->serial_signals & SerialSignal_RTS)) - RegValue |= 0x01; - write_reg(info, CTL, RegValue); - - /* enable status interrupts */ - info->ie0_value |= TXINTE + RXINTE; - write_reg(info, IE0, info->ie0_value); - - /* enable break detect interrupt */ - info->ie1_value = BRKD; - write_reg(info, IE1, info->ie1_value); - - /* enable rx overrun interrupt */ - info->ie2_value = OVRN; - write_reg(info, IE2, info->ie2_value); - - set_rate( info, info->params.data_rate * 16 ); -} - -/* Program the SCA for HDLC communications. - */ -static void hdlc_mode(SLMP_INFO *info) -{ - unsigned char RegValue; - u32 DpllDivisor; - - // Can't use DPLL because SCA outputs recovered clock on RxC when - // DPLL mode selected. This causes output contention with RxC receiver. - // Use of DPLL would require external hardware to disable RxC receiver - // when DPLL mode selected. - info->params.flags &= ~(HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL); - - /* disable DMA interrupts */ - write_reg(info, TXDMA + DIR, 0); - write_reg(info, RXDMA + DIR, 0); - - /* MD0, Mode Register 0 - * - * 07..05 PRCTL<2..0>, Protocol Mode, 100=HDLC - * 04 AUTO, Auto-enable (RTS/CTS/DCD) - * 03 Reserved, must be 0 - * 02 CRCCC, CRC Calculation, 1=enabled - * 01 CRC1, CRC selection, 0=CRC-16,1=CRC-CCITT-16 - * 00 CRC0, CRC initial value, 1 = all 1s - * - * 1000 0001 - */ - RegValue = 0x81; - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - RegValue |= BIT4; - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - RegValue |= BIT4; - if (info->params.crc_type == HDLC_CRC_16_CCITT) - RegValue |= BIT2 + BIT1; - write_reg(info, MD0, RegValue); - - /* MD1, Mode Register 1 - * - * 07..06 ADDRS<1..0>, Address detect, 00=no addr check - * 05..04 TXCHR<1..0>, tx char size, 00=8 bits - * 03..02 RXCHR<1..0>, rx char size, 00=8 bits - * 01..00 PMPM<1..0>, Parity mode, 00=no parity - * - * 0000 0000 - */ - RegValue = 0x00; - write_reg(info, MD1, RegValue); - - /* MD2, Mode Register 2 - * - * 07 NRZFM, 0=NRZ, 1=FM - * 06..05 CODE<1..0> Encoding, 00=NRZ - * 04..03 DRATE<1..0> DPLL Divisor, 00=8 - * 02 Reserved, must be 0 - * 01..00 CNCT<1..0> Channel connection, 0=normal - * - * 0000 0000 - */ - RegValue = 0x00; - switch(info->params.encoding) { - case HDLC_ENCODING_NRZI: RegValue |= BIT5; break; - case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT7 + BIT5; break; /* aka FM1 */ - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT7 + BIT6; break; /* aka FM0 */ - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT7; break; /* aka Manchester */ -#if 0 - case HDLC_ENCODING_NRZB: /* not supported */ - case HDLC_ENCODING_NRZI_MARK: /* not supported */ - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: /* not supported */ -#endif - } - if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { - DpllDivisor = 16; - RegValue |= BIT3; - } else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { - DpllDivisor = 8; - } else { - DpllDivisor = 32; - RegValue |= BIT4; - } - write_reg(info, MD2, RegValue); - - - /* RXS, Receive clock source - * - * 07 Reserved, must be 0 - * 06..04 RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL - * 03..00 RXBR<3..0>, rate divisor, 0000=1 - */ - RegValue=0; - if (info->params.flags & HDLC_FLAG_RXC_BRG) - RegValue |= BIT6; - if (info->params.flags & HDLC_FLAG_RXC_DPLL) - RegValue |= BIT6 + BIT5; - write_reg(info, RXS, RegValue); - - /* TXS, Transmit clock source - * - * 07 Reserved, must be 0 - * 06..04 RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock - * 03..00 RXBR<3..0>, rate divisor, 0000=1 - */ - RegValue=0; - if (info->params.flags & HDLC_FLAG_TXC_BRG) - RegValue |= BIT6; - if (info->params.flags & HDLC_FLAG_TXC_DPLL) - RegValue |= BIT6 + BIT5; - write_reg(info, TXS, RegValue); - - if (info->params.flags & HDLC_FLAG_RXC_DPLL) - set_rate(info, info->params.clock_speed * DpllDivisor); - else - set_rate(info, info->params.clock_speed); - - /* GPDATA (General Purpose I/O Data Register) - * - * 6,4,2,0 CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out - */ - if (info->params.flags & HDLC_FLAG_TXC_BRG) - info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); - else - info->port_array[0]->ctrlreg_value &= ~(BIT0 << (info->port_num * 2)); - write_control_reg(info); - - /* RRC Receive Ready Control 0 - * - * 07..05 Reserved, must be 0 - * 04..00 RRC<4..0> Rx FIFO trigger active - */ - write_reg(info, RRC, rx_active_fifo_level); - - /* TRC0 Transmit Ready Control 0 - * - * 07..05 Reserved, must be 0 - * 04..00 TRC<4..0> Tx FIFO trigger active - */ - write_reg(info, TRC0, tx_active_fifo_level); - - /* TRC1 Transmit Ready Control 1 - * - * 07..05 Reserved, must be 0 - * 04..00 TRC<4..0> Tx FIFO trigger inactive 0x1f = 32 bytes (full) - */ - write_reg(info, TRC1, (unsigned char)(tx_negate_fifo_level - 1)); - - /* DMR, DMA Mode Register - * - * 07..05 Reserved, must be 0 - * 04 TMOD, Transfer Mode: 1=chained-block - * 03 Reserved, must be 0 - * 02 NF, Number of Frames: 1=multi-frame - * 01 CNTE, Frame End IRQ Counter enable: 0=disabled - * 00 Reserved, must be 0 - * - * 0001 0100 - */ - write_reg(info, TXDMA + DMR, 0x14); - write_reg(info, RXDMA + DMR, 0x14); - - /* Set chain pointer base (upper 8 bits of 24 bit addr) */ - write_reg(info, RXDMA + CPB, - (unsigned char)(info->buffer_list_phys >> 16)); - - /* Set chain pointer base (upper 8 bits of 24 bit addr) */ - write_reg(info, TXDMA + CPB, - (unsigned char)(info->buffer_list_phys >> 16)); - - /* enable status interrupts. other code enables/disables - * the individual sources for these two interrupt classes. - */ - info->ie0_value |= TXINTE + RXINTE; - write_reg(info, IE0, info->ie0_value); - - /* CTL, MSCI control register - * - * 07..06 Reserved, set to 0 - * 05 UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC) - * 04 IDLC, idle control, 0=mark 1=idle register - * 03 BRK, break, 0=off 1 =on (async) - * 02 SYNCLD, sync char load enable (BSC) 1=enabled - * 01 GOP, go active on poll (LOOP mode) 1=enabled - * 00 RTS, RTS output control, 0=active 1=inactive - * - * 0001 0001 - */ - RegValue = 0x10; - if (!(info->serial_signals & SerialSignal_RTS)) - RegValue |= 0x01; - write_reg(info, CTL, RegValue); - - /* preamble not supported ! */ - - tx_set_idle(info); - tx_stop(info); - rx_stop(info); - - set_rate(info, info->params.clock_speed); - - if (info->params.loopback) - enable_loopback(info,1); -} - -/* Set the transmit HDLC idle mode - */ -static void tx_set_idle(SLMP_INFO *info) -{ - unsigned char RegValue = 0xff; - - /* Map API idle mode to SCA register bits */ - switch(info->idle_mode) { - case HDLC_TXIDLE_FLAGS: RegValue = 0x7e; break; - case HDLC_TXIDLE_ALT_ZEROS_ONES: RegValue = 0xaa; break; - case HDLC_TXIDLE_ZEROS: RegValue = 0x00; break; - case HDLC_TXIDLE_ONES: RegValue = 0xff; break; - case HDLC_TXIDLE_ALT_MARK_SPACE: RegValue = 0xaa; break; - case HDLC_TXIDLE_SPACE: RegValue = 0x00; break; - case HDLC_TXIDLE_MARK: RegValue = 0xff; break; - } - - write_reg(info, IDL, RegValue); -} - -/* Query the adapter for the state of the V24 status (input) signals. - */ -static void get_signals(SLMP_INFO *info) -{ - u16 status = read_reg(info, SR3); - u16 gpstatus = read_status_reg(info); - u16 testbit; - - /* clear all serial signals except DTR and RTS */ - info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; - - /* set serial signal bits to reflect MISR */ - - if (!(status & BIT3)) - info->serial_signals |= SerialSignal_CTS; - - if ( !(status & BIT2)) - info->serial_signals |= SerialSignal_DCD; - - testbit = BIT1 << (info->port_num * 2); // Port 0..3 RI is GPDATA<1,3,5,7> - if (!(gpstatus & testbit)) - info->serial_signals |= SerialSignal_RI; - - testbit = BIT0 << (info->port_num * 2); // Port 0..3 DSR is GPDATA<0,2,4,6> - if (!(gpstatus & testbit)) - info->serial_signals |= SerialSignal_DSR; -} - -/* Set the state of DTR and RTS based on contents of - * serial_signals member of device context. - */ -static void set_signals(SLMP_INFO *info) -{ - unsigned char RegValue; - u16 EnableBit; - - RegValue = read_reg(info, CTL); - if (info->serial_signals & SerialSignal_RTS) - RegValue &= ~BIT0; - else - RegValue |= BIT0; - write_reg(info, CTL, RegValue); - - // Port 0..3 DTR is ctrl reg <1,3,5,7> - EnableBit = BIT1 << (info->port_num*2); - if (info->serial_signals & SerialSignal_DTR) - info->port_array[0]->ctrlreg_value &= ~EnableBit; - else - info->port_array[0]->ctrlreg_value |= EnableBit; - write_control_reg(info); -} - -/*******************/ -/* DMA Buffer Code */ -/*******************/ - -/* Set the count for all receive buffers to SCABUFSIZE - * and set the current buffer to the first buffer. This effectively - * makes all buffers free and discards any data in buffers. - */ -static void rx_reset_buffers(SLMP_INFO *info) -{ - rx_free_frame_buffers(info, 0, info->rx_buf_count - 1); -} - -/* Free the buffers used by a received frame - * - * info pointer to device instance data - * first index of 1st receive buffer of frame - * last index of last receive buffer of frame - */ -static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last) -{ - bool done = false; - - while(!done) { - /* reset current buffer for reuse */ - info->rx_buf_list[first].status = 0xff; - - if (first == last) { - done = true; - /* set new last rx descriptor address */ - write_reg16(info, RXDMA + EDA, info->rx_buf_list_ex[first].phys_entry); - } - - first++; - if (first == info->rx_buf_count) - first = 0; - } - - /* set current buffer to next buffer after last buffer of frame */ - info->current_rx_buf = first; -} - -/* Return a received frame from the receive DMA buffers. - * Only frames received without errors are returned. - * - * Return Value: true if frame returned, otherwise false - */ -static bool rx_get_frame(SLMP_INFO *info) -{ - unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ - unsigned short status; - unsigned int framesize = 0; - bool ReturnCode = false; - unsigned long flags; - struct tty_struct *tty = info->port.tty; - unsigned char addr_field = 0xff; - SCADESC *desc; - SCADESC_EX *desc_ex; - -CheckAgain: - /* assume no frame returned, set zero length */ - framesize = 0; - addr_field = 0xff; - - /* - * current_rx_buf points to the 1st buffer of the next available - * receive frame. To find the last buffer of the frame look for - * a non-zero status field in the buffer entries. (The status - * field is set by the 16C32 after completing a receive frame. - */ - StartIndex = EndIndex = info->current_rx_buf; - - for ( ;; ) { - desc = &info->rx_buf_list[EndIndex]; - desc_ex = &info->rx_buf_list_ex[EndIndex]; - - if (desc->status == 0xff) - goto Cleanup; /* current desc still in use, no frames available */ - - if (framesize == 0 && info->params.addr_filter != 0xff) - addr_field = desc_ex->virt_addr[0]; - - framesize += desc->length; - - /* Status != 0 means last buffer of frame */ - if (desc->status) - break; - - EndIndex++; - if (EndIndex == info->rx_buf_count) - EndIndex = 0; - - if (EndIndex == info->current_rx_buf) { - /* all buffers have been 'used' but none mark */ - /* the end of a frame. Reset buffers and receiver. */ - if ( info->rx_enabled ){ - spin_lock_irqsave(&info->lock,flags); - rx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } - goto Cleanup; - } - - } - - /* check status of receive frame */ - - /* frame status is byte stored after frame data - * - * 7 EOM (end of msg), 1 = last buffer of frame - * 6 Short Frame, 1 = short frame - * 5 Abort, 1 = frame aborted - * 4 Residue, 1 = last byte is partial - * 3 Overrun, 1 = overrun occurred during frame reception - * 2 CRC, 1 = CRC error detected - * - */ - status = desc->status; - - /* ignore CRC bit if not using CRC (bit is undefined) */ - /* Note:CRC is not save to data buffer */ - if (info->params.crc_type == HDLC_CRC_NONE) - status &= ~BIT2; - - if (framesize == 0 || - (addr_field != 0xff && addr_field != info->params.addr_filter)) { - /* discard 0 byte frames, this seems to occur sometime - * when remote is idling flags. - */ - rx_free_frame_buffers(info, StartIndex, EndIndex); - goto CheckAgain; - } - - if (framesize < 2) - status |= BIT6; - - if (status & (BIT6+BIT5+BIT3+BIT2)) { - /* received frame has errors, - * update counts and mark frame size as 0 - */ - if (status & BIT6) - info->icount.rxshort++; - else if (status & BIT5) - info->icount.rxabort++; - else if (status & BIT3) - info->icount.rxover++; - else - info->icount.rxcrc++; - - framesize = 0; -#if SYNCLINK_GENERIC_HDLC - { - info->netdev->stats.rx_errors++; - info->netdev->stats.rx_frame_errors++; - } -#endif - } - - if ( debug_level >= DEBUG_LEVEL_BH ) - printk("%s(%d):%s rx_get_frame() status=%04X size=%d\n", - __FILE__,__LINE__,info->device_name,status,framesize); - - if ( debug_level >= DEBUG_LEVEL_DATA ) - trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr, - min_t(int, framesize,SCABUFSIZE),0); - - if (framesize) { - if (framesize > info->max_frame_size) - info->icount.rxlong++; - else { - /* copy dma buffer(s) to contiguous intermediate buffer */ - int copy_count = framesize; - int index = StartIndex; - unsigned char *ptmp = info->tmp_rx_buf; - info->tmp_rx_buf_count = framesize; - - info->icount.rxok++; - - while(copy_count) { - int partial_count = min(copy_count,SCABUFSIZE); - memcpy( ptmp, - info->rx_buf_list_ex[index].virt_addr, - partial_count ); - ptmp += partial_count; - copy_count -= partial_count; - - if ( ++index == info->rx_buf_count ) - index = 0; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_rx(info,info->tmp_rx_buf,framesize); - else -#endif - ldisc_receive_buf(tty,info->tmp_rx_buf, - info->flag_buf, framesize); - } - } - /* Free the buffers used by this frame. */ - rx_free_frame_buffers( info, StartIndex, EndIndex ); - - ReturnCode = true; - -Cleanup: - if ( info->rx_enabled && info->rx_overflow ) { - /* Receiver is enabled, but needs to restarted due to - * rx buffer overflow. If buffers are empty, restart receiver. - */ - if (info->rx_buf_list[EndIndex].status == 0xff) { - spin_lock_irqsave(&info->lock,flags); - rx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } - } - - return ReturnCode; -} - -/* load the transmit DMA buffer with data - */ -static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count) -{ - unsigned short copy_count; - unsigned int i = 0; - SCADESC *desc; - SCADESC_EX *desc_ex; - - if ( debug_level >= DEBUG_LEVEL_DATA ) - trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1); - - /* Copy source buffer to one or more DMA buffers, starting with - * the first transmit dma buffer. - */ - for(i=0;;) - { - copy_count = min_t(unsigned short,count,SCABUFSIZE); - - desc = &info->tx_buf_list[i]; - desc_ex = &info->tx_buf_list_ex[i]; - - load_pci_memory(info, desc_ex->virt_addr,buf,copy_count); - - desc->length = copy_count; - desc->status = 0; - - buf += copy_count; - count -= copy_count; - - if (!count) - break; - - i++; - if (i >= info->tx_buf_count) - i = 0; - } - - info->tx_buf_list[i].status = 0x81; /* set EOM and EOT status */ - info->last_tx_buf = ++i; -} - -static bool register_test(SLMP_INFO *info) -{ - static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96}; - static unsigned int count = ARRAY_SIZE(testval); - unsigned int i; - bool rc = true; - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - reset_port(info); - - /* assume failure */ - info->init_error = DiagStatus_AddressFailure; - - /* Write bit patterns to various registers but do it out of */ - /* sync, then read back and verify values. */ - - for (i = 0 ; i < count ; i++) { - write_reg(info, TMC, testval[i]); - write_reg(info, IDL, testval[(i+1)%count]); - write_reg(info, SA0, testval[(i+2)%count]); - write_reg(info, SA1, testval[(i+3)%count]); - - if ( (read_reg(info, TMC) != testval[i]) || - (read_reg(info, IDL) != testval[(i+1)%count]) || - (read_reg(info, SA0) != testval[(i+2)%count]) || - (read_reg(info, SA1) != testval[(i+3)%count]) ) - { - rc = false; - break; - } - } - - reset_port(info); - spin_unlock_irqrestore(&info->lock,flags); - - return rc; -} - -static bool irq_test(SLMP_INFO *info) -{ - unsigned long timeout; - unsigned long flags; - - unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0; - - spin_lock_irqsave(&info->lock,flags); - reset_port(info); - - /* assume failure */ - info->init_error = DiagStatus_IrqFailure; - info->irq_occurred = false; - - /* setup timer0 on SCA0 to interrupt */ - - /* IER2<7..4> = timer<3..0> interrupt enables (1=enabled) */ - write_reg(info, IER2, (unsigned char)((info->port_num & 1) ? BIT6 : BIT4)); - - write_reg(info, (unsigned char)(timer + TEPR), 0); /* timer expand prescale */ - write_reg16(info, (unsigned char)(timer + TCONR), 1); /* timer constant */ - - - /* TMCS, Timer Control/Status Register - * - * 07 CMF, Compare match flag (read only) 1=match - * 06 ECMI, CMF Interrupt Enable: 1=enabled - * 05 Reserved, must be 0 - * 04 TME, Timer Enable - * 03..00 Reserved, must be 0 - * - * 0101 0000 - */ - write_reg(info, (unsigned char)(timer + TMCS), 0x50); - - spin_unlock_irqrestore(&info->lock,flags); - - timeout=100; - while( timeout-- && !info->irq_occurred ) { - msleep_interruptible(10); - } - - spin_lock_irqsave(&info->lock,flags); - reset_port(info); - spin_unlock_irqrestore(&info->lock,flags); - - return info->irq_occurred; -} - -/* initialize individual SCA device (2 ports) - */ -static bool sca_init(SLMP_INFO *info) -{ - /* set wait controller to single mem partition (low), no wait states */ - write_reg(info, PABR0, 0); /* wait controller addr boundary 0 */ - write_reg(info, PABR1, 0); /* wait controller addr boundary 1 */ - write_reg(info, WCRL, 0); /* wait controller low range */ - write_reg(info, WCRM, 0); /* wait controller mid range */ - write_reg(info, WCRH, 0); /* wait controller high range */ - - /* DPCR, DMA Priority Control - * - * 07..05 Not used, must be 0 - * 04 BRC, bus release condition: 0=all transfers complete - * 03 CCC, channel change condition: 0=every cycle - * 02..00 PR<2..0>, priority 100=round robin - * - * 00000100 = 0x04 - */ - write_reg(info, DPCR, dma_priority); - - /* DMA Master Enable, BIT7: 1=enable all channels */ - write_reg(info, DMER, 0x80); - - /* enable all interrupt classes */ - write_reg(info, IER0, 0xff); /* TxRDY,RxRDY,TxINT,RxINT (ports 0-1) */ - write_reg(info, IER1, 0xff); /* DMIB,DMIA (channels 0-3) */ - write_reg(info, IER2, 0xf0); /* TIRQ (timers 0-3) */ - - /* ITCR, interrupt control register - * 07 IPC, interrupt priority, 0=MSCI->DMA - * 06..05 IAK<1..0>, Acknowledge cycle, 00=non-ack cycle - * 04 VOS, Vector Output, 0=unmodified vector - * 03..00 Reserved, must be 0 - */ - write_reg(info, ITCR, 0); - - return true; -} - -/* initialize adapter hardware - */ -static bool init_adapter(SLMP_INFO *info) -{ - int i; - - /* Set BIT30 of Local Control Reg 0x50 to reset SCA */ - volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); - u32 readval; - - info->misc_ctrl_value |= BIT30; - *MiscCtrl = info->misc_ctrl_value; - - /* - * Force at least 170ns delay before clearing - * reset bit. Each read from LCR takes at least - * 30ns so 10 times for 300ns to be safe. - */ - for(i=0;i<10;i++) - readval = *MiscCtrl; - - info->misc_ctrl_value &= ~BIT30; - *MiscCtrl = info->misc_ctrl_value; - - /* init control reg (all DTRs off, all clksel=input) */ - info->ctrlreg_value = 0xaa; - write_control_reg(info); - - { - volatile u32 *LCR1BRDR = (u32 *)(info->lcr_base + 0x2c); - lcr1_brdr_value &= ~(BIT5 + BIT4 + BIT3); - - switch(read_ahead_count) - { - case 16: - lcr1_brdr_value |= BIT5 + BIT4 + BIT3; - break; - case 8: - lcr1_brdr_value |= BIT5 + BIT4; - break; - case 4: - lcr1_brdr_value |= BIT5 + BIT3; - break; - case 0: - lcr1_brdr_value |= BIT5; - break; - } - - *LCR1BRDR = lcr1_brdr_value; - *MiscCtrl = misc_ctrl_value; - } - - sca_init(info->port_array[0]); - sca_init(info->port_array[2]); - - return true; -} - -/* Loopback an HDLC frame to test the hardware - * interrupt and DMA functions. - */ -static bool loopback_test(SLMP_INFO *info) -{ -#define TESTFRAMESIZE 20 - - unsigned long timeout; - u16 count = TESTFRAMESIZE; - unsigned char buf[TESTFRAMESIZE]; - bool rc = false; - unsigned long flags; - - struct tty_struct *oldtty = info->port.tty; - u32 speed = info->params.clock_speed; - - info->params.clock_speed = 3686400; - info->port.tty = NULL; - - /* assume failure */ - info->init_error = DiagStatus_DmaFailure; - - /* build and send transmit frame */ - for (count = 0; count < TESTFRAMESIZE;++count) - buf[count] = (unsigned char)count; - - memset(info->tmp_rx_buf,0,TESTFRAMESIZE); - - /* program hardware for HDLC and enabled receiver */ - spin_lock_irqsave(&info->lock,flags); - hdlc_mode(info); - enable_loopback(info,1); - rx_start(info); - info->tx_count = count; - tx_load_dma_buffer(info,buf,count); - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - - /* wait for receive complete */ - /* Set a timeout for waiting for interrupt. */ - for ( timeout = 100; timeout; --timeout ) { - msleep_interruptible(10); - - if (rx_get_frame(info)) { - rc = true; - break; - } - } - - /* verify received frame length and contents */ - if (rc && - ( info->tmp_rx_buf_count != count || - memcmp(buf, info->tmp_rx_buf,count))) { - rc = false; - } - - spin_lock_irqsave(&info->lock,flags); - reset_adapter(info); - spin_unlock_irqrestore(&info->lock,flags); - - info->params.clock_speed = speed; - info->port.tty = oldtty; - - return rc; -} - -/* Perform diagnostics on hardware - */ -static int adapter_test( SLMP_INFO *info ) -{ - unsigned long flags; - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):Testing device %s\n", - __FILE__,__LINE__,info->device_name ); - - spin_lock_irqsave(&info->lock,flags); - init_adapter(info); - spin_unlock_irqrestore(&info->lock,flags); - - info->port_array[0]->port_count = 0; - - if ( register_test(info->port_array[0]) && - register_test(info->port_array[1])) { - - info->port_array[0]->port_count = 2; - - if ( register_test(info->port_array[2]) && - register_test(info->port_array[3]) ) - info->port_array[0]->port_count += 2; - } - else { - printk( "%s(%d):Register test failure for device %s Addr=%08lX\n", - __FILE__,__LINE__,info->device_name, (unsigned long)(info->phys_sca_base)); - return -ENODEV; - } - - if ( !irq_test(info->port_array[0]) || - !irq_test(info->port_array[1]) || - (info->port_count == 4 && !irq_test(info->port_array[2])) || - (info->port_count == 4 && !irq_test(info->port_array[3]))) { - printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", - __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); - return -ENODEV; - } - - if (!loopback_test(info->port_array[0]) || - !loopback_test(info->port_array[1]) || - (info->port_count == 4 && !loopback_test(info->port_array[2])) || - (info->port_count == 4 && !loopback_test(info->port_array[3]))) { - printk( "%s(%d):DMA test failure for device %s\n", - __FILE__,__LINE__,info->device_name); - return -ENODEV; - } - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):device %s passed diagnostics\n", - __FILE__,__LINE__,info->device_name ); - - info->port_array[0]->init_error = 0; - info->port_array[1]->init_error = 0; - if ( info->port_count > 2 ) { - info->port_array[2]->init_error = 0; - info->port_array[3]->init_error = 0; - } - - return 0; -} - -/* Test the shared memory on a PCI adapter. - */ -static bool memory_test(SLMP_INFO *info) -{ - static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa, - 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; - unsigned long count = ARRAY_SIZE(testval); - unsigned long i; - unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long); - unsigned long * addr = (unsigned long *)info->memory_base; - - /* Test data lines with test pattern at one location. */ - - for ( i = 0 ; i < count ; i++ ) { - *addr = testval[i]; - if ( *addr != testval[i] ) - return false; - } - - /* Test address lines with incrementing pattern over */ - /* entire address range. */ - - for ( i = 0 ; i < limit ; i++ ) { - *addr = i * 4; - addr++; - } - - addr = (unsigned long *)info->memory_base; - - for ( i = 0 ; i < limit ; i++ ) { - if ( *addr != i * 4 ) - return false; - addr++; - } - - memset( info->memory_base, 0, SCA_MEM_SIZE ); - return true; -} - -/* Load data into PCI adapter shared memory. - * - * The PCI9050 releases control of the local bus - * after completing the current read or write operation. - * - * While the PCI9050 write FIFO not empty, the - * PCI9050 treats all of the writes as a single transaction - * and does not release the bus. This causes DMA latency problems - * at high speeds when copying large data blocks to the shared memory. - * - * This function breaks a write into multiple transations by - * interleaving a read which flushes the write FIFO and 'completes' - * the write transation. This allows any pending DMA request to gain control - * of the local bus in a timely fasion. - */ -static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count) -{ - /* A load interval of 16 allows for 4 32-bit writes at */ - /* 136ns each for a maximum latency of 542ns on the local bus.*/ - - unsigned short interval = count / sca_pci_load_interval; - unsigned short i; - - for ( i = 0 ; i < interval ; i++ ) - { - memcpy(dest, src, sca_pci_load_interval); - read_status_reg(info); - dest += sca_pci_load_interval; - src += sca_pci_load_interval; - } - - memcpy(dest, src, count % sca_pci_load_interval); -} - -static void trace_block(SLMP_INFO *info,const char* data, int count, int xmit) -{ - int i; - int linecount; - if (xmit) - printk("%s tx data:\n",info->device_name); - else - printk("%s rx data:\n",info->device_name); - - while(count) { - if (count > 16) - linecount = 16; - else - linecount = count; - - for(i=0;i=040 && data[i]<=0176) - printk("%c",data[i]); - else - printk("."); - } - printk("\n"); - - data += linecount; - count -= linecount; - } -} /* end of trace_block() */ - -/* called when HDLC frame times out - * update stats and do tx completion processing - */ -static void tx_timeout(unsigned long context) -{ - SLMP_INFO *info = (SLMP_INFO*)context; - unsigned long flags; - - if ( debug_level >= DEBUG_LEVEL_INFO ) - printk( "%s(%d):%s tx_timeout()\n", - __FILE__,__LINE__,info->device_name); - if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { - info->icount.txtimeout++; - } - spin_lock_irqsave(&info->lock,flags); - info->tx_active = false; - info->tx_count = info->tx_put = info->tx_get = 0; - - spin_unlock_irqrestore(&info->lock,flags); - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - bh_transmit(info); -} - -/* called to periodically check the DSR/RI modem signal input status - */ -static void status_timeout(unsigned long context) -{ - u16 status = 0; - SLMP_INFO *info = (SLMP_INFO*)context; - unsigned long flags; - unsigned char delta; - - - spin_lock_irqsave(&info->lock,flags); - get_signals(info); - spin_unlock_irqrestore(&info->lock,flags); - - /* check for DSR/RI state change */ - - delta = info->old_signals ^ info->serial_signals; - info->old_signals = info->serial_signals; - - if (delta & SerialSignal_DSR) - status |= MISCSTATUS_DSR_LATCHED|(info->serial_signals&SerialSignal_DSR); - - if (delta & SerialSignal_RI) - status |= MISCSTATUS_RI_LATCHED|(info->serial_signals&SerialSignal_RI); - - if (delta & SerialSignal_DCD) - status |= MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD); - - if (delta & SerialSignal_CTS) - status |= MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS); - - if (status) - isr_io_pin(info,status); - - mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10)); -} - - -/* Register Access Routines - - * All registers are memory mapped - */ -#define CALC_REGADDR() \ - unsigned char * RegAddr = (unsigned char*)(info->sca_base + Addr); \ - if (info->port_num > 1) \ - RegAddr += 256; /* port 0-1 SCA0, 2-3 SCA1 */ \ - if ( info->port_num & 1) { \ - if (Addr > 0x7f) \ - RegAddr += 0x40; /* DMA access */ \ - else if (Addr > 0x1f && Addr < 0x60) \ - RegAddr += 0x20; /* MSCI access */ \ - } - - -static unsigned char read_reg(SLMP_INFO * info, unsigned char Addr) -{ - CALC_REGADDR(); - return *RegAddr; -} -static void write_reg(SLMP_INFO * info, unsigned char Addr, unsigned char Value) -{ - CALC_REGADDR(); - *RegAddr = Value; -} - -static u16 read_reg16(SLMP_INFO * info, unsigned char Addr) -{ - CALC_REGADDR(); - return *((u16 *)RegAddr); -} - -static void write_reg16(SLMP_INFO * info, unsigned char Addr, u16 Value) -{ - CALC_REGADDR(); - *((u16 *)RegAddr) = Value; -} - -static unsigned char read_status_reg(SLMP_INFO * info) -{ - unsigned char *RegAddr = (unsigned char *)info->statctrl_base; - return *RegAddr; -} - -static void write_control_reg(SLMP_INFO * info) -{ - unsigned char *RegAddr = (unsigned char *)info->statctrl_base; - *RegAddr = info->port_array[0]->ctrlreg_value; -} - - -static int __devinit synclinkmp_init_one (struct pci_dev *dev, - const struct pci_device_id *ent) -{ - if (pci_enable_device(dev)) { - printk("error enabling pci device %p\n", dev); - return -EIO; - } - device_init( ++synclinkmp_adapter_count, dev ); - return 0; -} - -static void __devexit synclinkmp_remove_one (struct pci_dev *dev) -{ -} diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 9cfbdb318ed9..3fd7199301b6 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -147,4 +147,175 @@ config LEGACY_PTY_COUNT When not in use, each legacy PTY occupies 12 bytes on 32-bit architectures and 24 bytes on 64-bit architectures. +config BFIN_JTAG_COMM + tristate "Blackfin JTAG Communication" + depends on BLACKFIN + help + Add support for emulating a TTY device over the Blackfin JTAG. + + To compile this driver as a module, choose M here: the + module will be called bfin_jtag_comm. + +config BFIN_JTAG_COMM_CONSOLE + bool "Console on Blackfin JTAG" + depends on BFIN_JTAG_COMM=y + +config SERIAL_NONSTANDARD + bool "Non-standard serial port support" + depends on HAS_IOMEM + ---help--- + Say Y here if you have any non-standard serial boards -- boards + which aren't supported using the standard "dumb" serial driver. + This includes intelligent serial boards such as Cyclades, + Digiboards, etc. These are usually used for systems that need many + serial ports because they serve many terminals or dial-in + connections. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about non-standard serial boards. + + Most people can say N here. + +config ROCKETPORT + tristate "Comtrol RocketPort support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + help + This driver supports Comtrol RocketPort and RocketModem PCI boards. + These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or + modems. For information about the RocketPort/RocketModem boards + and this driver read . + + To compile this driver as a module, choose M here: the + module will be called rocket. + + If you want to compile this driver into the kernel, say Y here. If + you don't have a Comtrol RocketPort/RocketModem card installed, say N. + +config CYCLADES + tristate "Cyclades async mux support" + depends on SERIAL_NONSTANDARD && (PCI || ISA) + select FW_LOADER + ---help--- + This driver supports Cyclades Z and Y multiserial boards. + You would need something like this to connect more than two modems to + your Linux box, for instance in order to become a dial-in server. + + For information about the Cyclades-Z card, read + . + + To compile this driver as a module, choose M here: the + module will be called cyclades. + + If you haven't heard about it, it's safe to say N. + +config CYZ_INTR + bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)" + depends on EXPERIMENTAL && CYCLADES + help + The Cyclades-Z family of multiport cards allows 2 (two) driver op + modes: polling and interrupt. In polling mode, the driver will check + the status of the Cyclades-Z ports every certain amount of time + (which is called polling cycle and is configurable). In interrupt + mode, it will use an interrupt line (IRQ) in order to check the + status of the Cyclades-Z ports. The default op mode is polling. If + unsure, say N. + +config MOXA_INTELLIO + tristate "Moxa Intellio support" + depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + select FW_LOADER + help + Say Y here if you have a Moxa Intellio multiport serial card. + + To compile this driver as a module, choose M here: the + module will be called moxa. + +config MOXA_SMARTIO + tristate "Moxa SmartIO support v. 2.0" + depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) + help + Say Y here if you have a Moxa SmartIO multiport serial card and/or + want to help develop a new version of this driver. + + This is upgraded (1.9.1) driver from original Moxa drivers with + changes finally resulting in PCI probing. + + This driver can also be built as a module. The module will be called + mxser. If you want to do that, say M here. + +config SYNCLINK + tristate "Microgate SyncLink card support" + depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API + help + Provides support for the SyncLink ISA and PCI multiprotocol serial + adapters. These adapters support asynchronous and HDLC bit + synchronous communication up to 10Mbps (PCI adapter). + + This driver can only be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called synclink. If you want to do that, say M + here. + +config SYNCLINKMP + tristate "SyncLink Multiport support" + depends on SERIAL_NONSTANDARD && PCI + help + Enable support for the SyncLink Multiport (2 or 4 ports) + serial adapter, running asynchronous and HDLC communications up + to 2.048Mbps. Each ports is independently selectable for + RS-232, V.35, RS-449, RS-530, and X.21 + + This driver may be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called synclinkmp. If you want to do that, say M + here. + +config SYNCLINK_GT + tristate "SyncLink GT/AC support" + depends on SERIAL_NONSTANDARD && PCI + help + Support for SyncLink GT and SyncLink AC families of + synchronous and asynchronous serial adapters + manufactured by Microgate Systems, Ltd. (www.microgate.com) + +config NOZOMI + tristate "HSDPA Broadband Wireless Data Card - Globe Trotter" + depends on PCI && EXPERIMENTAL + help + If you have a HSDPA driver Broadband Wireless Data Card - + Globe Trotter PCMCIA card, say Y here. + + To compile this driver as a module, choose M here, the module + will be called nozomi. + +config ISI + tristate "Multi-Tech multiport card support (EXPERIMENTAL)" + depends on SERIAL_NONSTANDARD && PCI + select FW_LOADER + help + This is a driver for the Multi-Tech cards which provide several + serial ports. The driver is experimental and can currently only be + built as a module. The module will be called isicom. + If you want to do that, choose M here. + +config N_HDLC + tristate "HDLC line discipline support" + depends on SERIAL_NONSTANDARD + help + Allows synchronous HDLC communications with tty device drivers that + support synchronous HDLC such as the Microgate SyncLink adapter. + + This driver can be built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called n_hdlc. If you want to do that, say M + here. + +config N_GSM + tristate "GSM MUX line discipline support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on NET + help + This line discipline provides support for the GSM MUX protocol and + presents the mux as a set of 61 individual tty devices. diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 396277216e4f..e549da348a04 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -11,3 +11,16 @@ obj-$(CONFIG_R3964) += n_r3964.o obj-y += vt/ obj-$(CONFIG_HVC_DRIVER) += hvc/ obj-y += serial/ + +# tty drivers +obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o +obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o +obj-$(CONFIG_CYCLADES) += cyclades.o +obj-$(CONFIG_ISI) += isicom.o +obj-$(CONFIG_MOXA_INTELLIO) += moxa.o +obj-$(CONFIG_MOXA_SMARTIO) += mxser.o +obj-$(CONFIG_NOZOMI) += nozomi.o +obj-$(CONFIG_ROCKETPORT) += rocket.o +obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o +obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o +obj-$(CONFIG_SYNCLINK) += synclink.o diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c new file mode 100644 index 000000000000..f214e5022472 --- /dev/null +++ b/drivers/tty/amiserial.c @@ -0,0 +1,2178 @@ +/* + * linux/drivers/char/amiserial.c + * + * Serial driver for the amiga builtin port. + * + * This code was created by taking serial.c version 4.30 from kernel + * release 2.3.22, replacing all hardware related stuff with the + * corresponding amiga hardware actions, and removing all irrelevant + * code. As a consequence, it uses many of the constants and names + * associated with the registers and bits of 16550 compatible UARTS - + * but only to keep track of status, etc in the state variables. It + * was done this was to make it easier to keep the code in line with + * (non hardware specific) changes to serial.c. + * + * The port is registered with the tty driver as minor device 64, and + * therefore other ports should should only use 65 upwards. + * + * Richard Lucock 28/12/99 + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + */ + +/* + * Serial driver configuration section. Here are the various options: + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include + +#undef SERIAL_PARANOIA_CHECK +#define SERIAL_DO_RESTART + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + +/* Sanity checks */ + +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) +#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s) +#else +#define DBG_CNT(s) +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#include +#include +#include +static char *serial_version = "4.30"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#define custom amiga_custom +static char *serial_name = "Amiga-builtin serial driver"; + +static struct tty_driver *serial_driver; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +static struct async_struct *IRQ_ports; + +static unsigned char current_ctl_bits; + +static void change_speed(struct async_struct *info, struct ktermios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + + +static struct serial_state rs_table[1]; + +#define NR_PORTS ARRAY_SIZE(rs_table) + +#include + +#define serial_isroot() (capable(CAP_SYS_ADMIN)) + + +static inline int serial_paranoia_check(struct async_struct *info, + char *name, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, name, routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#endif + return 0; +} + +/* some serial hardware definitions */ +#define SDR_OVRUN (1<<15) +#define SDR_RBF (1<<14) +#define SDR_TBE (1<<13) +#define SDR_TSRE (1<<12) + +#define SERPER_PARENB (1<<15) + +#define AC_SETCLR (1<<15) +#define AC_UARTBRK (1<<11) + +#define SER_DTR (1<<7) +#define SER_RTS (1<<6) +#define SER_DCD (1<<5) +#define SER_CTS (1<<4) +#define SER_DSR (1<<3) + +static __inline__ void rtsdtr_ctrl(int bits) +{ + ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR)); +} + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_stop")) + return; + + local_irq_save(flags); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + /* disable Tx interrupt and remove any pending interrupts */ + custom.intena = IF_TBE; + mb(); + custom.intreq = IF_TBE; + mb(); + } + local_irq_restore(flags); +} + +static void rs_start(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_start")) + return; + + local_irq_save(flags); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + } + local_irq_restore(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void rs_sched_event(struct async_struct *info, + int event) +{ + info->event |= 1 << event; + tasklet_schedule(&info->tlet); +} + +static void receive_chars(struct async_struct *info) +{ + int status; + int serdatr; + struct tty_struct *tty = info->tty; + unsigned char ch, flag; + struct async_icount *icount; + int oe = 0; + + icount = &info->state->icount; + + status = UART_LSR_DR; /* We obviously have a character! */ + serdatr = custom.serdatr; + mb(); + custom.intreq = IF_RBF; + mb(); + + if((serdatr & 0x1ff) == 0) + status |= UART_LSR_BI; + if(serdatr & SDR_OVRUN) + status |= UART_LSR_OE; + + ch = serdatr & 0xff; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, status); +#endif + flag = TTY_NORMAL; + + /* + * We don't handle parity or frame errors - but I have left + * the code in, since I'm not sure that the errors can't be + * detected. + */ + + if (status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + /* + * For statistics only + */ + if (status & UART_LSR_BI) { + status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + } else if (status & UART_LSR_PE) + icount->parity++; + else if (status & UART_LSR_FE) + icount->frame++; + if (status & UART_LSR_OE) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (status & info->ignore_status_mask) + goto out; + + status &= info->read_status_mask; + + if (status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + flag = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & UART_LSR_PE) + flag = TTY_PARITY; + else if (status & UART_LSR_FE) + flag = TTY_FRAME; + if (status & UART_LSR_OE) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + oe = 1; + } + } + tty_insert_flip_char(tty, ch, flag); + if (oe == 1) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_flip_buffer_push(tty); +out: + return; +} + +static void transmit_chars(struct async_struct *info) +{ + custom.intreq = IF_TBE; + mb(); + if (info->x_char) { + custom.serdat = info->x_char | 0x100; + mb(); + info->state->icount.tx++; + info->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + custom.intena = IF_TBE; + mb(); + return; + } + + custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100; + mb(); + info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (info->xmit.head == info->xmit.tail) { + custom.intena = IF_TBE; + mb(); + info->IER &= ~UART_IER_THRI; + } +} + +static void check_modem_status(struct async_struct *info) +{ + unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); + unsigned char dstatus; + struct async_icount *icount; + + /* Determine bits that have changed */ + dstatus = status ^ current_ctl_bits; + current_ctl_bits = status; + + if (dstatus) { + icount = &info->state->icount; + /* update input line counters */ + if (dstatus & SER_DSR) + icount->dsr++; + if (dstatus & SER_DCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + !(status & SER_DCD)) + hardpps(); +#endif + } + if (dstatus & SER_CTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttyS%d CD now %s...", info->line, + (!(status & SER_DCD)) ? "on" : "off"); +#endif + if (!(status & SER_DCD)) + wake_up_interruptible(&info->open_wait); + else { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (!(status & SER_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if ((status & SER_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + /* disable Tx interrupt and remove any pending interrupts */ + custom.intena = IF_TBE; + mb(); + custom.intreq = IF_TBE; + mb(); + } + } + } +} + +static irqreturn_t ser_vbl_int( int irq, void *data) +{ + /* vbl is just a periodic interrupt we tie into to update modem status */ + struct async_struct * info = IRQ_ports; + /* + * TBD - is it better to unregister from this interrupt or to + * ignore it if MSI is clear ? + */ + if(info->IER & UART_IER_MSI) + check_modem_status(info); + return IRQ_HANDLED; +} + +static irqreturn_t ser_rx_int(int irq, void *dev_id) +{ + struct async_struct * info; + +#ifdef SERIAL_DEBUG_INTR + printk("ser_rx_int..."); +#endif + + info = IRQ_ports; + if (!info || !info->tty) + return IRQ_NONE; + + receive_chars(info); + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t ser_tx_int(int irq, void *dev_id) +{ + struct async_struct * info; + + if (custom.serdatr & SDR_TBE) { +#ifdef SERIAL_DEBUG_INTR + printk("ser_tx_int..."); +#endif + + info = IRQ_ports; + if (!info || !info->tty) + return IRQ_NONE; + + transmit_chars(info); + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif + } + return IRQ_HANDLED; +} + +/* + * ------------------------------------------------------------------- + * Here ends the serial 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_softint(unsigned long private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); +} + +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + unsigned long page; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + local_irq_save(flags); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d ...", info->line); +#endif + + /* Clear anything in the input buffer */ + + custom.intreq = IF_RBF; + mb(); + + retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info); + if (retval) { + if (serial_isroot()) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + + /* enable both Rx and Tx interrupts */ + custom.intena = IF_SETCLR | IF_RBF | IF_TBE; + mb(); + info->IER = UART_IER_MSI; + + /* remember current state of the DCD and CTS bits */ + current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); + + IRQ_ports = info; + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = SER_DTR | SER_RTS; + rtsdtr_ctrl(info->MCR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + /* + * and set the speed of the serial port + */ + change_speed(info, NULL); + + info->flags |= ASYNC_INITIALIZED; + local_irq_restore(flags); + return 0; + +errout: + local_irq_restore(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct async_struct * info) +{ + unsigned long flags; + struct serial_state *state; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d ....\n", info->line); +#endif + + local_irq_save(flags); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + IRQ_ports = NULL; + + /* + * Free the IRQ, if necessary + */ + free_irq(IRQ_AMIGA_VERTB, info); + + if (info->xmit.buf) { + free_page((unsigned long) info->xmit.buf); + info->xmit.buf = NULL; + } + + info->IER = 0; + custom.intena = IF_RBF | IF_TBE; + mb(); + + /* disable break condition */ + custom.adkcon = AC_UARTBRK; + mb(); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(SER_DTR|SER_RTS); + rtsdtr_ctrl(info->MCR); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + local_irq_restore(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 async_struct *info, + struct ktermios *old_termios) +{ + int quot = 0, baud_base, baud; + unsigned cflag, cval = 0; + int bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + + /* Byte size is always 8 bits plus parity bit if requested */ + + cval = 3; bits = 10; + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + baud_base = info->state->baud_base; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + /* If the quotient is zero refuse the change */ + if (!quot && old_termios) { + /* FIXME: Will need updating for new tty in the end */ + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + } + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + /* TBD: + * Does clearing IER_MSI imply that we should disable the VBL interrupt ? + */ + + /* + * Set up parity check flag + */ + + info->read_status_mask = UART_LSR_OE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; + local_irq_save(flags); + + { + short serper; + + /* Set up the baud rate */ + serper = quot - 1; + + /* Enable or disable parity bit */ + + if(cval & UART_LCR_PARITY) + serper |= (SERPER_PARENB); + + custom.serper = serper; + mb(); + } + + info->LCR = cval; /* Save LCR */ + local_irq_restore(flags); +} + +static int rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct async_struct *info; + unsigned long flags; + + info = tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_put_char")) + return 0; + + if (!info->xmit.buf) + return 0; + + local_irq_save(flags); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + local_irq_restore(flags); + return 0; + } + + info->xmit.buf[info->xmit.head++] = ch; + info->xmit.head &= SERIAL_XMIT_SIZE-1; + local_irq_restore(flags); + return 1; +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) + return; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + local_irq_save(flags); + info->IER |= UART_IER_THRI; + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + local_irq_restore(flags); +} + +static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info; + unsigned long flags; + + info = tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_write")) + return 0; + + if (!info->xmit.buf) + return 0; + + local_irq_save(flags); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + local_irq_restore(flags); + + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + local_irq_disable(); + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + local_irq_restore(flags); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) + return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) + return; + local_irq_save(flags); + info->xmit.head = info->xmit.tail = 0; + local_irq_restore(flags); + tty_wakeup(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + + /* Check this ! */ + local_irq_save(flags); + if(!(custom.intenar & IF_TBE)) { + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + } + local_irq_restore(flags); + + info->IER |= UART_IER_THRI; + } +} + +/* + * ------------------------------------------------------------ + * 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 async_struct *info = 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->name, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~SER_RTS; + + local_irq_save(flags); + rtsdtr_ctrl(info->MCR); + local_irq_restore(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = 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->name, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_send_xchar(tty, START_CHAR(tty)); + } + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= SER_RTS; + local_irq_save(flags); + rtsdtr_ctrl(info->MCR); + local_irq_restore(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct async_struct * info, + struct serial_struct __user * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tty_lock(); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tty_unlock(); + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct __user * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int change_irq,change_port; + int retval = 0; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + + tty_lock(); + state = info->state; + old_state = *state; + + change_irq = new_serial.irq != state->irq; + change_port = (new_serial.port != state->port); + if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) { + tty_unlock(); + return -EINVAL; + } + + if (!serial_isroot()) { + if ((new_serial.baud_base != state->baud_base) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (new_serial.baud_base < 9600) { + tty_unlock(); + return -EINVAL; + } + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + +check_and_exit: + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + change_speed(info, NULL); + } + } else + retval = startup(info); + tty_unlock(); + 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 async_struct * info, unsigned int __user *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + local_irq_save(flags); + status = custom.serdatr; + mb(); + local_irq_restore(flags); + result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0); + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + + +static int rs_tiocmget(struct tty_struct *tty) +{ + struct async_struct * info = tty->driver_data; + unsigned char control, status; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + control = info->MCR; + local_irq_save(flags); + status = ciab.pra; + local_irq_restore(flags); + return ((control & SER_RTS) ? TIOCM_RTS : 0) + | ((control & SER_DTR) ? TIOCM_DTR : 0) + | (!(status & SER_DCD) ? TIOCM_CAR : 0) + | (!(status & SER_DSR) ? TIOCM_DSR : 0) + | (!(status & SER_CTS) ? TIOCM_CTS : 0); +} + +static int rs_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ + struct async_struct * info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + local_irq_save(flags); + if (set & TIOCM_RTS) + info->MCR |= SER_RTS; + if (set & TIOCM_DTR) + info->MCR |= SER_DTR; + if (clear & TIOCM_RTS) + info->MCR &= ~SER_RTS; + if (clear & TIOCM_DTR) + info->MCR &= ~SER_DTR; + rtsdtr_ctrl(info->MCR); + local_irq_restore(flags); + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +static int rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_break")) + return -EINVAL; + + local_irq_save(flags); + if (break_state == -1) + custom.adkcon = AC_SETCLR | AC_UARTBRK; + else + custom.adkcon = AC_UARTBRK; + mb(); + local_irq_restore(flags); + return 0; +} + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int rs_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct async_struct *info = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + + local_irq_save(flags); + cnow = info->state->icount; + local_irq_restore(flags); + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + + return 0; +} + +static int rs_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = tty->driver_data; + struct async_icount cprev, cnow; /* kernel counter temps */ + void __user *argp = (void __user *)arg; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(info, argp); + case TIOCSSERIAL: + return set_serial_info(info, argp); + case TIOCSERCONFIG: + return 0; + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, argp); + + case TIOCSERGSTRUCT: + if (copy_to_user(argp, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + local_irq_save(flags); + /* note the counters on entry */ + cprev = info->state->icount; + local_irq_restore(flags); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + local_irq_save(flags); + cnow = info->state->icount; /* atomic copy */ + local_irq_restore(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct async_struct *info = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(SER_DTR|SER_RTS); + local_irq_save(flags); + rtsdtr_ctrl(info->MCR); + local_irq_restore(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= SER_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= SER_RTS; + } + local_irq_save(flags); + rtsdtr_ctrl(info->MCR); + local_irq_restore(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct async_struct * info = tty->driver_data; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->name, "rs_close")) + return; + + state = info->state; + + local_irq_save(flags); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + local_irq_restore(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->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, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + local_irq_restore(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_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, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + /* disable receive interrupts */ + custom.intena = IF_RBF; + mb(); + /* clear any pending receive interrupt */ + custom.intreq = IF_RBF; + mb(); + + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_wait_until_sent(tty, info->timeout); + } + shutdown(info); + rs_flush_buffer(tty); + + tty_ldisc_flush(tty); + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if (info->blocked_open) { + if (info->close_delay) { + msleep_interruptible(jiffies_to_msecs(info->close_delay)); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + local_irq_restore(flags); +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = tty->driver_data; + unsigned long orig_jiffies, char_time; + int tty_was_locked = tty_locked(); + int lsr; + + if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + orig_jiffies = jiffies; + + /* + * tty_wait_until_sent is called from lots of places, + * with or without the BTM. + */ + if (!tty_was_locked) + tty_lock(); + /* + * 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. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + 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_t(unsigned long, char_time, timeout); + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while(!((lsr = custom.serdatr) & SDR_TSRE)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("serdatr = %d (jiff=%lu)...", lsr, jiffies); +#endif + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + __set_current_state(TASK_RUNNING); + if (!tty_was_locked) + tty_unlock(); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->name, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct async_struct *info) +{ +#ifdef DECLARE_WAITQUEUE + DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue wait = { current, NULL }; +#endif + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * 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))) { + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + 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, state->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", + state->line, state->count); +#endif + local_irq_save(flags); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + local_irq_restore(flags); + info->blocked_open++; + while (1) { + local_irq_save(flags); + if (tty->termios->c_cflag & CBAUD) + rtsdtr_ctrl(SER_DTR|SER_RTS); + local_irq_restore(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CLOSING) && + (do_clocal || (!(ciab.pra & SER_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, state->count); +#endif + tty_unlock(); + schedule(); + tty_lock(); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kzalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } +#ifdef DECLARE_WAITQUEUE + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); +#endif + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + tasklet_init(&info->tlet, do_softint, (unsigned long)info); + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + + line = tty->index; + if ((line < 0) || (line >= NR_PORTS)) { + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + return retval; + } + tty->driver_data = info; + info->tty = tty; + if (serial_paranoia_check(info, tty->name, "rs_open")) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s, count = %d\n", tty->name, info->state->count); +#endif + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_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; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s successful...", tty->name); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline void line_info(struct seq_file *m, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; + unsigned long flags; + + seq_printf(m, "%d: uart:amiga_builtin",state->line); + + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->flags = state->flags; + info->quot = 0; + info->tty = NULL; + } + local_irq_save(flags); + status = ciab.pra; + control = info ? info->MCR : status; + local_irq_restore(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if(!(control & SER_RTS)) + strcat(stat_buf, "|RTS"); + if(!(status & SER_CTS)) + strcat(stat_buf, "|CTS"); + if(!(control & SER_DTR)) + strcat(stat_buf, "|DTR"); + if(!(status & SER_DSR)) + strcat(stat_buf, "|DSR"); + if(!(status & SER_DCD)) + strcat(stat_buf, "|CD"); + + if (info->quot) { + seq_printf(m, " baud:%d", state->baud_base / info->quot); + } + + seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx); + + if (state->icount.frame) + seq_printf(m, " fe:%d", state->icount.frame); + + if (state->icount.parity) + seq_printf(m, " pe:%d", state->icount.parity); + + if (state->icount.brk) + seq_printf(m, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + seq_printf(m, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + seq_printf(m, " %s\n", stat_buf+1); +} + +static int rs_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); + line_info(m, &rs_table[0]); + return 0; +} + +static int rs_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, rs_proc_show, NULL); +} + +static const struct file_operations rs_proc_fops = { + .owner = THIS_MODULE, + .open = rs_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); +} + + +static const struct tty_operations serial_ops = { + .open = rs_open, + .close = rs_close, + .write = rs_write, + .put_char = rs_put_char, + .flush_chars = rs_flush_chars, + .write_room = rs_write_room, + .chars_in_buffer = rs_chars_in_buffer, + .flush_buffer = rs_flush_buffer, + .ioctl = rs_ioctl, + .throttle = rs_throttle, + .unthrottle = rs_unthrottle, + .set_termios = rs_set_termios, + .stop = rs_stop, + .start = rs_start, + .hangup = rs_hangup, + .break_ctl = rs_break, + .send_xchar = rs_send_xchar, + .wait_until_sent = rs_wait_until_sent, + .tiocmget = rs_tiocmget, + .tiocmset = rs_tiocmset, + .get_icount = rs_get_icount, + .proc_fops = &rs_proc_fops, +}; + +/* + * The serial driver boot-time initialization code! + */ +static int __init amiga_serial_probe(struct platform_device *pdev) +{ + unsigned long flags; + struct serial_state * state; + int error; + + serial_driver = alloc_tty_driver(1); + if (!serial_driver) + return -ENOMEM; + + IRQ_ports = NULL; + + show_serial_version(); + + /* Initialize the tty_driver structure */ + + serial_driver->owner = THIS_MODULE; + serial_driver->driver_name = "amiserial"; + serial_driver->name = "ttyS"; + serial_driver->major = TTY_MAJOR; + serial_driver->minor_start = 64; + 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; + tty_set_operations(serial_driver, &serial_ops); + + error = tty_register_driver(serial_driver); + if (error) + goto fail_put_tty_driver; + + state = rs_table; + state->magic = SSTATE_MAGIC; + state->port = (int)&custom.serdatr; /* Just to give it a value */ + state->line = 0; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + + printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", + state->line); + + /* Hardware set up */ + + state->baud_base = amiga_colorclock; + state->xmit_fifo_size = 1; + + /* set ISRs, and then disable the rx interrupts */ + error = request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state); + if (error) + goto fail_unregister; + + error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED, + "serial RX", state); + if (error) + goto fail_free_irq; + + local_irq_save(flags); + + /* turn off Rx and Tx interrupts */ + custom.intena = IF_RBF | IF_TBE; + mb(); + + /* clear any pending interrupt */ + custom.intreq = IF_RBF | IF_TBE; + mb(); + + local_irq_restore(flags); + + /* + * set the appropriate directions for the modem control flags, + * and clear RTS and DTR + */ + ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ + ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ + + platform_set_drvdata(pdev, state); + + return 0; + +fail_free_irq: + free_irq(IRQ_AMIGA_TBE, state); +fail_unregister: + tty_unregister_driver(serial_driver); +fail_put_tty_driver: + put_tty_driver(serial_driver); + return error; +} + +static int __exit amiga_serial_remove(struct platform_device *pdev) +{ + int error; + struct serial_state *state = platform_get_drvdata(pdev); + struct async_struct *info = state->info; + + /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ + tasklet_kill(&info->tlet); + if ((error = tty_unregister_driver(serial_driver))) + printk("SERIAL: failed to unregister serial driver (%d)\n", + error); + put_tty_driver(serial_driver); + + rs_table[0].info = NULL; + kfree(info); + + free_irq(IRQ_AMIGA_TBE, rs_table); + free_irq(IRQ_AMIGA_RBF, rs_table); + + platform_set_drvdata(pdev, NULL); + + return error; +} + +static struct platform_driver amiga_serial_driver = { + .remove = __exit_p(amiga_serial_remove), + .driver = { + .name = "amiga-serial", + .owner = THIS_MODULE, + }, +}; + +static int __init amiga_serial_init(void) +{ + return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); +} + +module_init(amiga_serial_init); + +static void __exit amiga_serial_exit(void) +{ + platform_driver_unregister(&amiga_serial_driver); +} + +module_exit(amiga_serial_exit); + + +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ + +static void amiga_serial_putc(char c) +{ + custom.serdat = (unsigned char)c | 0x100; + while (!(custom.serdatr & 0x2000)) + barrier(); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here. + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + unsigned short intena = custom.intenar; + + custom.intena = IF_TBE; + + while (count--) { + if (*s == '\n') + amiga_serial_putc('\r'); + amiga_serial_putc(*s++); + } + + custom.intena = IF_SETCLR | (intena & IF_TBE); +} + +static struct tty_driver *serial_console_device(struct console *c, int *index) +{ + *index = 0; + return serial_driver; +} + +static struct console sercons = { + .name = "ttyS", + .write = serial_console_write, + .device = serial_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/* + * Register console. + */ +static int __init amiserial_console_init(void) +{ + register_console(&sercons); + return 0; +} +console_initcall(amiserial_console_init); + +#endif /* CONFIG_SERIAL_CONSOLE && !MODULE */ + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:amiga-serial"); diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c new file mode 100644 index 000000000000..16402445f2b2 --- /dev/null +++ b/drivers/tty/bfin_jtag_comm.c @@ -0,0 +1,366 @@ +/* + * TTY over Blackfin JTAG Communication + * + * Copyright 2008-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#define DRV_NAME "bfin-jtag-comm" +#define DEV_NAME "ttyBFJC" +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +/* See the Debug/Emulation chapter in the HRM */ +#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */ +#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */ +#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */ +#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */ + +static inline uint32_t bfin_write_emudat(uint32_t emudat) +{ + __asm__ __volatile__("emudat = %0;" : : "d"(emudat)); + return emudat; +} + +static inline uint32_t bfin_read_emudat(void) +{ + uint32_t emudat; + __asm__ __volatile__("%0 = emudat;" : "=d"(emudat)); + return emudat; +} + +static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d) +{ + return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24)); +} + +#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */ +#define CIRC_MASK (CIRC_SIZE - 1) +#define circ_empty(circ) ((circ)->head == (circ)->tail) +#define circ_free(circ) CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE) +#define circ_cnt(circ) CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE) +#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK]) + +static struct tty_driver *bfin_jc_driver; +static struct task_struct *bfin_jc_kthread; +static struct tty_struct * volatile bfin_jc_tty; +static unsigned long bfin_jc_count; +static DEFINE_MUTEX(bfin_jc_tty_mutex); +static volatile struct circ_buf bfin_jc_write_buf; + +static int +bfin_jc_emudat_manager(void *arg) +{ + uint32_t inbound_len = 0, outbound_len = 0; + + while (!kthread_should_stop()) { + /* no one left to give data to, so sleep */ + if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) { + pr_debug("waiting for readers\n"); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + __set_current_state(TASK_RUNNING); + } + + /* no data available, so just chill */ + if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) { + pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n", + inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head); + if (inbound_len) + schedule(); + else + schedule_timeout_interruptible(HZ); + continue; + } + + /* if incoming data is ready, eat it */ + if (bfin_read_DBGSTAT() & EMUDIF) { + struct tty_struct *tty; + mutex_lock(&bfin_jc_tty_mutex); + tty = (struct tty_struct *)bfin_jc_tty; + if (tty != NULL) { + uint32_t emudat = bfin_read_emudat(); + if (inbound_len == 0) { + pr_debug("incoming length: 0x%08x\n", emudat); + inbound_len = emudat; + } else { + size_t num_chars = (4 <= inbound_len ? 4 : inbound_len); + pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); + inbound_len -= num_chars; + tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars); + tty_flip_buffer_push(tty); + } + } + mutex_unlock(&bfin_jc_tty_mutex); + } + + /* if outgoing data is ready, post it */ + if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) { + if (outbound_len == 0) { + outbound_len = circ_cnt(&bfin_jc_write_buf); + bfin_write_emudat(outbound_len); + pr_debug("outgoing length: 0x%08x\n", outbound_len); + } else { + struct tty_struct *tty; + int tail = bfin_jc_write_buf.tail; + size_t ate = (4 <= outbound_len ? 4 : outbound_len); + uint32_t emudat = + bfin_write_emudat_chars( + circ_byte(&bfin_jc_write_buf, tail + 0), + circ_byte(&bfin_jc_write_buf, tail + 1), + circ_byte(&bfin_jc_write_buf, tail + 2), + circ_byte(&bfin_jc_write_buf, tail + 3) + ); + bfin_jc_write_buf.tail += ate; + outbound_len -= ate; + mutex_lock(&bfin_jc_tty_mutex); + tty = (struct tty_struct *)bfin_jc_tty; + if (tty) + tty_wakeup(tty); + mutex_unlock(&bfin_jc_tty_mutex); + pr_debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate); + } + } + } + + __set_current_state(TASK_RUNNING); + return 0; +} + +static int +bfin_jc_open(struct tty_struct *tty, struct file *filp) +{ + mutex_lock(&bfin_jc_tty_mutex); + pr_debug("open %lu\n", bfin_jc_count); + ++bfin_jc_count; + bfin_jc_tty = tty; + wake_up_process(bfin_jc_kthread); + mutex_unlock(&bfin_jc_tty_mutex); + return 0; +} + +static void +bfin_jc_close(struct tty_struct *tty, struct file *filp) +{ + mutex_lock(&bfin_jc_tty_mutex); + pr_debug("close %lu\n", bfin_jc_count); + if (--bfin_jc_count == 0) + bfin_jc_tty = NULL; + wake_up_process(bfin_jc_kthread); + mutex_unlock(&bfin_jc_tty_mutex); +} + +/* XXX: we dont handle the put_char() case where we must handle count = 1 */ +static int +bfin_jc_circ_write(const unsigned char *buf, int count) +{ + int i; + count = min(count, circ_free(&bfin_jc_write_buf)); + pr_debug("going to write chunk of %i bytes\n", count); + for (i = 0; i < count; ++i) + circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i]; + bfin_jc_write_buf.head += i; + return i; +} + +#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE +# define console_lock() +# define console_unlock() +#endif +static int +bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + int i; + console_lock(); + i = bfin_jc_circ_write(buf, count); + console_unlock(); + wake_up_process(bfin_jc_kthread); + return i; +} + +static void +bfin_jc_flush_chars(struct tty_struct *tty) +{ + wake_up_process(bfin_jc_kthread); +} + +static int +bfin_jc_write_room(struct tty_struct *tty) +{ + return circ_free(&bfin_jc_write_buf); +} + +static int +bfin_jc_chars_in_buffer(struct tty_struct *tty) +{ + return circ_cnt(&bfin_jc_write_buf); +} + +static void +bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout) +{ + unsigned long expire = jiffies + timeout; + while (!circ_empty(&bfin_jc_write_buf)) { + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } +} + +static const struct tty_operations bfin_jc_ops = { + .open = bfin_jc_open, + .close = bfin_jc_close, + .write = bfin_jc_write, + /*.put_char = bfin_jc_put_char,*/ + .flush_chars = bfin_jc_flush_chars, + .write_room = bfin_jc_write_room, + .chars_in_buffer = bfin_jc_chars_in_buffer, + .wait_until_sent = bfin_jc_wait_until_sent, +}; + +static int __init bfin_jc_init(void) +{ + int ret; + + bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME); + if (IS_ERR(bfin_jc_kthread)) + return PTR_ERR(bfin_jc_kthread); + + ret = -ENOMEM; + + bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0; + bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL); + if (!bfin_jc_write_buf.buf) + goto err; + + bfin_jc_driver = alloc_tty_driver(1); + if (!bfin_jc_driver) + goto err; + + bfin_jc_driver->owner = THIS_MODULE; + bfin_jc_driver->driver_name = DRV_NAME; + bfin_jc_driver->name = DEV_NAME; + bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL; + bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL; + bfin_jc_driver->init_termios = tty_std_termios; + tty_set_operations(bfin_jc_driver, &bfin_jc_ops); + + ret = tty_register_driver(bfin_jc_driver); + if (ret) + goto err; + + pr_init(KERN_INFO DRV_NAME ": initialized\n"); + + return 0; + + err: + put_tty_driver(bfin_jc_driver); + kfree(bfin_jc_write_buf.buf); + kthread_stop(bfin_jc_kthread); + return ret; +} +module_init(bfin_jc_init); + +static void __exit bfin_jc_exit(void) +{ + kthread_stop(bfin_jc_kthread); + kfree(bfin_jc_write_buf.buf); + tty_unregister_driver(bfin_jc_driver); + put_tty_driver(bfin_jc_driver); +} +module_exit(bfin_jc_exit); + +#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK) +static void +bfin_jc_straight_buffer_write(const char *buf, unsigned count) +{ + unsigned ate = 0; + while (bfin_read_DBGSTAT() & EMUDOF) + continue; + bfin_write_emudat(count); + while (ate < count) { + while (bfin_read_DBGSTAT() & EMUDOF) + continue; + bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]); + ate += 4; + } +} +#endif + +#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE +static void +bfin_jc_console_write(struct console *co, const char *buf, unsigned count) +{ + if (bfin_jc_kthread == NULL) + bfin_jc_straight_buffer_write(buf, count); + else + bfin_jc_circ_write(buf, count); +} + +static struct tty_driver * +bfin_jc_console_device(struct console *co, int *index) +{ + *index = co->index; + return bfin_jc_driver; +} + +static struct console bfin_jc_console = { + .name = DEV_NAME, + .write = bfin_jc_console_write, + .device = bfin_jc_console_device, + .flags = CON_ANYTIME | CON_PRINTBUFFER, + .index = -1, +}; + +static int __init bfin_jc_console_init(void) +{ + register_console(&bfin_jc_console); + return 0; +} +console_initcall(bfin_jc_console_init); +#endif + +#ifdef CONFIG_EARLY_PRINTK +static void __init +bfin_jc_early_write(struct console *co, const char *buf, unsigned int count) +{ + bfin_jc_straight_buffer_write(buf, count); +} + +static struct __initdata console bfin_jc_early_console = { + .name = "early_BFJC", + .write = bfin_jc_early_write, + .flags = CON_ANYTIME | CON_PRINTBUFFER, + .index = -1, +}; + +struct console * __init +bfin_jc_early_init(unsigned int port, unsigned int cflag) +{ + return &bfin_jc_early_console; +} +#endif + +MODULE_AUTHOR("Mike Frysinger "); +MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c new file mode 100644 index 000000000000..c99728f0cd9f --- /dev/null +++ b/drivers/tty/cyclades.c @@ -0,0 +1,4200 @@ +#undef BLOCKMOVE +#define Z_WAKE +#undef Z_EXT_CHARS_IN_BUFFER + +/* + * linux/drivers/char/cyclades.c + * + * This file contains the driver for the Cyclades async multiport + * serial boards. + * + * Initially written by Randolph Bentson . + * Modified and maintained by Marcio Saito . + * + * Copyright (C) 2007-2009 Jiri Slaby + * + * Much of the design and some of the code came from serial.c + * which was copyright (C) 1991, 1992 Linus Torvalds. It was + * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, + * and then fixed as suggested by Michael K. Johnson 12/12/92. + * Converted to pci probing and cleaned up by Jiri Slaby. + * + */ + +#define CY_VERSION "2.6" + +/* If you need to install more boards than NR_CARDS, change the constant + in the definition below. No other change is necessary to support up to + eight boards. Beyond that you'll have to extend cy_isa_addresses. */ + +#define NR_CARDS 4 + +/* + If the total number of ports is larger than NR_PORTS, change this + constant in the definition below. No other change is necessary to + support more boards/ports. */ + +#define NR_PORTS 256 + +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 + +#define SERIAL_PARANOIA_CHECK +#undef CY_DEBUG_OPEN +#undef CY_DEBUG_THROTTLE +#undef CY_DEBUG_OTHER +#undef CY_DEBUG_IO +#undef CY_DEBUG_COUNT +#undef CY_DEBUG_DTR +#undef CY_DEBUG_WAIT_UNTIL_SENT +#undef CY_DEBUG_INTERRUPTS +#undef CY_16Y_HACK +#undef CY_ENABLE_MONITORING +#undef CY_PCI_DEBUG + +/* + * Include section + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +static void cy_send_xchar(struct tty_struct *tty, char ch); + +#ifndef SERIAL_XMIT_SIZE +#define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) +#endif + +#define STD_COM_FLAGS (0) + +/* firmware stuff */ +#define ZL_MAX_BLOCKS 16 +#define DRIVER_VERSION 0x02010203 +#define RAM_SIZE 0x80000 + +enum zblock_type { + ZBLOCK_PRG = 0, + ZBLOCK_FPGA = 1 +}; + +struct zfile_header { + char name[64]; + char date[32]; + char aux[32]; + u32 n_config; + u32 config_offset; + u32 n_blocks; + u32 block_offset; + u32 reserved[9]; +} __attribute__ ((packed)); + +struct zfile_config { + char name[64]; + u32 mailbox; + u32 function; + u32 n_blocks; + u32 block_list[ZL_MAX_BLOCKS]; +} __attribute__ ((packed)); + +struct zfile_block { + u32 type; + u32 file_offset; + u32 ram_offset; + u32 size; +} __attribute__ ((packed)); + +static struct tty_driver *cy_serial_driver; + +#ifdef CONFIG_ISA +/* This is the address lookup table. The driver will probe for + Cyclom-Y/ISA boards at all addresses in here. If you want the + driver to probe addresses at a different address, add it to + this table. If the driver is probing some other board and + causing problems, remove the offending address from this table. +*/ + +static unsigned int cy_isa_addresses[] = { + 0xD0000, + 0xD2000, + 0xD4000, + 0xD6000, + 0xD8000, + 0xDA000, + 0xDC000, + 0xDE000, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) + +static long maddr[NR_CARDS]; +static int irq[NR_CARDS]; + +module_param_array(maddr, long, NULL, 0); +module_param_array(irq, int, NULL, 0); + +#endif /* CONFIG_ISA */ + +/* This is the per-card data structure containing address, irq, number of + channels, etc. This driver supports a maximum of NR_CARDS cards. +*/ +static struct cyclades_card cy_card[NR_CARDS]; + +static int cy_next_channel; /* next minor available */ + +/* + * This is used to look up the divisor speeds and the timeouts + * We're normally limited to 15 distinct baud rates. The extra + * are accessed via settings in info->port.flags. + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + * 20 + */ +static const int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, + 230400, 0 +}; + +static const char baud_co_25[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 +}; + +static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + +static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ + 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, + 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, + 0x21 +}; + +static const char baud_cor3[] = { /* receive threshold */ + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, + 0x07 +}; + +/* + * The Cyclades driver implements HW flow control as any serial driver. + * The cyclades_port structure member rflow and the vector rflow_thr + * allows us to take advantage of a special feature in the CD1400 to avoid + * data loss even when the system interrupt latency is too high. These flags + * are to be used only with very special applications. Setting these flags + * requires the use of a special cable (DTR and RTS reversed). In the new + * CD1400-based boards (rev. 6.00 or later), there is no need for special + * cables. + */ + +static const char rflow_thr[] = { /* rflow threshold */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a +}; + +/* The Cyclom-Ye has placed the sequential chips in non-sequential + * address order. This look-up table overcomes that problem. + */ +static const unsigned int cy_chip_offset[] = { 0x0000, + 0x0400, + 0x0800, + 0x0C00, + 0x0200, + 0x0600, + 0x0A00, + 0x0E00 +}; + +/* PCI related definitions */ + +#ifdef CONFIG_PCI +static const struct pci_device_id cy_pci_dev_id[] = { + /* PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, + /* PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, + /* 4Y PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, + /* 4Y PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, + /* 8Y PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, + /* 8Y PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, + /* Z PCI < 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, + /* Z PCI > 1Mb */ + { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, + { } /* end of table */ +}; +MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); +#endif + +static void cy_start(struct tty_struct *); +static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); +static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); +#ifdef CONFIG_ISA +static unsigned detect_isa_irq(void __iomem *); +#endif /* CONFIG_ISA */ + +#ifndef CONFIG_CYZ_INTR +static void cyz_poll(unsigned long); + +/* The Cyclades-Z polling cycle is defined by this variable */ +static long cyz_polling_cycle = CZ_DEF_POLL; + +static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0); + +#else /* CONFIG_CYZ_INTR */ +static void cyz_rx_restart(unsigned long); +static struct timer_list cyz_rx_full_timer[NR_PORTS]; +#endif /* CONFIG_CYZ_INTR */ + +static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) +{ + struct cyclades_card *card = port->card; + + cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); +} + +static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) +{ + struct cyclades_card *card = port->card; + + return readb(port->u.cyy.base_addr + (reg << card->bus_index)); +} + +static inline bool cy_is_Z(struct cyclades_card *card) +{ + return card->num_chips == (unsigned int)-1; +} + +static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr) +{ + return readl(&ctl_addr->init_ctrl) & (1 << 17); +} + +static inline bool cyz_fpga_loaded(struct cyclades_card *card) +{ + return __cyz_fpga_loaded(card->ctl_addr.p9060); +} + +static inline bool cyz_is_loaded(struct cyclades_card *card) +{ + struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; + + return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) && + readl(&fw_id->signature) == ZFIRM_ID; +} + +static inline int serial_paranoia_check(struct cyclades_port *info, + const char *name, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + if (!info) { + printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) " + "in %s\n", name, routine); + return 1; + } + + if (info->magic != CYCLADES_MAGIC) { + printk(KERN_WARNING "cyc Warning: bad magic number for serial " + "struct (%s) in %s\n", name, routine); + return 1; + } +#endif + return 0; +} + +/***********************************************************/ +/********* Start of block of Cyclom-Y specific code ********/ + +/* This routine waits up to 1000 micro-seconds for the previous + command to the Cirrus chip to complete and then issues the + new command. An error is returned if the previous command + didn't finish within the time limit. + + This function is only called from inside spinlock-protected code. + */ +static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) +{ + void __iomem *ccr = base_addr + (CyCCR << index); + unsigned int i; + + /* Check to see that the previous command has completed */ + for (i = 0; i < 100; i++) { + if (readb(ccr) == 0) + break; + udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if (i == 100) + return -1; + + /* Issue the new command */ + cy_writeb(ccr, cmd); + + return 0; +} + +static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) +{ + return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, + port->card->bus_index); +} + +#ifdef CONFIG_ISA +/* ISA interrupt detection code */ +static unsigned detect_isa_irq(void __iomem *address) +{ + int irq; + unsigned long irqs, flags; + int save_xir, save_car; + int index = 0; /* IRQ probing is only for ISA */ + + /* forget possible initially masked and pending IRQ */ + irq = probe_irq_off(probe_irq_on()); + + /* Clear interrupts on the board first */ + cy_writeb(address + (Cy_ClrIntr << index), 0); + /* Cy_ClrIntr is 0x1800 */ + + irqs = probe_irq_on(); + /* Wait ... */ + msleep(5); + + /* Enable the Tx interrupts on the CD1400 */ + local_irq_save(flags); + cy_writeb(address + (CyCAR << index), 0); + __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); + + cy_writeb(address + (CyCAR << index), 0); + cy_writeb(address + (CySRER << index), + readb(address + (CySRER << index)) | CyTxRdy); + local_irq_restore(flags); + + /* Wait ... */ + msleep(5); + + /* Check which interrupt is in use */ + irq = probe_irq_off(irqs); + + /* Clean up */ + save_xir = (u_char) readb(address + (CyTIR << index)); + save_car = readb(address + (CyCAR << index)); + cy_writeb(address + (CyCAR << index), (save_xir & 0x3)); + cy_writeb(address + (CySRER << index), + readb(address + (CySRER << index)) & ~CyTxRdy); + cy_writeb(address + (CyTIR << index), (save_xir & 0x3f)); + cy_writeb(address + (CyCAR << index), (save_car)); + cy_writeb(address + (Cy_ClrIntr << index), 0); + /* Cy_ClrIntr is 0x1800 */ + + return (irq > 0) ? irq : 0; +} +#endif /* CONFIG_ISA */ + +static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, + void __iomem *base_addr) +{ + struct cyclades_port *info; + struct tty_struct *tty; + int len, index = cinfo->bus_index; + u8 ivr, save_xir, channel, save_car, data, char_count; + +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); +#endif + /* determine the channel & change to that context */ + save_xir = readb(base_addr + (CyRIR << index)); + channel = save_xir & CyIRChannel; + info = &cinfo->ports[channel + chip * 4]; + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); + ivr = cyy_readb(info, CyRIVR) & CyIVRMask; + + tty = tty_port_tty_get(&info->port); + /* if there is nowhere to put the data, discard it */ + if (tty == NULL) { + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); + } else { /* normal character reception */ + char_count = cyy_readb(info, CyRDCR); + while (char_count--) + data = cyy_readb(info, CyRDSR); + } + goto end; + } + /* there is an open port for this data */ + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); + + /* For statistics only */ + if (data & CyBREAK) + info->icount.brk++; + else if (data & CyFRAME) + info->icount.frame++; + else if (data & CyPARITY) + info->icount.parity++; + else if (data & CyOVERRUN) + info->icount.overrun++; + + if (data & info->ignore_status_mask) { + info->icount.rx++; + tty_kref_put(tty); + return; + } + if (tty_buffer_request_room(tty, 1)) { + if (data & info->read_status_mask) { + if (data & CyBREAK) { + tty_insert_flip_char(tty, + cyy_readb(info, CyRDSR), + TTY_BREAK); + info->icount.rx++; + if (info->port.flags & ASYNC_SAK) + do_SAK(tty); + } else if (data & CyFRAME) { + tty_insert_flip_char(tty, + cyy_readb(info, CyRDSR), + TTY_FRAME); + info->icount.rx++; + info->idle_stats.frame_errs++; + } else if (data & CyPARITY) { + /* Pieces of seven... */ + tty_insert_flip_char(tty, + cyy_readb(info, CyRDSR), + TTY_PARITY); + info->icount.rx++; + info->idle_stats.parity_errs++; + } else if (data & CyOVERRUN) { + tty_insert_flip_char(tty, 0, + TTY_OVERRUN); + info->icount.rx++; + /* If the flip buffer itself is + overflowing, we still lose + the next incoming character. + */ + tty_insert_flip_char(tty, + cyy_readb(info, CyRDSR), + TTY_FRAME); + info->icount.rx++; + info->idle_stats.overruns++; + /* These two conditions may imply */ + /* a normal read should be done. */ + /* } else if(data & CyTIMEOUT) { */ + /* } else if(data & CySPECHAR) { */ + } else { + tty_insert_flip_char(tty, 0, + TTY_NORMAL); + info->icount.rx++; + } + } else { + tty_insert_flip_char(tty, 0, TTY_NORMAL); + info->icount.rx++; + } + } else { + /* there was a software buffer overrun and nothing + * could be done about it!!! */ + info->icount.buf_overrun++; + info->idle_stats.overruns++; + } + } else { /* normal character reception */ + /* load # chars available from the chip */ + char_count = cyy_readb(info, CyRDCR); + +#ifdef CY_ENABLE_MONITORING + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + len = tty_buffer_request_room(tty, char_count); + while (len--) { + data = cyy_readb(info, CyRDSR); + tty_insert_flip_char(tty, data, TTY_NORMAL); + info->idle_stats.recv_bytes++; + info->icount.rx++; +#ifdef CY_16Y_HACK + udelay(10L); +#endif + } + info->idle_stats.recv_idle = jiffies; + } + tty_schedule_flip(tty); + tty_kref_put(tty); +end: + /* end of service */ + cyy_writeb(info, CyRIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); +} + +static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, + void __iomem *base_addr) +{ + struct cyclades_port *info; + struct tty_struct *tty; + int char_count, index = cinfo->bus_index; + u8 save_xir, channel, save_car, outch; + + /* Since we only get here when the transmit buffer + is empty, we know we can always stuff a dozen + characters. */ +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip); +#endif + + /* determine the channel & change to that context */ + save_xir = readb(base_addr + (CyTIR << index)); + channel = save_xir & CyIRChannel; + save_car = readb(base_addr + (CyCAR << index)); + cy_writeb(base_addr + (CyCAR << index), save_xir); + + info = &cinfo->ports[channel + chip * 4]; + tty = tty_port_tty_get(&info->port); + if (tty == NULL) { + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); + goto end; + } + + /* load the on-chip space for outbound data */ + char_count = info->xmit_fifo_size; + + if (info->x_char) { /* send special char */ + outch = info->x_char; + cyy_writeb(info, CyTDR, outch); + char_count--; + info->icount.tx++; + info->x_char = 0; + } + + if (info->breakon || info->breakoff) { + if (info->breakon) { + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x81); + info->breakon = 0; + char_count -= 2; + } + if (info->breakoff) { + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x83); + info->breakoff = 0; + char_count -= 2; + } + } + + while (char_count-- > 0) { + if (!info->xmit_cnt) { + if (cyy_readb(info, CySRER) & CyTxMpty) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxMpty); + } else { + cyy_writeb(info, CySRER, CyTxMpty | + (cyy_readb(info, CySRER) & ~CyTxRdy)); + } + goto done; + } + if (info->port.xmit_buf == NULL) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); + goto done; + } + if (tty->stopped || tty->hw_stopped) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); + goto done; + } + /* Because the Embedded Transmit Commands have been enabled, + * we must check to see if the escape character, NULL, is being + * sent. If it is, we must ensure that there is room for it to + * be doubled in the output stream. Therefore we no longer + * advance the pointer when the character is fetched, but + * rather wait until after the check for a NULL output + * character. This is necessary because there may not be room + * for the two chars needed to send a NULL.) + */ + outch = info->port.xmit_buf[info->xmit_tail]; + if (outch) { + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) & + (SERIAL_XMIT_SIZE - 1); + cyy_writeb(info, CyTDR, outch); + info->icount.tx++; + } else { + if (char_count > 1) { + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) & + (SERIAL_XMIT_SIZE - 1); + cyy_writeb(info, CyTDR, outch); + cyy_writeb(info, CyTDR, 0); + info->icount.tx++; + char_count--; + } + } + } + +done: + tty_wakeup(tty); + tty_kref_put(tty); +end: + /* end of service */ + cyy_writeb(info, CyTIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); +} + +static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, + void __iomem *base_addr) +{ + struct cyclades_port *info; + struct tty_struct *tty; + int index = cinfo->bus_index; + u8 save_xir, channel, save_car, mdm_change, mdm_status; + + /* determine the channel & change to that context */ + save_xir = readb(base_addr + (CyMIR << index)); + channel = save_xir & CyIRChannel; + info = &cinfo->ports[channel + chip * 4]; + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); + + mdm_change = cyy_readb(info, CyMISR); + mdm_status = cyy_readb(info, CyMSVR1); + + tty = tty_port_tty_get(&info->port); + if (!tty) + goto end; + + if (mdm_change & CyANY_DELTA) { + /* For statistics only */ + if (mdm_change & CyDCD) + info->icount.dcd++; + if (mdm_change & CyCTS) + info->icount.cts++; + if (mdm_change & CyDSR) + info->icount.dsr++; + if (mdm_change & CyRI) + info->icount.rng++; + + wake_up_interruptible(&info->port.delta_msr_wait); + } + + if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { + if (mdm_status & CyDCD) + wake_up_interruptible(&info->port.open_wait); + else + tty_hangup(tty); + } + if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { + if (tty->hw_stopped) { + if (mdm_status & CyCTS) { + /* cy_start isn't used + because... !!! */ + tty->hw_stopped = 0; + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) | CyTxRdy); + tty_wakeup(tty); + } + } else { + if (!(mdm_status & CyCTS)) { + /* cy_stop isn't used + because ... !!! */ + tty->hw_stopped = 1; + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); + } + } + } +/* if (mdm_change & CyDSR) { + } + if (mdm_change & CyRI) { + }*/ + tty_kref_put(tty); +end: + /* end of service */ + cyy_writeb(info, CyMIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); +} + +/* The real interrupt service routine is called + whenever the card wants its hand held--chars + received, out buffer empty, modem change, etc. + */ +static irqreturn_t cyy_interrupt(int irq, void *dev_id) +{ + int status; + struct cyclades_card *cinfo = dev_id; + void __iomem *base_addr, *card_base_addr; + unsigned int chip, too_many, had_work; + int index; + + if (unlikely(cinfo == NULL)) { +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n", + irq); +#endif + return IRQ_NONE; /* spurious interrupt */ + } + + card_base_addr = cinfo->base_addr; + index = cinfo->bus_index; + + /* card was not initialized yet (e.g. DEBUG_SHIRQ) */ + if (unlikely(card_base_addr == NULL)) + return IRQ_HANDLED; + + /* This loop checks all chips in the card. Make a note whenever + _any_ chip had some work to do, as this is considered an + indication that there will be more to do. Only when no chip + has any work does this outermost loop exit. + */ + do { + had_work = 0; + for (chip = 0; chip < cinfo->num_chips; chip++) { + base_addr = cinfo->base_addr + + (cy_chip_offset[chip] << index); + too_many = 0; + while ((status = readb(base_addr + + (CySVRR << index))) != 0x00) { + had_work++; + /* The purpose of the following test is to ensure that + no chip can monopolize the driver. This forces the + chips to be checked in a round-robin fashion (after + draining each of a bunch (1000) of characters). + */ + if (1000 < too_many++) + break; + spin_lock(&cinfo->card_lock); + if (status & CySRReceive) /* rx intr */ + cyy_chip_rx(cinfo, chip, base_addr); + if (status & CySRTransmit) /* tx intr */ + cyy_chip_tx(cinfo, chip, base_addr); + if (status & CySRModem) /* modem intr */ + cyy_chip_modem(cinfo, chip, base_addr); + spin_unlock(&cinfo->card_lock); + } + } + } while (had_work); + + /* clear interrupts */ + spin_lock(&cinfo->card_lock); + cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0); + /* Cy_ClrIntr is 0x1800 */ + spin_unlock(&cinfo->card_lock); + return IRQ_HANDLED; +} /* cyy_interrupt */ + +static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, + unsigned int clear) +{ + struct cyclades_card *card = info->card; + int channel = info->line - card->first_line; + u32 rts, dtr, msvrr, msvrd; + + channel &= 0x03; + + if (info->rtsdtr_inv) { + msvrr = CyMSVR2; + msvrd = CyMSVR1; + rts = CyDTR; + dtr = CyRTS; + } else { + msvrr = CyMSVR1; + msvrd = CyMSVR2; + rts = CyRTS; + dtr = CyDTR; + } + if (set & TIOCM_RTS) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, rts); + } + if (clear & TIOCM_RTS) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, ~rts); + } + if (set & TIOCM_DTR) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, dtr); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); +#endif + } + if (clear & TIOCM_DTR) { + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, ~dtr); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); +#endif + } +} + +/***********************************************************/ +/********* End of block of Cyclom-Y specific code **********/ +/******** Start of block of Cyclades-Z specific code *******/ +/***********************************************************/ + +static int +cyz_fetch_msg(struct cyclades_card *cinfo, + __u32 *channel, __u8 *cmd, __u32 *param) +{ + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; + unsigned long loc_doorbell; + + loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); + if (loc_doorbell) { + *cmd = (char)(0xff & loc_doorbell); + *channel = readl(&board_ctrl->fwcmd_channel); + *param = (__u32) readl(&board_ctrl->fwcmd_param); + cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff); + return 1; + } + return 0; +} /* cyz_fetch_msg */ + +static int +cyz_issue_cmd(struct cyclades_card *cinfo, + __u32 channel, __u8 cmd, __u32 param) +{ + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; + __u32 __iomem *pci_doorbell; + unsigned int index; + + if (!cyz_is_loaded(cinfo)) + return -1; + + index = 0; + pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; + while ((readl(pci_doorbell) & 0xff) != 0) { + if (index++ == 1000) + return (int)(readl(pci_doorbell) & 0xff); + udelay(50L); + } + cy_writel(&board_ctrl->hcmd_channel, channel); + cy_writel(&board_ctrl->hcmd_param, param); + cy_writel(pci_doorbell, (long)cmd); + + return 0; +} /* cyz_issue_cmd */ + +static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) +{ + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; + struct cyclades_card *cinfo = info->card; + unsigned int char_count; + int len; +#ifdef BLOCKMOVE + unsigned char *buf; +#else + char data; +#endif + __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; + + rx_get = new_rx_get = readl(&buf_ctrl->rx_get); + rx_put = readl(&buf_ctrl->rx_put); + rx_bufsize = readl(&buf_ctrl->rx_bufsize); + rx_bufaddr = readl(&buf_ctrl->rx_bufaddr); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + + if (char_count) { +#ifdef CY_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + if (tty == NULL) { + /* flush received characters */ + new_rx_get = (new_rx_get + char_count) & + (rx_bufsize - 1); + info->rflush_count++; + } else { +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while (1) { + len = tty_prepare_flip_string(tty, &buf, + char_count); + if (!len) + break; + + len = min_t(unsigned int, min(len, char_count), + rx_bufsize - new_rx_get); + + memcpy_fromio(buf, cinfo->base_addr + + rx_bufaddr + new_rx_get, len); + + new_rx_get = (new_rx_get + len) & + (rx_bufsize - 1); + char_count -= len; + info->icount.rx += len; + info->idle_stats.recv_bytes += len; + } +#else + len = tty_buffer_request_room(tty, char_count); + while (len--) { + data = readb(cinfo->base_addr + rx_bufaddr + + new_rx_get); + new_rx_get = (new_rx_get + 1) & + (rx_bufsize - 1); + tty_insert_flip_char(tty, data, TTY_NORMAL); + info->idle_stats.recv_bytes++; + info->icount.rx++; + } +#endif +#ifdef CONFIG_CYZ_INTR + /* Recalculate the number of chars in the RX buffer and issue + a cmd in case it's higher than the RX high water mark */ + rx_put = readl(&buf_ctrl->rx_put); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + if (char_count >= readl(&buf_ctrl->rx_threshold) && + !timer_pending(&cyz_rx_full_timer[ + info->line])) + mod_timer(&cyz_rx_full_timer[info->line], + jiffies + 1); +#endif + info->idle_stats.recv_idle = jiffies; + tty_schedule_flip(tty); + } + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, new_rx_get); + } +} + +static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) +{ + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; + struct cyclades_card *cinfo = info->card; + u8 data; + unsigned int char_count; +#ifdef BLOCKMOVE + int small_count; +#endif + __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr; + + if (info->xmit_cnt <= 0) /* Nothing to transmit */ + return; + + tx_get = readl(&buf_ctrl->tx_get); + tx_put = readl(&buf_ctrl->tx_put); + tx_bufsize = readl(&buf_ctrl->tx_bufsize); + tx_bufaddr = readl(&buf_ctrl->tx_bufaddr); + if (tx_put >= tx_get) + char_count = tx_get - tx_put - 1 + tx_bufsize; + else + char_count = tx_get - tx_put - 1; + + if (char_count) { + + if (tty == NULL) + goto ztxdone; + + if (info->x_char) { /* send special char */ + data = info->x_char; + + cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + info->x_char = 0; + char_count--; + info->icount.tx++; + } +#ifdef BLOCKMOVE + while (0 < (small_count = min_t(unsigned int, + tx_bufsize - tx_put, min_t(unsigned int, + (SERIAL_XMIT_SIZE - info->xmit_tail), + min_t(unsigned int, info->xmit_cnt, + char_count))))) { + + memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + + tx_put), + &info->port.xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); + char_count -= small_count; + info->icount.tx += small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = (info->xmit_tail + small_count) & + (SERIAL_XMIT_SIZE - 1); + } +#else + while (info->xmit_cnt && char_count) { + data = info->port.xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) & + (SERIAL_XMIT_SIZE - 1); + + cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + char_count--; + info->icount.tx++; + } +#endif + tty_wakeup(tty); +ztxdone: + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); + } +} + +static void cyz_handle_cmd(struct cyclades_card *cinfo) +{ + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; + struct tty_struct *tty; + struct cyclades_port *info; + __u32 channel, param, fw_ver; + __u8 cmd; + int special_count; + int delta_count; + + fw_ver = readl(&board_ctrl->fw_version); + + while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { + special_count = 0; + delta_count = 0; + info = &cinfo->ports[channel]; + tty = tty_port_tty_get(&info->port); + if (tty == NULL) + continue; + + switch (cmd) { + case C_CM_PR_ERROR: + tty_insert_flip_char(tty, 0, TTY_PARITY); + info->icount.rx++; + special_count++; + break; + case C_CM_FR_ERROR: + tty_insert_flip_char(tty, 0, TTY_FRAME); + info->icount.rx++; + special_count++; + break; + case C_CM_RXBRK: + tty_insert_flip_char(tty, 0, TTY_BREAK); + info->icount.rx++; + special_count++; + break; + case C_CM_MDCD: + info->icount.dcd++; + delta_count++; + if (info->port.flags & ASYNC_CHECK_CD) { + u32 dcd = fw_ver > 241 ? param : + readl(&info->u.cyz.ch_ctrl->rs_status); + if (dcd & C_RS_DCD) + wake_up_interruptible(&info->port.open_wait); + else + tty_hangup(tty); + } + break; + case C_CM_MCTS: + info->icount.cts++; + delta_count++; + break; + case C_CM_MRI: + info->icount.rng++; + delta_count++; + break; + case C_CM_MDSR: + info->icount.dsr++; + delta_count++; + break; +#ifdef Z_WAKE + case C_CM_IOCTLW: + complete(&info->shutdown_wait); + break; +#endif +#ifdef CONFIG_CYZ_INTR + case C_CM_RXHIWM: + case C_CM_RXNNDT: + case C_CM_INTBACK2: + /* Reception Interrupt */ +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " + "port %ld\n", info->card, channel); +#endif + cyz_handle_rx(info, tty); + break; + case C_CM_TXBEMPTY: + case C_CM_TXLOWWM: + case C_CM_INTBACK: + /* Transmission Interrupt */ +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " + "port %ld\n", info->card, channel); +#endif + cyz_handle_tx(info, tty); + break; +#endif /* CONFIG_CYZ_INTR */ + case C_CM_FATAL: + /* should do something with this !!! */ + break; + default: + break; + } + if (delta_count) + wake_up_interruptible(&info->port.delta_msr_wait); + if (special_count) + tty_schedule_flip(tty); + tty_kref_put(tty); + } +} + +#ifdef CONFIG_CYZ_INTR +static irqreturn_t cyz_interrupt(int irq, void *dev_id) +{ + struct cyclades_card *cinfo = dev_id; + + if (unlikely(!cyz_is_loaded(cinfo))) { +#ifdef CY_DEBUG_INTERRUPTS + printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " + "(IRQ%d).\n", irq); +#endif + return IRQ_NONE; + } + + /* Handle the interrupts */ + cyz_handle_cmd(cinfo); + + return IRQ_HANDLED; +} /* cyz_interrupt */ + +static void cyz_rx_restart(unsigned long arg) +{ + struct cyclades_port *info = (struct cyclades_port *)arg; + struct cyclades_card *card = info->card; + int retval; + __u32 channel = info->line - card->first_line; + unsigned long flags; + + spin_lock_irqsave(&card->card_lock, flags); + retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n", + info->line, retval); + } + spin_unlock_irqrestore(&card->card_lock, flags); +} + +#else /* CONFIG_CYZ_INTR */ + +static void cyz_poll(unsigned long arg) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info; + unsigned long expires = jiffies + HZ; + unsigned int port, card; + + for (card = 0; card < NR_CARDS; card++) { + cinfo = &cy_card[card]; + + if (!cy_is_Z(cinfo)) + continue; + if (!cyz_is_loaded(cinfo)) + continue; + + /* Skip first polling cycle to avoid racing conditions with the FW */ + if (!cinfo->intr_enabled) { + cinfo->intr_enabled = 1; + continue; + } + + cyz_handle_cmd(cinfo); + + for (port = 0; port < cinfo->nports; port++) { + struct tty_struct *tty; + + info = &cinfo->ports[port]; + tty = tty_port_tty_get(&info->port); + /* OK to pass NULL to the handle functions below. + They need to drop the data in that case. */ + + if (!info->throttle) + cyz_handle_rx(info, tty); + cyz_handle_tx(info, tty); + tty_kref_put(tty); + } + /* poll every 'cyz_polling_cycle' period */ + expires = jiffies + cyz_polling_cycle; + } + mod_timer(&cyz_timerlist, expires); +} /* cyz_poll */ + +#endif /* CONFIG_CYZ_INTR */ + +/********** End of block of Cyclades-Z specific code *********/ +/***********************************************************/ + +/* This is called whenever a port becomes active; + interrupts are enabled and DTR & RTS are turned on. + */ +static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) +{ + struct cyclades_card *card; + unsigned long flags; + int retval = 0; + int channel; + unsigned long page; + + card = info->card; + channel = info->line - card->first_line; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + spin_lock_irqsave(&card->card_lock, flags); + + if (info->port.flags & ASYNC_INITIALIZED) + goto errout; + + if (!info->type) { + set_bit(TTY_IO_ERROR, &tty->flags); + goto errout; + } + + if (info->port.xmit_buf) + free_page(page); + else + info->port.xmit_buf = (unsigned char *)page; + + spin_unlock_irqrestore(&card->card_lock, flags); + + cy_set_line_char(info, tty); + + if (!cy_is_Z(card)) { + channel &= 0x03; + + spin_lock_irqsave(&card->card_lock, flags); + + cyy_writeb(info, CyCAR, channel); + + cyy_writeb(info, CyRTPR, + (info->default_timeout ? info->default_timeout : 0x02)); + /* 10ms rx timeout */ + + cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); + + cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); + + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); + } else { + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + + if (!cyz_is_loaded(card)) + return -ENODEV; + +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " + "base_addr %p\n", card, channel, card->base_addr); +#endif + spin_lock_irqsave(&card->card_lock, flags); + + cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); +#ifdef Z_WAKE +#ifdef CONFIG_CYZ_INTR + cy_writel(&ch_ctrl->intr_enable, + C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | + C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); +#else + cy_writel(&ch_ctrl->intr_enable, + C_IN_IOCTLW | C_IN_MDCD); +#endif /* CONFIG_CYZ_INTR */ +#else +#ifdef CONFIG_CYZ_INTR + cy_writel(&ch_ctrl->intr_enable, + C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | + C_IN_RXNNDT | C_IN_MDCD); +#else + cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); +#endif /* CONFIG_CYZ_INTR */ +#endif /* Z_WAKE */ + + retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was " + "%x\n", info->line, retval); + } + + /* Flush RX buffers before raising DTR and RTS */ + retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was " + "%x\n", info->line, retval); + } + + /* set timeout !!! */ + /* set RTS and DTR !!! */ + tty_port_raise_dtr_rts(&info->port); + + /* enable send, recv, modem !!! */ + } + + info->port.flags |= ASYNC_INITIALIZED; + + clear_bit(TTY_IO_ERROR, &tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + info->breakon = info->breakoff = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; + + spin_unlock_irqrestore(&card->card_lock, flags); + +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc startup done\n"); +#endif + return 0; + +errout: + spin_unlock_irqrestore(&card->card_lock, flags); + free_page(page); + return retval; +} /* startup */ + +static void start_xmit(struct cyclades_port *info) +{ + struct cyclades_card *card = info->card; + unsigned long flags; + int channel = info->line - card->first_line; + + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { +#ifdef CONFIG_CYZ_INTR + int retval; + + spin_lock_irqsave(&card->card_lock, flags); + retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " + "%x\n", info->line, retval); + } + spin_unlock_irqrestore(&card->card_lock, flags); +#else /* CONFIG_CYZ_INTR */ + /* Don't have to do anything at this time */ +#endif /* CONFIG_CYZ_INTR */ + } +} /* start_xmit */ + +/* + * This routine shuts down a serial port; interrupts are disabled, + * and DTR is dropped if the hangup on close termio flag is on. + */ +static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) +{ + struct cyclades_card *card; + unsigned long flags; + int channel; + + if (!(info->port.flags & ASYNC_INITIALIZED)) + return; + + card = info->card; + channel = info->line - card->first_line; + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + + /* Clear delta_msr_wait queue to avoid mem leaks. */ + wake_up_interruptible(&info->port.delta_msr_wait); + + if (info->port.xmit_buf) { + unsigned char *temp; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; + free_page((unsigned long)temp); + } + if (tty->termios->c_cflag & HUPCL) + cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); + + cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ + + set_bit(TTY_IO_ERROR, &tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; + spin_unlock_irqrestore(&card->card_lock, flags); + } else { +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " + "base_addr %p\n", card, channel, card->base_addr); +#endif + + if (!cyz_is_loaded(card)) + return; + + spin_lock_irqsave(&card->card_lock, flags); + + if (info->port.xmit_buf) { + unsigned char *temp; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; + free_page((unsigned long)temp); + } + + if (tty->termios->c_cflag & HUPCL) + tty_port_lower_dtr_rts(&info->port); + + set_bit(TTY_IO_ERROR, &tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; + + spin_unlock_irqrestore(&card->card_lock, flags); + } + +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc shutdown done\n"); +#endif +} /* shutdown */ + +/* + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ + */ + +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +static int cy_open(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info; + unsigned int i, line; + int retval; + + line = tty->index; + if (tty->index < 0 || NR_PORTS <= line) + return -ENODEV; + + for (i = 0; i < NR_CARDS; i++) + if (line < cy_card[i].first_line + cy_card[i].nports && + line >= cy_card[i].first_line) + break; + if (i >= NR_CARDS) + return -ENODEV; + info = &cy_card[i].ports[line - cy_card[i].first_line]; + if (info->line < 0) + return -ENODEV; + + /* If the card's firmware hasn't been loaded, + treat it as absent from the system. This + will make the user pay attention. + */ + if (cy_is_Z(info->card)) { + struct cyclades_card *cinfo = info->card; + struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS; + + if (!cyz_is_loaded(cinfo)) { + if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) && + readl(&firm_id->signature) == + ZFIRM_HLT) { + printk(KERN_ERR "cyc:Cyclades-Z Error: you " + "need an external power supply for " + "this number of ports.\nFirmware " + "halted.\n"); + } else { + printk(KERN_ERR "cyc:Cyclades-Z firmware not " + "yet loaded\n"); + } + return -ENODEV; + } +#ifdef CONFIG_CYZ_INTR + else { + /* In case this Z board is operating in interrupt mode, its + interrupts should be enabled as soon as the first open + happens to one of its ports. */ + if (!cinfo->intr_enabled) { + u16 intr; + + /* Enable interrupts on the PLX chip */ + intr = readw(&cinfo->ctl_addr.p9060-> + intr_ctrl_stat) | 0x0900; + cy_writew(&cinfo->ctl_addr.p9060-> + intr_ctrl_stat, intr); + /* Enable interrupts on the FW */ + retval = cyz_issue_cmd(cinfo, 0, + C_CM_IRQ_ENBL, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:IRQ enable retval " + "was %x\n", retval); + } + cinfo->intr_enabled = 1; + } + } +#endif /* CONFIG_CYZ_INTR */ + /* Make sure this Z port really exists in hardware */ + if (info->line > (cinfo->first_line + cinfo->nports - 1)) + return -ENODEV; + } +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); +#endif + tty->driver_data = info; + if (serial_paranoia_check(info, tty->name, "cy_open")) + return -ENODEV; + +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, + info->port.count); +#endif + info->port.count++; +#ifdef CY_DEBUG_COUNT + printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", + current->pid, info->port.count); +#endif + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + } + + /* + * Start up serial port + */ + retval = cy_startup(info, tty); + if (retval) + return retval; + + retval = tty_port_block_til_ready(&info->port, tty, filp); + if (retval) { +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " + "with %d\n", retval); +#endif + return retval; + } + + info->throttle = 0; + tty_port_tty_set(&info->port, tty); + +#ifdef CY_DEBUG_OPEN + printk(KERN_DEBUG "cyc:cy_open done\n"); +#endif + return 0; +} /* cy_open */ + +/* + * cy_wait_until_sent() --- wait until the transmitter is empty + */ +static void cy_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct cyclades_card *card; + struct cyclades_port *info = tty->driver_data; + unsigned long orig_jiffies; + int char_time; + + if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent")) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + 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. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time <= 0) + char_time = 1; + if (timeout < 0) + timeout = 0; + if (timeout) + char_time = min(char_time, timeout); + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2 * info->timeout) + timeout = 2 * info->timeout; +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk(KERN_DEBUG "In cy_wait_until_sent(%d) check=%d, jiff=%lu...", + timeout, char_time, jiffies); +#endif + card = info->card; + if (!cy_is_Z(card)) { + while (cyy_readb(info, CySRER) & CyTxRdy) { +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies); +#endif + if (msleep_interruptible(jiffies_to_msecs(char_time))) + break; + if (timeout && time_after(jiffies, orig_jiffies + + timeout)) + break; + } + } + /* Run one more char cycle */ + msleep_interruptible(jiffies_to_msecs(char_time * 5)); +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies); +#endif +} + +static void cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + int channel, retval; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) + return; + + card = info->card; + channel = info->line - card->first_line; + + spin_lock_irqsave(&card->card_lock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&card->card_lock, flags); + + if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board + buffers as well */ + spin_lock_irqsave(&card->card_lock, flags); + retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " + "was %x\n", info->line, retval); + } + spin_unlock_irqrestore(&card->card_lock, flags); + } + tty_wakeup(tty); +} /* cy_flush_buffer */ + + +static void cy_do_close(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *card; + unsigned long flags; + int channel; + + card = info->card; + channel = info->line - card->first_line; + spin_lock_irqsave(&card->card_lock, flags); + + if (!cy_is_Z(card)) { + /* Stop accepting input */ + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); + if (info->port.flags & ASYNC_INITIALIZED) { + /* Waiting for on-board buffers to be empty before + closing the port */ + spin_unlock_irqrestore(&card->card_lock, flags); + cy_wait_until_sent(port->tty, info->timeout); + spin_lock_irqsave(&card->card_lock, flags); + } + } else { +#ifdef Z_WAKE + /* Waiting for on-board buffers to be empty before closing + the port */ + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int retval; + + if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); + if (retval != 0) { + printk(KERN_DEBUG "cyc:cy_close retval on " + "ttyC%d was %x\n", info->line, retval); + } + spin_unlock_irqrestore(&card->card_lock, flags); + wait_for_completion_interruptible(&info->shutdown_wait); + spin_lock_irqsave(&card->card_lock, flags); + } +#endif + } + spin_unlock_irqrestore(&card->card_lock, flags); + cy_shutdown(info, port->tty); +} + +/* + * This routine is called when a particular tty device is closed. + */ +static void cy_close(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info = tty->driver_data; + if (!info || serial_paranoia_check(info, tty->name, "cy_close")) + return; + tty_port_close(&info->port, tty, filp); +} /* cy_close */ + +/* This routine gets called when tty_write has put something into + * the write_queue. The characters may come from user space or + * kernel space. + * + * This routine will return the number of characters actually + * accepted for writing. + * + * If the port is not already transmitting stuff, start it off by + * enabling interrupts. The interrupt service routine will then + * ensure that the characters are sent. + * If the port is already active, there is no need to kick it. + * + */ +static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + int c, ret = 0; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_write")) + return 0; + + if (!info->port.xmit_buf) + return 0; + + spin_lock_irqsave(&info->card->card_lock, flags); + while (1) { + c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); + c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); + + if (c <= 0) + break; + + memcpy(info->port.xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & + (SERIAL_XMIT_SIZE - 1); + info->xmit_cnt += c; + buf += c; + count -= c; + ret += c; + } + spin_unlock_irqrestore(&info->card->card_lock, flags); + + info->idle_stats.xmit_bytes += ret; + info->idle_stats.xmit_idle = jiffies; + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) + start_xmit(info); + + return ret; +} /* cy_write */ + +/* + * This routine is called by the kernel to write a single + * character to the tty device. If the kernel uses this routine, + * it must call the flush_chars() routine (if defined) when it is + * done stuffing characters into the driver. If there is no room + * in the queue, the character is ignored. + */ +static int cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = tty->driver_data; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_put_char")) + return 0; + + if (!info->port.xmit_buf) + return 0; + + spin_lock_irqsave(&info->card->card_lock, flags); + if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { + spin_unlock_irqrestore(&info->card->card_lock, flags); + return 0; + } + + info->port.xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + info->idle_stats.xmit_bytes++; + info->idle_stats.xmit_idle = jiffies; + spin_unlock_irqrestore(&info->card->card_lock, flags); + return 1; +} /* cy_put_char */ + +/* + * This routine is called by the kernel after it has written a + * series of characters to the tty device using put_char(). + */ +static void cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->port.xmit_buf) + return; + + start_xmit(info); +} /* cy_flush_chars */ + +/* + * This routine returns the numbers of characters the tty driver + * will accept for queuing to be written. This number is subject + * to change as output buffers get emptied, or if the output flow + * control is activated. + */ +static int cy_write_room(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + int ret; + +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} /* cy_write_room */ + +static int cy_chars_in_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) + return 0; + +#ifdef Z_EXT_CHARS_IN_BUFFER + if (!cy_is_Z(info->card)) { +#endif /* Z_EXT_CHARS_IN_BUFFER */ +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt); +#endif + return info->xmit_cnt; +#ifdef Z_EXT_CHARS_IN_BUFFER + } else { + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; + int char_count; + __u32 tx_put, tx_get, tx_bufsize; + + tx_get = readl(&buf_ctrl->tx_get); + tx_put = readl(&buf_ctrl->tx_put); + tx_bufsize = readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_put - tx_get; + else + char_count = tx_put - tx_get + tx_bufsize; +#ifdef CY_DEBUG_IO + printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt + char_count); +#endif + return info->xmit_cnt + char_count; + } +#endif /* Z_EXT_CHARS_IN_BUFFER */ +} /* cy_chars_in_buffer */ + +/* + * ------------------------------------------------------------ + * cy_ioctl() and friends + * ------------------------------------------------------------ + */ + +static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) +{ + int co, co_val, bpr; + __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : + 25000000); + + if (baud == 0) { + info->tbpr = info->tco = info->rbpr = info->rco = 0; + return; + } + + /* determine which prescaler to use */ + for (co = 4, co_val = 2048; co; co--, co_val >>= 2) { + if (cy_clock / co_val / baud > 63) + break; + } + + bpr = (cy_clock / co_val * 2 / baud + 1) / 2; + if (bpr > 255) + bpr = 255; + + info->tbpr = info->rbpr = bpr; + info->tco = info->rco = co; +} + +/* + * This routine finds or computes the various line characteristics. + * It used to be called config_setup + */ +static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) +{ + struct cyclades_card *card; + unsigned long flags; + int channel; + unsigned cflag, iflag; + int baud, baud_rate = 0; + int i; + + if (!tty->termios) /* XXX can this happen at all? */ + return; + + if (info->line == -1) + return; + + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; + + /* + * Set up the tty->alt_speed kludge + */ + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + tty->alt_speed = 460800; + + card = info->card; + channel = info->line - card->first_line; + + if (!cy_is_Z(card)) { + u32 cflags; + + /* baud rate */ + baud = tty_get_baud_rate(tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == + ASYNC_SPD_CUST) { + if (info->custom_divisor) + baud_rate = info->baud / info->custom_divisor; + else + baud_rate = info->baud; + } else if (baud > CD1400_MAX_SPEED) { + baud = CD1400_MAX_SPEED; + } + /* find the baud index */ + for (i = 0; i < 20; i++) { + if (baud == baud_table[i]) + break; + } + if (i == 20) + i = 19; /* CD1400_MAX_SPEED */ + + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == + ASYNC_SPD_CUST) { + cyy_baud_calc(info, baud_rate); + } else { + if (info->chip_rev >= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[i]; /* Tx BPR */ + info->tco = baud_co_60[i]; /* Tx CO */ + info->rbpr = baud_bpr_60[i]; /* Rx BPR */ + info->rco = baud_co_60[i]; /* Rx CO */ + } else { + info->tbpr = baud_bpr_25[i]; /* Tx BPR */ + info->tco = baud_co_25[i]; /* Tx CO */ + info->rbpr = baud_bpr_25[i]; /* Rx BPR */ + info->rco = baud_co_25[i]; /* Rx CO */ + } + } + if (baud_table[i] == 134) { + /* get it right for 134.5 baud */ + info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + + 2; + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == + ASYNC_SPD_CUST) { + info->timeout = (info->xmit_fifo_size * HZ * 15 / + baud_rate) + 2; + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size * HZ * 15 / + baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor5 = 0; + info->cor4 = 0; + /* receive threshold */ + info->cor3 = (info->default_threshold ? + info->default_threshold : baud_cor3[i]); + info->cor2 = CyETC; + switch (cflag & CSIZE) { + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if (cflag & CSTOPB) + info->cor1 |= Cy_2_STOP; + + if (cflag & PARENB) { + if (cflag & PARODD) + info->cor1 |= CyPARITY_O; + else + info->cor1 |= CyPARITY_E; + } else + info->cor1 |= CyPARITY_NONE; + + /* CTS flow control flag */ + if (cflag & CRTSCTS) { + info->port.flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + } else { + info->port.flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } + if (cflag & CLOCAL) + info->port.flags &= ~ASYNC_CHECK_CD; + else + info->port.flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + channel &= 0x03; + + spin_lock_irqsave(&card->card_lock, flags); + cyy_writeb(info, CyCAR, channel); + + /* tx and rx baud rate */ + + cyy_writeb(info, CyTCOR, info->tco); + cyy_writeb(info, CyTBPR, info->tbpr); + cyy_writeb(info, CyRCOR, info->rco); + cyy_writeb(info, CyRBPR, info->rbpr); + + /* set line characteristics according configuration */ + + cyy_writeb(info, CySCHR1, START_CHAR(tty)); + cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); + cyy_writeb(info, CyCOR1, info->cor1); + cyy_writeb(info, CyCOR2, info->cor2); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_writeb(info, CyCOR4, info->cor4); + cyy_writeb(info, CyCOR5, info->cor5); + + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | + CyCOR3ch); + + /* !!! Is this needed? */ + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, CyRTPR, + (info->default_timeout ? info->default_timeout : 0x02)); + /* 10ms rx timeout */ + + cflags = CyCTS; + if (!C_CLOCAL(tty)) + cflags |= CyDSR | CyRI | CyDCD; + /* without modem intr */ + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); + /* act on 1->0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) + cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); + else + cyy_writeb(info, CyMCOR1, cflags); + /* act on 0->1 modem transitions */ + cyy_writeb(info, CyMCOR2, cflags); + + if (i == 0) /* baud rate is zero, turn off line */ + cyy_change_rts_dtr(info, 0, TIOCM_DTR); + else + cyy_change_rts_dtr(info, TIOCM_DTR, 0); + + clear_bit(TTY_IO_ERROR, &tty->flags); + spin_unlock_irqrestore(&card->card_lock, flags); + + } else { + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + __u32 sw_flow; + int retval; + + if (!cyz_is_loaded(card)) + return; + + /* baud rate */ + baud = tty_get_baud_rate(tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == + ASYNC_SPD_CUST) { + if (info->custom_divisor) + baud_rate = info->baud / info->custom_divisor; + else + baud_rate = info->baud; + } else if (baud > CYZ_MAX_SPEED) { + baud = CYZ_MAX_SPEED; + } + cy_writel(&ch_ctrl->comm_baud, baud); + + if (baud == 134) { + /* get it right for 134.5 baud */ + info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + + 2; + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == + ASYNC_SPD_CUST) { + info->timeout = (info->xmit_fifo_size * HZ * 15 / + baud_rate) + 2; + } else if (baud) { + info->timeout = (info->xmit_fifo_size * HZ * 15 / + baud) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5); + break; + case CS6: + cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6); + break; + case CS7: + cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7); + break; + case CS8: + cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8); + break; + } + if (cflag & CSTOPB) { + cy_writel(&ch_ctrl->comm_data_l, + readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); + } else { + cy_writel(&ch_ctrl->comm_data_l, + readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); + } + if (cflag & PARENB) { + if (cflag & PARODD) + cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); + else + cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); + } else + cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); + + /* CTS flow control flag */ + if (cflag & CRTSCTS) { + cy_writel(&ch_ctrl->hw_flow, + readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); + } else { + cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) & + ~(C_RS_CTS | C_RS_RTS)); + } + /* As the HW flow control is done in firmware, the driver + doesn't need to care about it */ + info->port.flags &= ~ASYNC_CTS_FLOW; + + /* XON/XOFF/XANY flow control flags */ + sw_flow = 0; + if (iflag & IXON) { + sw_flow |= C_FL_OXX; + if (iflag & IXANY) + sw_flow |= C_FL_OIXANY; + } + cy_writel(&ch_ctrl->sw_flow, sw_flow); + + retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:set_line_char retval on ttyC%d " + "was %x\n", info->line, retval); + } + + /* CD sensitivity */ + if (cflag & CLOCAL) + info->port.flags &= ~ASYNC_CHECK_CD; + else + info->port.flags |= ASYNC_CHECK_CD; + + if (baud == 0) { /* baud rate is zero, turn off line */ + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n"); +#endif + } else { + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n"); +#endif + } + + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " + "was %x\n", info->line, retval); + } + + clear_bit(TTY_IO_ERROR, &tty->flags); + } +} /* set_line_char */ + +static int cy_get_serial_info(struct cyclades_port *info, + struct serial_struct __user *retinfo) +{ + struct cyclades_card *cinfo = info->card; + struct serial_struct tmp = { + .type = info->type, + .line = info->line, + .port = (info->card - cy_card) * 0x100 + info->line - + cinfo->first_line, + .irq = cinfo->irq, + .flags = info->port.flags, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, + .baud_base = info->baud, + .custom_divisor = info->custom_divisor, + .hub6 = 0, /*!!! */ + }; + return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; +} + +static int +cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, + struct serial_struct __user *new_info) +{ + struct serial_struct new_serial; + int ret; + + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + mutex_lock(&info->port.mutex); + if (!capable(CAP_SYS_ADMIN)) { + if (new_serial.close_delay != info->port.close_delay || + new_serial.baud_base != info->baud || + (new_serial.flags & ASYNC_FLAGS & + ~ASYNC_USR_MASK) != + (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) + { + mutex_unlock(&info->port.mutex); + return -EPERM; + } + info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK); + info->baud = new_serial.baud_base; + info->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud = new_serial.baud_base; + info->custom_divisor = new_serial.custom_divisor; + info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS); + info->port.close_delay = new_serial.close_delay * HZ / 100; + info->port.closing_wait = new_serial.closing_wait * HZ / 100; + +check_and_exit: + if (info->port.flags & ASYNC_INITIALIZED) { + cy_set_line_char(info, tty); + ret = 0; + } else { + ret = cy_startup(info, tty); + } + mutex_unlock(&info->port.mutex); + return ret; +} /* set_serial_info */ + +/* + * 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 cyclades_port *info, unsigned int __user *value) +{ + struct cyclades_card *card = info->card; + unsigned int result; + unsigned long flags; + u8 status; + + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); + spin_unlock_irqrestore(&card->card_lock, flags); + result = (status ? 0 : TIOCSER_TEMT); + } else { + /* Not supported yet */ + return -EINVAL; + } + return put_user(result, (unsigned long __user *)value); +} + +static int cy_tiocmget(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + int result; + + if (serial_paranoia_check(info, tty->name, __func__)) + return -ENODEV; + + card = info->card; + + if (!cy_is_Z(card)) { + unsigned long flags; + int channel = info->line - card->first_line; + u8 status; + + spin_lock_irqsave(&card->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + status = cyy_readb(info, CyMSVR1); + status |= cyy_readb(info, CyMSVR2); + spin_unlock_irqrestore(&card->card_lock, flags); + + if (info->rtsdtr_inv) { + result = ((status & CyRTS) ? TIOCM_DTR : 0) | + ((status & CyDTR) ? TIOCM_RTS : 0); + } else { + result = ((status & CyRTS) ? TIOCM_RTS : 0) | + ((status & CyDTR) ? TIOCM_DTR : 0); + } + result |= ((status & CyDCD) ? TIOCM_CAR : 0) | + ((status & CyRI) ? TIOCM_RNG : 0) | + ((status & CyDSR) ? TIOCM_DSR : 0) | + ((status & CyCTS) ? TIOCM_CTS : 0); + } else { + u32 lstatus; + + if (!cyz_is_loaded(card)) { + result = -ENODEV; + goto end; + } + + lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | + ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | + ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | + ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | + ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | + ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); + } +end: + return result; +} /* cy_tiomget */ + +static int +cy_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, __func__)) + return -ENODEV; + + card = info->card; + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + cyy_change_rts_dtr(info, set, clear); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int retval, channel = info->line - card->first_line; + u32 rs; + + if (!cyz_is_loaded(card)) + return -ENODEV; + + spin_lock_irqsave(&card->card_lock, flags); + rs = readl(&ch_ctrl->rs_control); + if (set & TIOCM_RTS) + rs |= C_RS_RTS; + if (clear & TIOCM_RTS) + rs &= ~C_RS_RTS; + if (set & TIOCM_DTR) { + rs |= C_RS_DTR; +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); +#endif + } + if (clear & TIOCM_DTR) { + rs &= ~C_RS_DTR; +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info clearing " + "Z DTR\n"); +#endif + } + cy_writel(&ch_ctrl->rs_control, rs); + retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); + spin_unlock_irqrestore(&card->card_lock, flags); + if (retval != 0) { + printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " + "was %x\n", info->line, retval); + } + } + return 0; +} + +/* + * cy_break() --- routine which turns the break handling on or off + */ +static int cy_break(struct tty_struct *tty, int break_state) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + unsigned long flags; + int retval = 0; + + if (serial_paranoia_check(info, tty->name, "cy_break")) + return -EINVAL; + + card = info->card; + + spin_lock_irqsave(&card->card_lock, flags); + if (!cy_is_Z(card)) { + /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + if (break_state == -1) { + if (!info->breakon) { + info->breakon = 1; + if (!info->xmit_cnt) { + spin_unlock_irqrestore(&card->card_lock, flags); + start_xmit(info); + spin_lock_irqsave(&card->card_lock, flags); + } + } + } else { + if (!info->breakoff) { + info->breakoff = 1; + if (!info->xmit_cnt) { + spin_unlock_irqrestore(&card->card_lock, flags); + start_xmit(info); + spin_lock_irqsave(&card->card_lock, flags); + } + } + } + } else { + if (break_state == -1) { + retval = cyz_issue_cmd(card, + info->line - card->first_line, + C_CM_SET_BREAK, 0L); + if (retval != 0) { + printk(KERN_ERR "cyc:cy_break (set) retval on " + "ttyC%d was %x\n", info->line, retval); + } + } else { + retval = cyz_issue_cmd(card, + info->line - card->first_line, + C_CM_CLR_BREAK, 0L); + if (retval != 0) { + printk(KERN_DEBUG "cyc:cy_break (clr) retval " + "on ttyC%d was %x\n", info->line, + retval); + } + } + } + spin_unlock_irqrestore(&card->card_lock, flags); + return retval; +} /* cy_break */ + +static int set_threshold(struct cyclades_port *info, unsigned long value) +{ + struct cyclades_card *card = info->card; + unsigned long flags; + + if (!cy_is_Z(card)) { + info->cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + + spin_lock_irqsave(&card->card_lock, flags); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); + spin_unlock_irqrestore(&card->card_lock, flags); + } + return 0; +} /* set_threshold */ + +static int get_threshold(struct cyclades_port *info, + unsigned long __user *value) +{ + struct cyclades_card *card = info->card; + + if (!cy_is_Z(card)) { + u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; + return put_user(tmp, value); + } + return 0; +} /* get_threshold */ + +static int set_timeout(struct cyclades_port *info, unsigned long value) +{ + struct cyclades_card *card = info->card; + unsigned long flags; + + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + cyy_writeb(info, CyRTPR, value & 0xff); + spin_unlock_irqrestore(&card->card_lock, flags); + } + return 0; +} /* set_timeout */ + +static int get_timeout(struct cyclades_port *info, + unsigned long __user *value) +{ + struct cyclades_card *card = info->card; + + if (!cy_is_Z(card)) { + u8 tmp = cyy_readb(info, CyRTPR); + return put_user(tmp, value); + } + return 0; +} /* get_timeout */ + +static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, + struct cyclades_icount *cprev) +{ + struct cyclades_icount cnow; + unsigned long flags; + int ret; + + spin_lock_irqsave(&info->card->card_lock, flags); + cnow = info->icount; /* atomic copy */ + spin_unlock_irqrestore(&info->card->card_lock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} + +/* + * This routine allows the tty driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ +static int +cy_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_icount cnow; /* kernel counter temps */ + int ret_val = 0; + unsigned long flags; + void __user *argp = (void __user *)arg; + + if (serial_paranoia_check(info, tty->name, "cy_ioctl")) + return -ENODEV; + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", + info->line, cmd, arg); +#endif + + switch (cmd) { + case CYGETMON: + if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { + ret_val = -EFAULT; + break; + } + memset(&info->mon, 0, sizeof(info->mon)); + break; + case CYGETTHRESH: + ret_val = get_threshold(info, argp); + break; + case CYSETTHRESH: + ret_val = set_threshold(info, arg); + break; + case CYGETDEFTHRESH: + ret_val = put_user(info->default_threshold, + (unsigned long __user *)argp); + break; + case CYSETDEFTHRESH: + info->default_threshold = arg & 0x0f; + break; + case CYGETTIMEOUT: + ret_val = get_timeout(info, argp); + break; + case CYSETTIMEOUT: + ret_val = set_timeout(info, arg); + break; + case CYGETDEFTIMEOUT: + ret_val = put_user(info->default_timeout, + (unsigned long __user *)argp); + break; + case CYSETDEFTIMEOUT: + info->default_timeout = arg & 0xff; + break; + case CYSETRFLOW: + info->rflow = (int)arg; + break; + case CYGETRFLOW: + ret_val = info->rflow; + break; + case CYSETRTSDTR_INV: + info->rtsdtr_inv = (int)arg; + break; + case CYGETRTSDTR_INV: + ret_val = info->rtsdtr_inv; + break; + case CYGETCD1400VER: + ret_val = info->chip_rev; + break; +#ifndef CONFIG_CYZ_INTR + case CYZSETPOLLCYCLE: + cyz_polling_cycle = (arg * HZ) / 1000; + break; + case CYZGETPOLLCYCLE: + ret_val = (cyz_polling_cycle * 1000) / HZ; + break; +#endif /* CONFIG_CYZ_INTR */ + case CYSETWAIT: + info->port.closing_wait = (unsigned short)arg * HZ / 100; + break; + case CYGETWAIT: + ret_val = info->port.closing_wait / (HZ / 100); + break; + case TIOCGSERIAL: + ret_val = cy_get_serial_info(info, argp); + break; + case TIOCSSERIAL: + ret_val = cy_set_serial_info(info, tty, argp); + break; + case TIOCSERGETLSR: /* Get line status register */ + ret_val = get_lsr_info(info, argp); + break; + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + spin_lock_irqsave(&info->card->card_lock, flags); + /* note the counters on entry */ + cnow = info->icount; + spin_unlock_irqrestore(&info->card->card_lock, flags); + ret_val = wait_event_interruptible(info->port.delta_msr_wait, + cy_cflags_changed(info, arg, &cnow)); + break; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + default: + ret_val = -ENOIOCTLCMD; + } + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_ioctl done\n"); +#endif + return ret_val; +} /* cy_ioctl */ + +static int cy_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *sic) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_icount cnow; /* Used to snapshot */ + unsigned long flags; + + spin_lock_irqsave(&info->card->card_lock, flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->card->card_lock, flags); + + sic->cts = cnow.cts; + sic->dsr = cnow.dsr; + sic->rng = cnow.rng; + sic->dcd = cnow.dcd; + sic->rx = cnow.rx; + sic->tx = cnow.tx; + sic->frame = cnow.frame; + sic->overrun = cnow.overrun; + sic->parity = cnow.parity; + sic->brk = cnow.brk; + sic->buf_overrun = cnow.buf_overrun; + return 0; +} + +/* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a + * well-designed tty driver should be prepared to accept the case + * where old == NULL, and try to do something rational. + */ +static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); +#endif + + cy_set_line_char(info, tty); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + cy_start(tty); + } +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->port.open_wait); +#endif +} /* cy_set_termios */ + +/* This function is used to send a high-priority XON/XOFF character to + the device. +*/ +static void cy_send_xchar(struct tty_struct *tty, char ch) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + int channel; + + if (serial_paranoia_check(info, tty->name, "cy_send_xchar")) + return; + + info->x_char = ch; + + if (ch) + cy_start(tty); + + card = info->card; + channel = info->line - card->first_line; + + if (cy_is_Z(card)) { + if (ch == STOP_CHAR(tty)) + cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L); + else if (ch == START_CHAR(tty)) + cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L); + } +} + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled because the input + buffers are close to full. + */ +static void cy_throttle(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + unsigned long flags; + +#ifdef CY_DEBUG_THROTTLE + char buf[64]; + + printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_throttle")) + return; + + card = info->card; + + if (I_IXOFF(tty)) { + if (!cy_is_Z(card)) + cy_send_xchar(tty, STOP_CHAR(tty)); + else + info->throttle = 1; + } + + if (tty->termios->c_cflag & CRTSCTS) { + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + cyy_change_rts_dtr(info, 0, TIOCM_RTS); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { + info->throttle = 1; + } + } +} /* cy_throttle */ + +/* + * This routine notifies the tty driver that it should signal + * that characters can now be sent to the tty without fear of + * overrunning the input buffers of the line disciplines. + */ +static void cy_unthrottle(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + struct cyclades_card *card; + unsigned long flags; + +#ifdef CY_DEBUG_THROTTLE + char buf[64]; + + printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n", + tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + cy_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + card = info->card; + if (!cy_is_Z(card)) { + spin_lock_irqsave(&card->card_lock, flags); + cyy_change_rts_dtr(info, TIOCM_RTS, 0); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { + info->throttle = 0; + } + } +} /* cy_unthrottle */ + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. +*/ +static void cy_stop(struct tty_struct *tty) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info = tty->driver_data; + int channel; + unsigned long flags; + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_stop")) + return; + + cinfo = info->card; + channel = info->line - cinfo->first_line; + if (!cy_is_Z(cinfo)) { + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); + spin_unlock_irqrestore(&cinfo->card_lock, flags); + } +} /* cy_stop */ + +static void cy_start(struct tty_struct *tty) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info = tty->driver_data; + int channel; + unsigned long flags; + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_start")) + return; + + cinfo = info->card; + channel = info->line - cinfo->first_line; + if (!cy_is_Z(cinfo)) { + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); + spin_unlock_irqrestore(&cinfo->card_lock, flags); + } +} /* cy_start */ + +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port *info = tty->driver_data; + +#ifdef CY_DEBUG_OTHER + printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->name, "cy_hangup")) + return; + + cy_flush_buffer(tty); + cy_shutdown(info, tty); + tty_port_hangup(&info->port); +} /* cy_hangup */ + +static int cyy_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + unsigned long flags; + int channel = info->line - cinfo->first_line; + u32 cd; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_writeb(info, CyCAR, channel & 0x03); + cd = cyy_readb(info, CyMSVR1) & CyDCD; + spin_unlock_irqrestore(&cinfo->card_lock, flags); + + return cd; +} + +static void cyy_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + unsigned long flags; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, + raise ? 0 : TIOCM_RTS | TIOCM_DTR); + spin_unlock_irqrestore(&cinfo->card_lock, flags); +} + +static int cyz_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + + return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; +} + +static void cyz_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int ret, channel = info->line - cinfo->first_line; + u32 rs; + + rs = readl(&ch_ctrl->rs_control); + if (raise) + rs |= C_RS_RTS | C_RS_DTR; + else + rs &= ~(C_RS_RTS | C_RS_DTR); + cy_writel(&ch_ctrl->rs_control, rs); + ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); + if (ret != 0) + printk(KERN_ERR "%s: retval on ttyC%d was %x\n", + __func__, info->line, ret); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); +#endif +} + +static const struct tty_port_operations cyy_port_ops = { + .carrier_raised = cyy_carrier_raised, + .dtr_rts = cyy_dtr_rts, + .shutdown = cy_do_close, +}; + +static const struct tty_port_operations cyz_port_ops = { + .carrier_raised = cyz_carrier_raised, + .dtr_rts = cyz_dtr_rts, + .shutdown = cy_do_close, +}; + +/* + * --------------------------------------------------------------------- + * cy_init() and friends + * + * cy_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +static int __devinit cy_init_card(struct cyclades_card *cinfo) +{ + struct cyclades_port *info; + unsigned int channel, port; + + spin_lock_init(&cinfo->card_lock); + cinfo->intr_enabled = 0; + + cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports), + GFP_KERNEL); + if (cinfo->ports == NULL) { + printk(KERN_ERR "Cyclades: cannot allocate ports\n"); + return -ENOMEM; + } + + for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; + channel++, port++) { + info = &cinfo->ports[channel]; + tty_port_init(&info->port); + info->magic = CYCLADES_MAGIC; + info->card = cinfo; + info->line = port; + + info->port.closing_wait = CLOSING_WAIT_DELAY; + info->port.close_delay = 5 * HZ / 10; + info->port.flags = STD_COM_FLAGS; + init_completion(&info->shutdown_wait); + + if (cy_is_Z(cinfo)) { + struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; + struct ZFW_CTRL *zfw_ctrl; + + info->port.ops = &cyz_port_ops; + info->type = PORT_STARTECH; + + zfw_ctrl = cinfo->base_addr + + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; + + if (cinfo->hw_ver == ZO_V1) + info->xmit_fifo_size = CYZ_FIFO_SIZE; + else + info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; +#ifdef CONFIG_CYZ_INTR + setup_timer(&cyz_rx_full_timer[port], + cyz_rx_restart, (unsigned long)info); +#endif + } else { + unsigned short chip_number; + int index = cinfo->bus_index; + + info->port.ops = &cyy_port_ops; + info->type = PORT_CIRRUS; + info->xmit_fifo_size = CyMAX_CHAR_FIFO; + info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = 0x08; /* _very_ small rcv threshold */ + + chip_number = channel / CyPORTS_PER_CHIP; + info->u.cyy.base_addr = cinfo->base_addr + + (cy_chip_offset[chip_number] << index); + info->chip_rev = cyy_readb(info, CyGFRCR); + + if (info->chip_rev >= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[13]; /* Tx BPR */ + info->tco = baud_co_60[13]; /* Tx CO */ + info->rbpr = baud_bpr_60[13]; /* Rx BPR */ + info->rco = baud_co_60[13]; /* Rx CO */ + info->rtsdtr_inv = 1; + } else { + info->tbpr = baud_bpr_25[13]; /* Tx BPR */ + info->tco = baud_co_25[13]; /* Tx CO */ + info->rbpr = baud_bpr_25[13]; /* Rx BPR */ + info->rco = baud_co_25[13]; /* Rx CO */ + info->rtsdtr_inv = 0; + } + info->read_status_mask = CyTIMEOUT | CySPECHAR | + CyBREAK | CyPARITY | CyFRAME | CyOVERRUN; + } + + } + +#ifndef CONFIG_CYZ_INTR + if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) { + mod_timer(&cyz_timerlist, jiffies + 1); +#ifdef CY_PCI_DEBUG + printk(KERN_DEBUG "Cyclades-Z polling initialized\n"); +#endif + } +#endif + return 0; +} + +/* initialize chips on Cyclom-Y card -- return number of valid + chips (which is number of ports/4) */ +static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, + int index) +{ + unsigned int chip_number; + void __iomem *base_addr; + + cy_writeb(true_base_addr + (Cy_HwReset << index), 0); + /* Cy_HwReset is 0x1400 */ + cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0); + /* Cy_ClrIntr is 0x1800 */ + udelay(500L); + + for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; + chip_number++) { + base_addr = + true_base_addr + (cy_chip_offset[chip_number] << index); + mdelay(1); + if (readb(base_addr + (CyCCR << index)) != 0x00) { + /************* + printk(" chip #%d at %#6lx is never idle (CCR != 0)\n", + chip_number, (unsigned long)base_addr); + *************/ + return chip_number; + } + + cy_writeb(base_addr + (CyGFRCR << index), 0); + udelay(10L); + + /* The Cyclom-16Y does not decode address bit 9 and therefore + cannot distinguish between references to chip 0 and a non- + existent chip 4. If the preceding clearing of the supposed + chip 4 GFRCR register appears at chip 0, there is no chip 4 + and this must be a Cyclom-16Y, not a Cyclom-32Ye. + */ + if (chip_number == 4 && readb(true_base_addr + + (cy_chip_offset[0] << index) + + (CyGFRCR << index)) == 0) { + return chip_number; + } + + cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET); + mdelay(1); + + if (readb(base_addr + (CyGFRCR << index)) == 0x00) { + /* + printk(" chip #%d at %#6lx is not responding ", + chip_number, (unsigned long)base_addr); + printk("(GFRCR stayed 0)\n", + */ + return chip_number; + } + if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) != + 0x40) { + /* + printk(" chip #%d at %#6lx is not valid (GFRCR == " + "%#2x)\n", + chip_number, (unsigned long)base_addr, + base_addr[CyGFRCR<= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + /* Impossible to reach 5ms with this chip. + Changed to 2ms instead (f = 500 Hz). */ + cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS); + } else { + /* f = 200 Hz */ + cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS); + } + + /* + printk(" chip #%d at %#6lx is rev 0x%2x\n", + chip_number, (unsigned long)base_addr, + readb(base_addr+(CyGFRCR< NR_PORTS) { + printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " + "more channels are available. Change NR_PORTS " + "in cyclades.c and recompile kernel.\n", + (unsigned long)cy_isa_address); + iounmap(cy_isa_address); + return nboard; + } + /* fill the next cy_card structure available */ + for (j = 0; j < NR_CARDS; j++) { + if (cy_card[j].base_addr == NULL) + break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " + "more cards can be used. Change NR_CARDS in " + "cyclades.c and recompile kernel.\n", + (unsigned long)cy_isa_address); + iounmap(cy_isa_address); + return nboard; + } + + /* allocate IRQ */ + if (request_irq(cy_isa_irq, cyy_interrupt, + IRQF_DISABLED, "Cyclom-Y", &cy_card[j])) { + printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " + "could not allocate IRQ#%d.\n", + (unsigned long)cy_isa_address, cy_isa_irq); + iounmap(cy_isa_address); + return nboard; + } + + /* set cy_card */ + cy_card[j].base_addr = cy_isa_address; + cy_card[j].ctl_addr.p9050 = NULL; + cy_card[j].irq = (int)cy_isa_irq; + cy_card[j].bus_index = 0; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; + cy_card[j].nports = cy_isa_nchan; + if (cy_init_card(&cy_card[j])) { + cy_card[j].base_addr = NULL; + free_irq(cy_isa_irq, &cy_card[j]); + iounmap(cy_isa_address); + continue; + } + nboard++; + + printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " + "%d channels starting from port %d\n", + j + 1, (unsigned long)cy_isa_address, + (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), + cy_isa_irq, cy_isa_nchan, cy_next_channel); + + for (j = cy_next_channel; + j < cy_next_channel + cy_isa_nchan; j++) + tty_register_device(cy_serial_driver, j, NULL); + cy_next_channel += cy_isa_nchan; + } + return nboard; +#else + return 0; +#endif /* CONFIG_ISA */ +} /* cy_detect_isa */ + +#ifdef CONFIG_PCI +static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) +{ + unsigned int a; + + for (a = 0; a < size && *str; a++, str++) + if (*str & 0x80) + return -EINVAL; + + for (; a < size; a++, str++) + if (*str) + return -EINVAL; + + return 0; +} + +static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data, + unsigned int size) +{ + for (; size > 0; size--) { + cy_writel(fpga, *data++); + udelay(10); + } +} + +static void __devinit plx_init(struct pci_dev *pdev, int irq, + struct RUNTIME_9060 __iomem *addr) +{ + /* Reset PLX */ + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); + udelay(100L); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); + + /* Reload Config. Registers from EEPROM */ + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); + udelay(100L); + cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); + + /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, + * the IRQ is lost and, thus, we have to re-write it to the PCI config. + * registers. This will remain here until we find a permanent fix. + */ + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); +} + +static int __devinit __cyz_load_fw(const struct firmware *fw, + const char *name, const u32 mailbox, void __iomem *base, + void __iomem *fpga) +{ + const void *ptr = fw->data; + const struct zfile_header *h = ptr; + const struct zfile_config *c, *cs; + const struct zfile_block *b, *bs; + unsigned int a, tmp, len = fw->size; +#define BAD_FW KERN_ERR "Bad firmware: " + if (len < sizeof(*h)) { + printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); + return -EINVAL; + } + + cs = ptr + h->config_offset; + bs = ptr + h->block_offset; + + if ((void *)(cs + h->n_config) > ptr + len || + (void *)(bs + h->n_blocks) > ptr + len) { + printk(BAD_FW "too short"); + return -EINVAL; + } + + if (cyc_isfwstr(h->name, sizeof(h->name)) || + cyc_isfwstr(h->date, sizeof(h->date))) { + printk(BAD_FW "bad formatted header string\n"); + return -EINVAL; + } + + if (strncmp(name, h->name, sizeof(h->name))) { + printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); + return -EINVAL; + } + + tmp = 0; + for (c = cs; c < cs + h->n_config; c++) { + for (a = 0; a < c->n_blocks; a++) + if (c->block_list[a] > h->n_blocks) { + printk(BAD_FW "bad block ref number in cfgs\n"); + return -EINVAL; + } + if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ + tmp++; + } + if (!tmp) { + printk(BAD_FW "nothing appropriate\n"); + return -EINVAL; + } + + for (b = bs; b < bs + h->n_blocks; b++) + if (b->file_offset + b->size > len) { + printk(BAD_FW "bad block data offset\n"); + return -EINVAL; + } + + /* everything is OK, let's seek'n'load it */ + for (c = cs; c < cs + h->n_config; c++) + if (c->mailbox == mailbox && c->function == 0) + break; + + for (a = 0; a < c->n_blocks; a++) { + b = &bs[c->block_list[a]]; + if (b->type == ZBLOCK_FPGA) { + if (fpga != NULL) + cyz_fpga_copy(fpga, ptr + b->file_offset, + b->size); + } else { + if (base != NULL) + memcpy_toio(base + b->ram_offset, + ptr + b->file_offset, b->size); + } + } +#undef BAD_FW + return 0; +} + +static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, + struct RUNTIME_9060 __iomem *ctl_addr, int irq) +{ + const struct firmware *fw; + struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; + struct CUSTOM_REG __iomem *cust = base_addr; + struct ZFW_CTRL __iomem *pt_zfwctrl; + void __iomem *tmp; + u32 mailbox, status, nchan; + unsigned int i; + int retval; + + retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); + if (retval) { + dev_err(&pdev->dev, "can't get firmware\n"); + goto err; + } + + /* Check whether the firmware is already loaded and running. If + positive, skip this board */ + if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { + u32 cntval = readl(base_addr + 0x190); + + udelay(100); + if (cntval != readl(base_addr + 0x190)) { + /* FW counter is working, FW is running */ + dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " + "Skipping board.\n"); + retval = 0; + goto err_rel; + } + } + + /* start boot */ + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & + ~0x00030800UL); + + mailbox = readl(&ctl_addr->mail_box_0); + + if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) { + /* stops CPU and set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_stop, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + udelay(100); + } + + plx_init(pdev, irq, ctl_addr); + + if (mailbox != 0) { + /* load FPGA */ + retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, + base_addr); + if (retval) + goto err_rel; + if (!__cyz_fpga_loaded(ctl_addr)) { + dev_err(&pdev->dev, "fw upload successful, but fw is " + "not loaded\n"); + goto err_rel; + } + } + + /* stops CPU and set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_stop, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + udelay(100); + + /* clear memory */ + for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) + cy_writeb(tmp, 255); + if (mailbox != 0) { + /* set window to last 512K of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); + for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) + cy_writeb(tmp, 255); + /* set window to beginning of RAM */ + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + } + + retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); + release_firmware(fw); + if (retval) + goto err; + + /* finish boot and start boards */ + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + cy_writel(&cust->cpu_start, 0); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + i = 0; + while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) + msleep(100); + if (status != ZFIRM_ID) { + if (status == ZFIRM_HLT) { + dev_err(&pdev->dev, "you need an external power supply " + "for this number of ports. Firmware halted and " + "board reset.\n"); + retval = -EIO; + goto err; + } + dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " + "some more time\n", status); + while ((status = readl(&fid->signature)) != ZFIRM_ID && + i++ < 200) + msleep(100); + if (status != ZFIRM_ID) { + dev_err(&pdev->dev, "Board not started in 20 seconds! " + "Giving up. (fid->signature = 0x%x)\n", + status); + dev_info(&pdev->dev, "*** Warning ***: if you are " + "upgrading the FW, please power cycle the " + "system before loading the new FW to the " + "Cyclades-Z.\n"); + + if (__cyz_fpga_loaded(ctl_addr)) + plx_init(pdev, irq, ctl_addr); + + retval = -EIO; + goto err; + } + dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", + i / 10); + } + pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); + + dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", + base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), + base_addr + readl(&fid->zfwctrl_addr)); + + nchan = readl(&pt_zfwctrl->board_ctrl.n_channel); + dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", + readl(&pt_zfwctrl->board_ctrl.fw_version), nchan); + + if (nchan == 0) { + dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " + "check the connection between the Z host card and the " + "serial expanders.\n"); + + if (__cyz_fpga_loaded(ctl_addr)) + plx_init(pdev, irq, ctl_addr); + + dev_info(&pdev->dev, "Null number of ports detected. Board " + "reset.\n"); + retval = 0; + goto err; + } + + cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); + cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); + + /* + Early firmware failed to start looking for commands. + This enables firmware interrupts for those commands. + */ + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | + (1 << 17)); + cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | + 0x00030800UL); + + return nchan; +err_rel: + release_firmware(fw); +err: + return retval; +} + +static int __devinit cy_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + void __iomem *addr0 = NULL, *addr2 = NULL; + char *card_name = NULL; + u32 uninitialized_var(mailbox); + unsigned int device_id, nchan = 0, card_no, i; + unsigned char plx_ver; + int retval, irq; + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "cannot enable device\n"); + goto err; + } + + /* read PCI configuration area */ + irq = pdev->irq; + device_id = pdev->device & ~PCI_DEVICE_ID_MASK; + +#if defined(__alpha__) + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ + dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " + "addresses on Alpha systems.\n"); + retval = -EIO; + goto err_dis; + } +#endif + if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { + dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " + "addresses\n"); + retval = -EIO; + goto err_dis; + } + + if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { + dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " + "it...\n"); + pdev->resource[2].flags &= ~IORESOURCE_IO; + } + + retval = pci_request_regions(pdev, "cyclades"); + if (retval) { + dev_err(&pdev->dev, "failed to reserve resources\n"); + goto err_dis; + } + + retval = -EIO; + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || + device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { + card_name = "Cyclom-Y"; + + addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Yctl); + if (addr0 == NULL) { + dev_err(&pdev->dev, "can't remap ctl region\n"); + goto err_reg; + } + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + CyPCI_Ywin); + if (addr2 == NULL) { + dev_err(&pdev->dev, "can't remap base region\n"); + goto err_unmap; + } + + nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); + if (nchan == 0) { + dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " + "Serial-Modules\n"); + goto err_unmap; + } + } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { + struct RUNTIME_9060 __iomem *ctl_addr; + + ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Zctl); + if (addr0 == NULL) { + dev_err(&pdev->dev, "can't remap ctl region\n"); + goto err_reg; + } + + /* Disable interrupts on the PLX before resetting it */ + cy_writew(&ctl_addr->intr_ctrl_stat, + readw(&ctl_addr->intr_ctrl_stat) & ~0x0900); + + plx_init(pdev, irq, addr0); + + mailbox = readl(&ctl_addr->mail_box_0); + + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); + if (addr2 == NULL) { + dev_err(&pdev->dev, "can't remap base region\n"); + goto err_unmap; + } + + if (mailbox == ZE_V1) { + card_name = "Cyclades-Ze"; + } else { + card_name = "Cyclades-8Zo"; +#ifdef CY_PCI_DEBUG + if (mailbox == ZO_V1) { + cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); + dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " + "id %lx, ver %lx\n", (ulong)(0xff & + readl(&((struct CUSTOM_REG *)addr2)-> + fpga_id)), (ulong)(0xff & + readl(&((struct CUSTOM_REG *)addr2)-> + fpga_version))); + cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); + } else { + dev_info(&pdev->dev, "Cyclades-Z/PCI: New " + "Cyclades-Z board. FPGA not loaded\n"); + } +#endif + /* The following clears the firmware id word. This + ensures that the driver will not attempt to talk to + the board until it has been properly initialized. + */ + if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) + cy_writel(addr2 + ID_ADDRESS, 0L); + } + + retval = cyz_load_fw(pdev, addr2, addr0, irq); + if (retval <= 0) + goto err_unmap; + nchan = retval; + } + + if ((cy_next_channel + nchan) > NR_PORTS) { + dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " + "channels are available. Change NR_PORTS in " + "cyclades.c and recompile kernel.\n"); + goto err_unmap; + } + /* fill the next cy_card structure available */ + for (card_no = 0; card_no < NR_CARDS; card_no++) { + if (cy_card[card_no].base_addr == NULL) + break; + } + if (card_no == NR_CARDS) { /* no more cy_cards available */ + dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " + "more cards can be used. Change NR_CARDS in " + "cyclades.c and recompile kernel.\n"); + goto err_unmap; + } + + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || + device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { + /* allocate IRQ */ + retval = request_irq(irq, cyy_interrupt, + IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); + if (retval) { + dev_err(&pdev->dev, "could not allocate IRQ\n"); + goto err_unmap; + } + cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; + } else { + struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; + struct ZFW_CTRL __iomem *zfw_ctrl; + + zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + + cy_card[card_no].hw_ver = mailbox; + cy_card[card_no].num_chips = (unsigned int)-1; + cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; +#ifdef CONFIG_CYZ_INTR + /* allocate IRQ only if board has an IRQ */ + if (irq != 0 && irq != 255) { + retval = request_irq(irq, cyz_interrupt, + IRQF_SHARED, "Cyclades-Z", + &cy_card[card_no]); + if (retval) { + dev_err(&pdev->dev, "could not allocate IRQ\n"); + goto err_unmap; + } + } +#endif /* CONFIG_CYZ_INTR */ + } + + /* set cy_card */ + cy_card[card_no].base_addr = addr2; + cy_card[card_no].ctl_addr.p9050 = addr0; + cy_card[card_no].irq = irq; + cy_card[card_no].bus_index = 1; + cy_card[card_no].first_line = cy_next_channel; + cy_card[card_no].nports = nchan; + retval = cy_init_card(&cy_card[card_no]); + if (retval) + goto err_null; + + pci_set_drvdata(pdev, &cy_card[card_no]); + + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || + device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { + /* enable interrupts in the PCI interface */ + plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; + switch (plx_ver) { + case PLX_9050: + cy_writeb(addr0 + 0x4c, 0x43); + break; + + case PLX_9060: + case PLX_9080: + default: /* Old boards, use PLX_9060 */ + { + struct RUNTIME_9060 __iomem *ctl_addr = addr0; + plx_init(pdev, irq, ctl_addr); + cy_writew(&ctl_addr->intr_ctrl_stat, + readw(&ctl_addr->intr_ctrl_stat) | 0x0900); + break; + } + } + } + + dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " + "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel); + for (i = cy_next_channel; i < cy_next_channel + nchan; i++) + tty_register_device(cy_serial_driver, i, &pdev->dev); + cy_next_channel += nchan; + + return 0; +err_null: + cy_card[card_no].base_addr = NULL; + free_irq(irq, &cy_card[card_no]); +err_unmap: + iounmap(addr0); + if (addr2) + iounmap(addr2); +err_reg: + pci_release_regions(pdev); +err_dis: + pci_disable_device(pdev); +err: + return retval; +} + +static void __devexit cy_pci_remove(struct pci_dev *pdev) +{ + struct cyclades_card *cinfo = pci_get_drvdata(pdev); + unsigned int i; + + /* non-Z with old PLX */ + if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == + PLX_9050) + cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0); + else +#ifndef CONFIG_CYZ_INTR + if (!cy_is_Z(cinfo)) +#endif + cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat, + readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) & + ~0x0900); + + iounmap(cinfo->base_addr); + if (cinfo->ctl_addr.p9050) + iounmap(cinfo->ctl_addr.p9050); + if (cinfo->irq +#ifndef CONFIG_CYZ_INTR + && !cy_is_Z(cinfo) +#endif /* CONFIG_CYZ_INTR */ + ) + free_irq(cinfo->irq, cinfo); + pci_release_regions(pdev); + + cinfo->base_addr = NULL; + for (i = cinfo->first_line; i < cinfo->first_line + + cinfo->nports; i++) + tty_unregister_device(cy_serial_driver, i); + cinfo->nports = 0; + kfree(cinfo->ports); +} + +static struct pci_driver cy_pci_driver = { + .name = "cyclades", + .id_table = cy_pci_dev_id, + .probe = cy_pci_probe, + .remove = __devexit_p(cy_pci_remove) +}; +#endif + +static int cyclades_proc_show(struct seq_file *m, void *v) +{ + struct cyclades_port *info; + unsigned int i, j; + __u32 cur_jifs = jiffies; + + seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " + "IdleIn Overruns Ldisc\n"); + + /* Output one line for each known port */ + for (i = 0; i < NR_CARDS; i++) + for (j = 0; j < cy_card[i].nports; j++) { + info = &cy_card[i].ports[j]; + + if (info->port.count) { + /* XXX is the ldisc num worth this? */ + struct tty_struct *tty; + struct tty_ldisc *ld; + int num = 0; + tty = tty_port_tty_get(&info->port); + if (tty) { + ld = tty_ldisc_ref(tty); + if (ld) { + num = ld->ops->num; + tty_ldisc_deref(ld); + } + tty_kref_put(tty); + } + seq_printf(m, "%3d %8lu %10lu %8lu " + "%10lu %8lu %9lu %6d\n", info->line, + (cur_jifs - info->idle_stats.in_use) / + HZ, info->idle_stats.xmit_bytes, + (cur_jifs - info->idle_stats.xmit_idle)/ + HZ, info->idle_stats.recv_bytes, + (cur_jifs - info->idle_stats.recv_idle)/ + HZ, info->idle_stats.overruns, + num); + } else + seq_printf(m, "%3d %8lu %10lu %8lu " + "%10lu %8lu %9lu %6ld\n", + info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); + } + return 0; +} + +static int cyclades_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cyclades_proc_show, NULL); +} + +static const struct file_operations cyclades_proc_fops = { + .owner = THIS_MODULE, + .open = cyclades_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* The serial driver boot-time initialization code! + Hardware I/O ports are mapped to character special devices on a + first found, first allocated manner. That is, this code searches + for Cyclom cards in the system. As each is found, it is probed + to discover how many chips (and thus how many ports) are present. + These ports are mapped to the tty ports 32 and upward in monotonic + fashion. If an 8-port card is replaced with a 16-port card, the + port mapping on a following card will shift. + + This approach is different from what is used in the other serial + device driver because the Cyclom is more properly a multiplexer, + not just an aggregation of serial ports on one card. + + If there are more cards with more ports than have been + statically allocated above, a warning is printed and the + extra ports are ignored. + */ + +static const struct tty_operations cy_ops = { + .open = cy_open, + .close = cy_close, + .write = cy_write, + .put_char = cy_put_char, + .flush_chars = cy_flush_chars, + .write_room = cy_write_room, + .chars_in_buffer = cy_chars_in_buffer, + .flush_buffer = cy_flush_buffer, + .ioctl = cy_ioctl, + .throttle = cy_throttle, + .unthrottle = cy_unthrottle, + .set_termios = cy_set_termios, + .stop = cy_stop, + .start = cy_start, + .hangup = cy_hangup, + .break_ctl = cy_break, + .wait_until_sent = cy_wait_until_sent, + .tiocmget = cy_tiocmget, + .tiocmset = cy_tiocmset, + .get_icount = cy_get_icount, + .proc_fops = &cyclades_proc_fops, +}; + +static int __init cy_init(void) +{ + unsigned int nboards; + int retval = -ENOMEM; + + cy_serial_driver = alloc_tty_driver(NR_PORTS); + if (!cy_serial_driver) + goto err; + + printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n", + __DATE__, __TIME__); + + /* Initialize the tty_driver structure */ + + cy_serial_driver->owner = THIS_MODULE; + cy_serial_driver->driver_name = "cyclades"; + cy_serial_driver->name = "ttyC"; + cy_serial_driver->major = CYCLADES_MAJOR; + cy_serial_driver->minor_start = 0; + cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; + cy_serial_driver->init_termios = tty_std_termios; + cy_serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(cy_serial_driver, &cy_ops); + + retval = tty_register_driver(cy_serial_driver); + if (retval) { + printk(KERN_ERR "Couldn't register Cyclades serial driver\n"); + goto err_frtty; + } + + /* the code below is responsible to find the boards. Each different + type of board has its own detection routine. If a board is found, + the next cy_card structure available is set by the detection + routine. These functions are responsible for checking the + availability of cy_card and cy_port data structures and updating + the cy_next_channel. */ + + /* look for isa boards */ + nboards = cy_detect_isa(); + +#ifdef CONFIG_PCI + /* look for pci boards */ + retval = pci_register_driver(&cy_pci_driver); + if (retval && !nboards) { + tty_unregister_driver(cy_serial_driver); + goto err_frtty; + } +#endif + + return 0; +err_frtty: + put_tty_driver(cy_serial_driver); +err: + return retval; +} /* cy_init */ + +static void __exit cy_cleanup_module(void) +{ + struct cyclades_card *card; + unsigned int i, e1; + +#ifndef CONFIG_CYZ_INTR + del_timer_sync(&cyz_timerlist); +#endif /* CONFIG_CYZ_INTR */ + + e1 = tty_unregister_driver(cy_serial_driver); + if (e1) + printk(KERN_ERR "failed to unregister Cyclades serial " + "driver(%d)\n", e1); + +#ifdef CONFIG_PCI + pci_unregister_driver(&cy_pci_driver); +#endif + + for (i = 0; i < NR_CARDS; i++) { + card = &cy_card[i]; + if (card->base_addr) { + /* clear interrupt */ + cy_writeb(card->base_addr + Cy_ClrIntr, 0); + iounmap(card->base_addr); + if (card->ctl_addr.p9050) + iounmap(card->ctl_addr.p9050); + if (card->irq +#ifndef CONFIG_CYZ_INTR + && !cy_is_Z(card) +#endif /* CONFIG_CYZ_INTR */ + ) + free_irq(card->irq, card); + for (e1 = card->first_line; e1 < card->first_line + + card->nports; e1++) + tty_unregister_device(cy_serial_driver, e1); + kfree(card->ports); + } + } + + put_tty_driver(cy_serial_driver); +} /* cy_cleanup_module */ + +module_init(cy_init); +module_exit(cy_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_VERSION(CY_VERSION); +MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); +MODULE_FIRMWARE("cyzfirm.bin"); diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c new file mode 100644 index 000000000000..db1cf9c328d8 --- /dev/null +++ b/drivers/tty/isicom.c @@ -0,0 +1,1736 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Original driver code supplied by Multi-Tech + * + * Changes + * 1/9/98 alan@lxorguk.ukuu.org.uk + * Merge to 2.0.x kernel tree + * Obtain and use official major/minors + * Loader switched to a misc device + * (fixed range check bug as a side effect) + * Printk clean up + * 9/12/98 alan@lxorguk.ukuu.org.uk + * Rough port to 2.1.x + * + * 10/6/99 sameer Merged the ISA and PCI drivers to + * a new unified driver. + * + * 3/9/99 sameer Added support for ISI4616 cards. + * + * 16/9/99 sameer We do not force RTS low anymore. + * This is to prevent the firmware + * from getting confused. + * + * 26/10/99 sameer Cosmetic changes:The driver now + * dumps the Port Count information + * along with I/O address and IRQ. + * + * 13/12/99 sameer Fixed the problem with IRQ sharing. + * + * 10/5/00 sameer Fixed isicom_shutdown_board() + * to not lower DTR on all the ports + * when the last port on the card is + * closed. + * + * 10/5/00 sameer Signal mask setup command added + * to isicom_setup_port and + * isicom_shutdown_port. + * + * 24/5/00 sameer The driver is now SMP aware. + * + * + * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem + * + * + * 03/01/01 anil .s Added support for resetting the + * internal modems on ISI cards. + * + * 08/02/01 anil .s Upgraded the driver for kernel + * 2.4.x + * + * 11/04/01 Kevin Fixed firmware load problem with + * ISIHP-4X card + * + * 30/04/01 anil .s Fixed the remote login through + * ISI port problem. Now the link + * does not go down before password + * prompt. + * + * 03/05/01 anil .s Fixed the problem with IRQ sharing + * among ISI-PCI cards. + * + * 03/05/01 anil .s Added support to display the version + * info during insmod as well as module + * listing by lsmod. + * + * 10/05/01 anil .s Done the modifications to the source + * file and Install script so that the + * same installation can be used for + * 2.2.x and 2.4.x kernel. + * + * 06/06/01 anil .s Now we drop both dtr and rts during + * shutdown_port as well as raise them + * during isicom_config_port. + * + * 09/06/01 acme@conectiva.com.br use capable, not suser, do + * restore_flags on failure in + * isicom_send_break, verify put_user + * result + * + * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps + * Baud index extended to 21 + * + * 20/03/03 ranjeeth Made to work for Linux Advanced server. + * Taken care of license warning. + * + * 10/12/03 Ravindra Made to work for Fedora Core 1 of + * Red Hat Distribution + * + * 06/01/05 Alan Cox Merged the ISI and base kernel strands + * into a single 2.6 driver + * + * *********************************************************** + * + * To use this driver you also need the support package. You + * can find this in RPM format on + * ftp://ftp.linux.org.uk/pub/linux/alan + * + * You can find the original tools for this direct from Multitech + * ftp://ftp.multitech.com/ISI-Cards/ + * + * Having installed the cards the module options (/etc/modprobe.conf) + * + * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 + * + * Omit those entries for boards you don't have installed. + * + * TODO + * Merge testing + * 64-bit verification + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define InterruptTheCard(base) outw(0, (base) + 0xc) +#define ClearInterrupt(base) inw((base) + 0x0a) + +#ifdef DEBUG +#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c)) +#else +#define isicom_paranoia_check(a, b, c) 0 +#endif + +static int isicom_probe(struct pci_dev *, const struct pci_device_id *); +static void __devexit isicom_remove(struct pci_dev *); + +static struct pci_device_id isicom_pci_tbl[] = { + { PCI_DEVICE(VENDOR_ID, 0x2028) }, + { PCI_DEVICE(VENDOR_ID, 0x2051) }, + { PCI_DEVICE(VENDOR_ID, 0x2052) }, + { PCI_DEVICE(VENDOR_ID, 0x2053) }, + { PCI_DEVICE(VENDOR_ID, 0x2054) }, + { PCI_DEVICE(VENDOR_ID, 0x2055) }, + { PCI_DEVICE(VENDOR_ID, 0x2056) }, + { PCI_DEVICE(VENDOR_ID, 0x2057) }, + { PCI_DEVICE(VENDOR_ID, 0x2058) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, isicom_pci_tbl); + +static struct pci_driver isicom_driver = { + .name = "isicom", + .id_table = isicom_pci_tbl, + .probe = isicom_probe, + .remove = __devexit_p(isicom_remove) +}; + +static int prev_card = 3; /* start servicing isi_card[0] */ +static struct tty_driver *isicom_normal; + +static void isicom_tx(unsigned long _data); +static void isicom_start(struct tty_struct *tty); + +static DEFINE_TIMER(tx, isicom_tx, 0, 0); + +/* baud index mappings from linux defns to isi */ + +static signed char linuxb_to_isib[] = { + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21 +}; + +struct isi_board { + unsigned long base; + int irq; + unsigned char port_count; + unsigned short status; + unsigned short port_status; /* each bit for each port */ + unsigned short shift_count; + struct isi_port *ports; + signed char count; + spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ + unsigned long flags; + unsigned int index; +}; + +struct isi_port { + unsigned short magic; + struct tty_port port; + u16 channel; + u16 status; + struct isi_board *card; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; +}; + +static struct isi_board isi_card[BOARD_COUNT]; +static struct isi_port isi_ports[PORT_COUNT]; + +/* + * Locking functions for card level locking. We need to own both + * the kernel lock for the card and have the card in a position that + * it wants to talk. + */ + +static inline int WaitTillCardIsFree(unsigned long base) +{ + unsigned int count = 0; + unsigned int a = in_atomic(); /* do we run under spinlock? */ + + while (!(inw(base + 0xe) & 0x1) && count++ < 100) + if (a) + mdelay(1); + else + msleep(1); + + return !(inw(base + 0xe) & 0x1); +} + +static int lock_card(struct isi_board *card) +{ + unsigned long base = card->base; + unsigned int retries, a; + + for (retries = 0; retries < 10; retries++) { + spin_lock_irqsave(&card->card_lock, card->flags); + for (a = 0; a < 10; a++) { + if (inw(base + 0xe) & 0x1) + return 1; + udelay(10); + } + spin_unlock_irqrestore(&card->card_lock, card->flags); + msleep(10); + } + pr_warning("Failed to lock Card (0x%lx)\n", card->base); + + return 0; /* Failed to acquire the card! */ +} + +static void unlock_card(struct isi_board *card) +{ + spin_unlock_irqrestore(&card->card_lock, card->flags); +} + +/* + * ISI Card specific ops ... + */ + +/* card->lock HAS to be held */ +static void raise_dtr(struct isi_port *port) +{ + struct isi_board *card = port->card; + unsigned long base = card->base; + u16 channel = port->channel; + + if (WaitTillCardIsFree(base)) + return; + + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0504, base); + InterruptTheCard(base); + port->status |= ISI_DTR; +} + +/* card->lock HAS to be held */ +static inline void drop_dtr(struct isi_port *port) +{ + struct isi_board *card = port->card; + unsigned long base = card->base; + u16 channel = port->channel; + + if (WaitTillCardIsFree(base)) + return; + + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0404, base); + InterruptTheCard(base); + port->status &= ~ISI_DTR; +} + +/* card->lock HAS to be held */ +static inline void raise_rts(struct isi_port *port) +{ + struct isi_board *card = port->card; + unsigned long base = card->base; + u16 channel = port->channel; + + if (WaitTillCardIsFree(base)) + return; + + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0a04, base); + InterruptTheCard(base); + port->status |= ISI_RTS; +} + +/* card->lock HAS to be held */ +static inline void drop_rts(struct isi_port *port) +{ + struct isi_board *card = port->card; + unsigned long base = card->base; + u16 channel = port->channel; + + if (WaitTillCardIsFree(base)) + return; + + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0804, base); + InterruptTheCard(base); + port->status &= ~ISI_RTS; +} + +/* card->lock MUST NOT be held */ + +static void isicom_dtr_rts(struct tty_port *port, int on) +{ + struct isi_port *ip = container_of(port, struct isi_port, port); + struct isi_board *card = ip->card; + unsigned long base = card->base; + u16 channel = ip->channel; + + if (!lock_card(card)) + return; + + if (on) { + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0f04, base); + InterruptTheCard(base); + ip->status |= (ISI_DTR | ISI_RTS); + } else { + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0C04, base); + InterruptTheCard(base); + ip->status &= ~(ISI_DTR | ISI_RTS); + } + unlock_card(card); +} + +/* card->lock HAS to be held */ +static void drop_dtr_rts(struct isi_port *port) +{ + struct isi_board *card = port->card; + unsigned long base = card->base; + u16 channel = port->channel; + + if (WaitTillCardIsFree(base)) + return; + + outw(0x8000 | (channel << card->shift_count) | 0x02, base); + outw(0x0c04, base); + InterruptTheCard(base); + port->status &= ~(ISI_RTS | ISI_DTR); +} + +/* + * ISICOM Driver specific routines ... + * + */ + +static inline int __isicom_paranoia_check(struct isi_port const *port, + char *name, const char *routine) +{ + if (!port) { + pr_warning("Warning: bad isicom magic for dev %s in %s.\n", + name, routine); + return 1; + } + if (port->magic != ISICOM_MAGIC) { + pr_warning("Warning: NULL isicom port for dev %s in %s.\n", + name, routine); + return 1; + } + + return 0; +} + +/* + * Transmitter. + * + * We shovel data into the card buffers on a regular basis. The card + * will do the rest of the work for us. + */ + +static void isicom_tx(unsigned long _data) +{ + unsigned long flags, base; + unsigned int retries; + short count = (BOARD_COUNT-1), card; + short txcount, wrd, residue, word_count, cnt; + struct isi_port *port; + struct tty_struct *tty; + + /* find next active board */ + card = (prev_card + 1) & 0x0003; + while (count-- > 0) { + if (isi_card[card].status & BOARD_ACTIVE) + break; + card = (card + 1) & 0x0003; + } + if (!(isi_card[card].status & BOARD_ACTIVE)) + goto sched_again; + + prev_card = card; + + count = isi_card[card].port_count; + port = isi_card[card].ports; + base = isi_card[card].base; + + spin_lock_irqsave(&isi_card[card].card_lock, flags); + for (retries = 0; retries < 100; retries++) { + if (inw(base + 0xe) & 0x1) + break; + udelay(2); + } + if (retries >= 100) + goto unlock; + + tty = tty_port_tty_get(&port->port); + if (tty == NULL) + goto put_unlock; + + for (; count > 0; count--, port++) { + /* port not active or tx disabled to force flow control */ + if (!(port->port.flags & ASYNC_INITIALIZED) || + !(port->status & ISI_TXOK)) + continue; + + txcount = min_t(short, TX_SIZE, port->xmit_cnt); + if (txcount <= 0 || tty->stopped || tty->hw_stopped) + continue; + + if (!(inw(base + 0x02) & (1 << port->channel))) + continue; + + pr_debug("txing %d bytes, port%d.\n", + txcount, port->channel + 1); + outw((port->channel << isi_card[card].shift_count) | txcount, + base); + residue = NO; + wrd = 0; + while (1) { + cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE + - port->xmit_tail)); + if (residue == YES) { + residue = NO; + if (cnt > 0) { + wrd |= (port->port.xmit_buf[port->xmit_tail] + << 8); + port->xmit_tail = (port->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + txcount--; + cnt--; + outw(wrd, base); + } else { + outw(wrd, base); + break; + } + } + if (cnt <= 0) + break; + word_count = cnt >> 1; + outsw(base, port->port.xmit_buf+port->xmit_tail, word_count); + port->xmit_tail = (port->xmit_tail + + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); + txcount -= (word_count << 1); + port->xmit_cnt -= (word_count << 1); + if (cnt & 0x0001) { + residue = YES; + wrd = port->port.xmit_buf[port->xmit_tail]; + port->xmit_tail = (port->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + txcount--; + } + } + + InterruptTheCard(base); + if (port->xmit_cnt <= 0) + port->status &= ~ISI_TXOK; + if (port->xmit_cnt <= WAKEUP_CHARS) + tty_wakeup(tty); + } + +put_unlock: + tty_kref_put(tty); +unlock: + spin_unlock_irqrestore(&isi_card[card].card_lock, flags); + /* schedule another tx for hopefully in about 10ms */ +sched_again: + mod_timer(&tx, jiffies + msecs_to_jiffies(10)); +} + +/* + * Main interrupt handler routine + */ + +static irqreturn_t isicom_interrupt(int irq, void *dev_id) +{ + struct isi_board *card = dev_id; + struct isi_port *port; + struct tty_struct *tty; + unsigned long base; + u16 header, word_count, count, channel; + short byte_count; + unsigned char *rp; + + if (!card || !(card->status & FIRMWARE_LOADED)) + return IRQ_NONE; + + base = card->base; + + /* did the card interrupt us? */ + if (!(inw(base + 0x0e) & 0x02)) + return IRQ_NONE; + + spin_lock(&card->card_lock); + + /* + * disable any interrupts from the PCI card and lower the + * interrupt line + */ + outw(0x8000, base+0x04); + ClearInterrupt(base); + + inw(base); /* get the dummy word out */ + header = inw(base); + channel = (header & 0x7800) >> card->shift_count; + byte_count = header & 0xff; + + if (channel + 1 > card->port_count) { + pr_warning("%s(0x%lx): %d(channel) > port_count.\n", + __func__, base, channel+1); + outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); + return IRQ_HANDLED; + } + port = card->ports + channel; + if (!(port->port.flags & ASYNC_INITIALIZED)) { + outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); + return IRQ_HANDLED; + } + + tty = tty_port_tty_get(&port->port); + if (tty == NULL) { + word_count = byte_count >> 1; + while (byte_count > 1) { + inw(base); + byte_count -= 2; + } + if (byte_count & 0x01) + inw(base); + outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); + return IRQ_HANDLED; + } + + if (header & 0x8000) { /* Status Packet */ + header = inw(base); + switch (header & 0xff) { + case 0: /* Change in EIA signals */ + if (port->port.flags & ASYNC_CHECK_CD) { + if (port->status & ISI_DCD) { + if (!(header & ISI_DCD)) { + /* Carrier has been lost */ + pr_debug("%s: DCD->low.\n", + __func__); + port->status &= ~ISI_DCD; + tty_hangup(tty); + } + } else if (header & ISI_DCD) { + /* Carrier has been detected */ + pr_debug("%s: DCD->high.\n", + __func__); + port->status |= ISI_DCD; + wake_up_interruptible(&port->port.open_wait); + } + } else { + if (header & ISI_DCD) + port->status |= ISI_DCD; + else + port->status &= ~ISI_DCD; + } + + if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty->hw_stopped) { + if (header & ISI_CTS) { + port->port.tty->hw_stopped = 0; + /* start tx ing */ + port->status |= (ISI_TXOK + | ISI_CTS); + tty_wakeup(tty); + } + } else if (!(header & ISI_CTS)) { + tty->hw_stopped = 1; + /* stop tx ing */ + port->status &= ~(ISI_TXOK | ISI_CTS); + } + } else { + if (header & ISI_CTS) + port->status |= ISI_CTS; + else + port->status &= ~ISI_CTS; + } + + if (header & ISI_DSR) + port->status |= ISI_DSR; + else + port->status &= ~ISI_DSR; + + if (header & ISI_RI) + port->status |= ISI_RI; + else + port->status &= ~ISI_RI; + + break; + + case 1: /* Received Break !!! */ + tty_insert_flip_char(tty, 0, TTY_BREAK); + if (port->port.flags & ASYNC_SAK) + do_SAK(tty); + tty_flip_buffer_push(tty); + break; + + case 2: /* Statistics */ + pr_debug("%s: stats!!!\n", __func__); + break; + + default: + pr_debug("%s: Unknown code in status packet.\n", + __func__); + break; + } + } else { /* Data Packet */ + + count = tty_prepare_flip_string(tty, &rp, byte_count & ~1); + pr_debug("%s: Can rx %d of %d bytes.\n", + __func__, count, byte_count); + word_count = count >> 1; + insw(base, rp, word_count); + byte_count -= (word_count << 1); + if (count & 0x0001) { + tty_insert_flip_char(tty, inw(base) & 0xff, + TTY_NORMAL); + byte_count -= 2; + } + if (byte_count > 0) { + pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n", + __func__, base, channel + 1); + /* drain out unread xtra data */ + while (byte_count > 0) { + inw(base); + byte_count -= 2; + } + } + tty_flip_buffer_push(tty); + } + outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); + tty_kref_put(tty); + + return IRQ_HANDLED; +} + +static void isicom_config_port(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long baud; + unsigned long base = card->base; + u16 channel_setup, channel = port->channel, + shift_count = card->shift_count; + unsigned char flow_ctrl; + + /* FIXME: Switch to new tty baud API */ + baud = C_BAUD(tty); + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + + /* if CBAUDEX bit is on and the baud is set to either 50 or 75 + * then the card is programmed for 57.6Kbps or 115Kbps + * respectively. + */ + + /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ + if (baud < 1 || baud > 4) + tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + + /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set + * by the set_serial_info ioctl ... this is done by + * the 'setserial' utility. + */ + + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud++; /* 57.6 Kbps */ + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud += 2; /* 115 Kbps */ + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baud += 3; /* 230 kbps*/ + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baud += 4; /* 460 kbps*/ + } + if (linuxb_to_isib[baud] == -1) { + /* hang up */ + drop_dtr(port); + return; + } else + raise_dtr(port); + + if (WaitTillCardIsFree(base) == 0) { + outw(0x8000 | (channel << shift_count) | 0x03, base); + outw(linuxb_to_isib[baud] << 8 | 0x03, base); + channel_setup = 0; + switch (C_CSIZE(tty)) { + case CS5: + channel_setup |= ISICOM_CS5; + break; + case CS6: + channel_setup |= ISICOM_CS6; + break; + case CS7: + channel_setup |= ISICOM_CS7; + break; + case CS8: + channel_setup |= ISICOM_CS8; + break; + } + + if (C_CSTOPB(tty)) + channel_setup |= ISICOM_2SB; + if (C_PARENB(tty)) { + channel_setup |= ISICOM_EVPAR; + if (C_PARODD(tty)) + channel_setup |= ISICOM_ODPAR; + } + outw(channel_setup, base); + InterruptTheCard(base); + } + if (C_CLOCAL(tty)) + port->port.flags &= ~ASYNC_CHECK_CD; + else + port->port.flags |= ASYNC_CHECK_CD; + + /* flow control settings ...*/ + flow_ctrl = 0; + port->port.flags &= ~ASYNC_CTS_FLOW; + if (C_CRTSCTS(tty)) { + port->port.flags |= ASYNC_CTS_FLOW; + flow_ctrl |= ISICOM_CTSRTS; + } + if (I_IXON(tty)) + flow_ctrl |= ISICOM_RESPOND_XONXOFF; + if (I_IXOFF(tty)) + flow_ctrl |= ISICOM_INITIATE_XONXOFF; + + if (WaitTillCardIsFree(base) == 0) { + outw(0x8000 | (channel << shift_count) | 0x04, base); + outw(flow_ctrl << 8 | 0x05, base); + outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); + InterruptTheCard(base); + } + + /* rx enabled -> enable port for rx on the card */ + if (C_CREAD(tty)) { + card->port_status |= (1 << channel); + outw(card->port_status, base + 0x02); + } +} + +/* open et all */ + +static inline void isicom_setup_board(struct isi_board *bp) +{ + int channel; + struct isi_port *port; + + bp->count++; + if (!(bp->status & BOARD_INIT)) { + port = bp->ports; + for (channel = 0; channel < bp->port_count; channel++, port++) + drop_dtr_rts(port); + } + bp->status |= BOARD_ACTIVE | BOARD_INIT; +} + +/* Activate and thus setup board are protected from races against shutdown + by the tty_port mutex */ + +static int isicom_activate(struct tty_port *tport, struct tty_struct *tty) +{ + struct isi_port *port = container_of(tport, struct isi_port, port); + struct isi_board *card = port->card; + unsigned long flags; + + if (tty_port_alloc_xmit_buf(tport) < 0) + return -ENOMEM; + + spin_lock_irqsave(&card->card_lock, flags); + isicom_setup_board(card); + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + + /* discard any residual data */ + if (WaitTillCardIsFree(card->base) == 0) { + outw(0x8000 | (port->channel << card->shift_count) | 0x02, + card->base); + outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); + InterruptTheCard(card->base); + } + isicom_config_port(tty); + spin_unlock_irqrestore(&card->card_lock, flags); + + return 0; +} + +static int isicom_carrier_raised(struct tty_port *port) +{ + struct isi_port *ip = container_of(port, struct isi_port, port); + return (ip->status & ISI_DCD)?1 : 0; +} + +static struct tty_port *isicom_find_port(struct tty_struct *tty) +{ + struct isi_port *port; + struct isi_board *card; + unsigned int board; + int line = tty->index; + + if (line < 0 || line > PORT_COUNT-1) + return NULL; + board = BOARD(line); + card = &isi_card[board]; + + if (!(card->status & FIRMWARE_LOADED)) + return NULL; + + /* open on a port greater than the port count for the card !!! */ + if (line > ((board * 16) + card->port_count - 1)) + return NULL; + + port = &isi_ports[line]; + if (isicom_paranoia_check(port, tty->name, "isicom_open")) + return NULL; + + return &port->port; +} + +static int isicom_open(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *port; + struct tty_port *tport; + + tport = isicom_find_port(tty); + if (tport == NULL) + return -ENODEV; + port = container_of(tport, struct isi_port, port); + + tty->driver_data = port; + return tty_port_open(tport, tty, filp); +} + +/* close et all */ + +/* card->lock HAS to be held */ +static void isicom_shutdown_port(struct isi_port *port) +{ + struct isi_board *card = port->card; + + if (--card->count < 0) { + pr_debug("%s: bad board(0x%lx) count %d.\n", + __func__, card->base, card->count); + card->count = 0; + } + /* last port was closed, shutdown that board too */ + if (!card->count) + card->status &= BOARD_ACTIVE; +} + +static void isicom_flush_buffer(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer")) + return; + + spin_lock_irqsave(&card->card_lock, flags); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + spin_unlock_irqrestore(&card->card_lock, flags); + + tty_wakeup(tty); +} + +static void isicom_shutdown(struct tty_port *port) +{ + struct isi_port *ip = container_of(port, struct isi_port, port); + struct isi_board *card = ip->card; + unsigned long flags; + + /* indicate to the card that no more data can be received + on this port */ + spin_lock_irqsave(&card->card_lock, flags); + card->port_status &= ~(1 << ip->channel); + outw(card->port_status, card->base + 0x02); + isicom_shutdown_port(ip); + spin_unlock_irqrestore(&card->card_lock, flags); + tty_port_free_xmit_buf(port); +} + +static void isicom_close(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *ip = tty->driver_data; + struct tty_port *port; + + if (ip == NULL) + return; + + port = &ip->port; + if (isicom_paranoia_check(ip, tty->name, "isicom_close")) + return; + tty_port_close(port, tty, filp); +} + +/* write et all */ +static int isicom_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long flags; + int cnt, total = 0; + + if (isicom_paranoia_check(port, tty->name, "isicom_write")) + return 0; + + spin_lock_irqsave(&card->card_lock, flags); + + while (1) { + cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt + - 1, SERIAL_XMIT_SIZE - port->xmit_head)); + if (cnt <= 0) + break; + + memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt); + port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE + - 1); + port->xmit_cnt += cnt; + buf += cnt; + count -= cnt; + total += cnt; + } + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) + port->status |= ISI_TXOK; + spin_unlock_irqrestore(&card->card_lock, flags); + return total; +} + +/* put_char et all */ +static int isicom_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->name, "isicom_put_char")) + return 0; + + spin_lock_irqsave(&card->card_lock, flags); + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + spin_unlock_irqrestore(&card->card_lock, flags); + return 0; + } + + port->port.xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt++; + spin_unlock_irqrestore(&card->card_lock, flags); + return 1; +} + +/* flush_chars et all */ +static void isicom_flush_chars(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + + if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->port.xmit_buf) + return; + + /* this tells the transmitter to consider this port for + data output to the card ... that's the best we can do. */ + port->status |= ISI_TXOK; +} + +/* write_room et all */ +static int isicom_write_room(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + int free; + + if (isicom_paranoia_check(port, tty->name, "isicom_write_room")) + return 0; + + free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (free < 0) + free = 0; + return free; +} + +/* chars_in_buffer et all */ +static int isicom_chars_in_buffer(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer")) + return 0; + return port->xmit_cnt; +} + +/* ioctl et all */ +static int isicom_send_break(struct tty_struct *tty, int length) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + unsigned long base = card->base; + + if (length == -1) + return -EOPNOTSUPP; + + if (!lock_card(card)) + return -EINVAL; + + outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); + outw((length & 0xff) << 8 | 0x00, base); + outw((length & 0xff00), base); + InterruptTheCard(base); + + unlock_card(card); + return 0; +} + +static int isicom_tiocmget(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + /* just send the port status */ + u16 status = port->status; + + if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) + return -ENODEV; + + return ((status & ISI_RTS) ? TIOCM_RTS : 0) | + ((status & ISI_DTR) ? TIOCM_DTR : 0) | + ((status & ISI_DCD) ? TIOCM_CAR : 0) | + ((status & ISI_DSR) ? TIOCM_DSR : 0) | + ((status & ISI_CTS) ? TIOCM_CTS : 0) | + ((status & ISI_RI ) ? TIOCM_RI : 0); +} + +static int isicom_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct isi_port *port = tty->driver_data; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) + return -ENODEV; + + spin_lock_irqsave(&port->card->card_lock, flags); + if (set & TIOCM_RTS) + raise_rts(port); + if (set & TIOCM_DTR) + raise_dtr(port); + + if (clear & TIOCM_RTS) + drop_rts(port); + if (clear & TIOCM_DTR) + drop_dtr(port); + spin_unlock_irqrestore(&port->card->card_lock, flags); + + return 0; +} + +static int isicom_set_serial_info(struct tty_struct *tty, + struct serial_struct __user *info) +{ + struct isi_port *port = tty->driver_data; + struct serial_struct newinfo; + int reconfig_port; + + if (copy_from_user(&newinfo, info, sizeof(newinfo))) + return -EFAULT; + + mutex_lock(&port->port.mutex); + reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) != + (newinfo.flags & ASYNC_SPD_MASK)); + + if (!capable(CAP_SYS_ADMIN)) { + if ((newinfo.close_delay != port->port.close_delay) || + (newinfo.closing_wait != port->port.closing_wait) || + ((newinfo.flags & ~ASYNC_USR_MASK) != + (port->port.flags & ~ASYNC_USR_MASK))) { + mutex_unlock(&port->port.mutex); + return -EPERM; + } + port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | + (newinfo.flags & ASYNC_USR_MASK)); + } else { + port->port.close_delay = newinfo.close_delay; + port->port.closing_wait = newinfo.closing_wait; + port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | + (newinfo.flags & ASYNC_FLAGS)); + } + if (reconfig_port) { + unsigned long flags; + spin_lock_irqsave(&port->card->card_lock, flags); + isicom_config_port(tty); + spin_unlock_irqrestore(&port->card->card_lock, flags); + } + mutex_unlock(&port->port.mutex); + return 0; +} + +static int isicom_get_serial_info(struct isi_port *port, + struct serial_struct __user *info) +{ + struct serial_struct out_info; + + mutex_lock(&port->port.mutex); + memset(&out_info, 0, sizeof(out_info)); +/* out_info.type = ? */ + out_info.line = port - isi_ports; + out_info.port = port->card->base; + out_info.irq = port->card->irq; + out_info.flags = port->port.flags; +/* out_info.baud_base = ? */ + out_info.close_delay = port->port.close_delay; + out_info.closing_wait = port->port.closing_wait; + mutex_unlock(&port->port.mutex); + if (copy_to_user(info, &out_info, sizeof(out_info))) + return -EFAULT; + return 0; +} + +static int isicom_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct isi_port *port = tty->driver_data; + void __user *argp = (void __user *)arg; + + if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) + return -ENODEV; + + switch (cmd) { + case TIOCGSERIAL: + return isicom_get_serial_info(port, argp); + + case TIOCSSERIAL: + return isicom_set_serial_info(tty, argp); + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* set_termios et all */ +static void isicom_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct isi_port *port = tty->driver_data; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + spin_lock_irqsave(&port->card->card_lock, flags); + isicom_config_port(tty); + spin_unlock_irqrestore(&port->card->card_lock, flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + isicom_start(tty); + } +} + +/* throttle et all */ +static void isicom_throttle(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + + if (isicom_paranoia_check(port, tty->name, "isicom_throttle")) + return; + + /* tell the card that this port cannot handle any more data for now */ + card->port_status &= ~(1 << port->channel); + outw(card->port_status, card->base + 0x02); +} + +/* unthrottle et all */ +static void isicom_unthrottle(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + struct isi_board *card = port->card; + + if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle")) + return; + + /* tell the card that this port is ready to accept more data */ + card->port_status |= (1 << port->channel); + outw(card->port_status, card->base + 0x02); +} + +/* stop et all */ +static void isicom_stop(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + + if (isicom_paranoia_check(port, tty->name, "isicom_stop")) + return; + + /* this tells the transmitter not to consider this port for + data output to the card. */ + port->status &= ~ISI_TXOK; +} + +/* start et all */ +static void isicom_start(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + + if (isicom_paranoia_check(port, tty->name, "isicom_start")) + return; + + /* this tells the transmitter to consider this port for + data output to the card. */ + port->status |= ISI_TXOK; +} + +static void isicom_hangup(struct tty_struct *tty) +{ + struct isi_port *port = tty->driver_data; + + if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) + return; + tty_port_hangup(&port->port); +} + + +/* + * Driver init and deinit functions + */ + +static const struct tty_operations isicom_ops = { + .open = isicom_open, + .close = isicom_close, + .write = isicom_write, + .put_char = isicom_put_char, + .flush_chars = isicom_flush_chars, + .write_room = isicom_write_room, + .chars_in_buffer = isicom_chars_in_buffer, + .ioctl = isicom_ioctl, + .set_termios = isicom_set_termios, + .throttle = isicom_throttle, + .unthrottle = isicom_unthrottle, + .stop = isicom_stop, + .start = isicom_start, + .hangup = isicom_hangup, + .flush_buffer = isicom_flush_buffer, + .tiocmget = isicom_tiocmget, + .tiocmset = isicom_tiocmset, + .break_ctl = isicom_send_break, +}; + +static const struct tty_port_operations isicom_port_ops = { + .carrier_raised = isicom_carrier_raised, + .dtr_rts = isicom_dtr_rts, + .activate = isicom_activate, + .shutdown = isicom_shutdown, +}; + +static int __devinit reset_card(struct pci_dev *pdev, + const unsigned int card, unsigned int *signature) +{ + struct isi_board *board = pci_get_drvdata(pdev); + unsigned long base = board->base; + unsigned int sig, portcount = 0; + int retval = 0; + + dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1, + base); + + inw(base + 0x8); + + msleep(10); + + outw(0, base + 0x8); /* Reset */ + + msleep(1000); + + sig = inw(base + 0x4) & 0xff; + + if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd && + sig != 0xee) { + dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible " + "bad I/O Port Address 0x%lx).\n", card + 1, base); + dev_dbg(&pdev->dev, "Sig=0x%x\n", sig); + retval = -EIO; + goto end; + } + + msleep(10); + + portcount = inw(base + 0x2); + if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 && + portcount != 8 && portcount != 16)) { + dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n", + card + 1); + retval = -EIO; + goto end; + } + + switch (sig) { + case 0xa5: + case 0xbb: + case 0xdd: + board->port_count = (portcount == 4) ? 4 : 8; + board->shift_count = 12; + break; + case 0xcc: + case 0xee: + board->port_count = 16; + board->shift_count = 11; + break; + } + dev_info(&pdev->dev, "-Done\n"); + *signature = sig; + +end: + return retval; +} + +static int __devinit load_firmware(struct pci_dev *pdev, + const unsigned int index, const unsigned int signature) +{ + struct isi_board *board = pci_get_drvdata(pdev); + const struct firmware *fw; + unsigned long base = board->base; + unsigned int a; + u16 word_count, status; + int retval = -EIO; + char *name; + u8 *data; + + struct stframe { + u16 addr; + u16 count; + u8 data[0]; + } *frame; + + switch (signature) { + case 0xa5: + name = "isi608.bin"; + break; + case 0xbb: + name = "isi608em.bin"; + break; + case 0xcc: + name = "isi616em.bin"; + break; + case 0xdd: + name = "isi4608.bin"; + break; + case 0xee: + name = "isi4616.bin"; + break; + default: + dev_err(&pdev->dev, "Unknown signature.\n"); + goto end; + } + + retval = request_firmware(&fw, name, &pdev->dev); + if (retval) + goto end; + + retval = -EIO; + + for (frame = (struct stframe *)fw->data; + frame < (struct stframe *)(fw->data + fw->size); + frame = (struct stframe *)((u8 *)(frame + 1) + + frame->count)) { + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf0, base); /* start upload sequence */ + outw(0x00, base); + outw(frame->addr, base); /* lsb of address */ + + word_count = frame->count / 2 + frame->count % 2; + outw(word_count, base); + InterruptTheCard(base); + + udelay(100); /* 0x2f */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + status = inw(base + 0x4); + if (status != 0) { + dev_warn(&pdev->dev, "Card%d rejected load header:\n" + "Address:0x%x\n" + "Count:0x%x\n" + "Status:0x%x\n", + index + 1, frame->addr, frame->count, status); + goto errrelfw; + } + outsw(base, frame->data, word_count); + + InterruptTheCard(base); + + udelay(50); /* 0x0f */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + status = inw(base + 0x4); + if (status != 0) { + dev_err(&pdev->dev, "Card%d got out of sync.Card " + "Status:0x%x\n", index + 1, status); + goto errrelfw; + } + } + +/* XXX: should we test it by reading it back and comparing with original like + * in load firmware package? */ + for (frame = (struct stframe *)fw->data; + frame < (struct stframe *)(fw->data + fw->size); + frame = (struct stframe *)((u8 *)(frame + 1) + + frame->count)) { + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf1, base); /* start download sequence */ + outw(0x00, base); + outw(frame->addr, base); /* lsb of address */ + + word_count = (frame->count >> 1) + frame->count % 2; + outw(word_count + 1, base); + InterruptTheCard(base); + + udelay(50); /* 0xf */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + status = inw(base + 0x4); + if (status != 0) { + dev_warn(&pdev->dev, "Card%d rejected verify header:\n" + "Address:0x%x\n" + "Count:0x%x\n" + "Status: 0x%x\n", + index + 1, frame->addr, frame->count, status); + goto errrelfw; + } + + data = kmalloc(word_count * 2, GFP_KERNEL); + if (data == NULL) { + dev_err(&pdev->dev, "Card%d, firmware upload " + "failed, not enough memory\n", index + 1); + goto errrelfw; + } + inw(base); + insw(base, data, word_count); + InterruptTheCard(base); + + for (a = 0; a < frame->count; a++) + if (data[a] != frame->data[a]) { + kfree(data); + dev_err(&pdev->dev, "Card%d, firmware upload " + "failed\n", index + 1); + goto errrelfw; + } + kfree(data); + + udelay(50); /* 0xf */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + status = inw(base + 0x4); + if (status != 0) { + dev_err(&pdev->dev, "Card%d verify got out of sync. " + "Card Status:0x%x\n", index + 1, status); + goto errrelfw; + } + } + + /* xfer ctrl */ + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf2, base); + outw(0x800, base); + outw(0x0, base); + outw(0x0, base); + InterruptTheCard(base); + outw(0x0, base + 0x4); /* for ISI4608 cards */ + + board->status |= FIRMWARE_LOADED; + retval = 0; + +errrelfw: + release_firmware(fw); +end: + return retval; +} + +/* + * Insmod can set static symbols so keep these static + */ +static unsigned int card_count; + +static int __devinit isicom_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + unsigned int uninitialized_var(signature), index; + int retval = -EPERM; + struct isi_board *board = NULL; + + if (card_count >= BOARD_COUNT) + goto err; + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "failed to enable\n"); + goto err; + } + + dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); + + /* allot the first empty slot in the array */ + for (index = 0; index < BOARD_COUNT; index++) { + if (isi_card[index].base == 0) { + board = &isi_card[index]; + break; + } + } + if (index == BOARD_COUNT) { + retval = -ENODEV; + goto err_disable; + } + + board->index = index; + board->base = pci_resource_start(pdev, 3); + board->irq = pdev->irq; + card_count++; + + pci_set_drvdata(pdev, board); + + retval = pci_request_region(pdev, 3, ISICOM_NAME); + if (retval) { + dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d " + "will be disabled.\n", board->base, board->base + 15, + index + 1); + retval = -EBUSY; + goto errdec; + } + + retval = request_irq(board->irq, isicom_interrupt, + IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board); + if (retval < 0) { + dev_err(&pdev->dev, "Could not install handler at Irq %d. " + "Card%d will be disabled.\n", board->irq, index + 1); + goto errunrr; + } + + retval = reset_card(pdev, index, &signature); + if (retval < 0) + goto errunri; + + retval = load_firmware(pdev, index, signature); + if (retval < 0) + goto errunri; + + for (index = 0; index < board->port_count; index++) + tty_register_device(isicom_normal, board->index * 16 + index, + &pdev->dev); + + return 0; + +errunri: + free_irq(board->irq, board); +errunrr: + pci_release_region(pdev, 3); +errdec: + board->base = 0; + card_count--; +err_disable: + pci_disable_device(pdev); +err: + return retval; +} + +static void __devexit isicom_remove(struct pci_dev *pdev) +{ + struct isi_board *board = pci_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < board->port_count; i++) + tty_unregister_device(isicom_normal, board->index * 16 + i); + + free_irq(board->irq, board); + pci_release_region(pdev, 3); + board->base = 0; + card_count--; + pci_disable_device(pdev); +} + +static int __init isicom_init(void) +{ + int retval, idx, channel; + struct isi_port *port; + + for (idx = 0; idx < BOARD_COUNT; idx++) { + port = &isi_ports[idx * 16]; + isi_card[idx].ports = port; + spin_lock_init(&isi_card[idx].card_lock); + for (channel = 0; channel < 16; channel++, port++) { + tty_port_init(&port->port); + port->port.ops = &isicom_port_ops; + port->magic = ISICOM_MAGIC; + port->card = &isi_card[idx]; + port->channel = channel; + port->port.close_delay = 50 * HZ/100; + port->port.closing_wait = 3000 * HZ/100; + port->status = 0; + /* . . . */ + } + isi_card[idx].base = 0; + isi_card[idx].irq = 0; + } + + /* tty driver structure initialization */ + isicom_normal = alloc_tty_driver(PORT_COUNT); + if (!isicom_normal) { + retval = -ENOMEM; + goto error; + } + + isicom_normal->owner = THIS_MODULE; + isicom_normal->name = "ttyM"; + isicom_normal->major = ISICOM_NMAJOR; + isicom_normal->minor_start = 0; + isicom_normal->type = TTY_DRIVER_TYPE_SERIAL; + isicom_normal->subtype = SERIAL_TYPE_NORMAL; + isicom_normal->init_termios = tty_std_termios; + isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | + CLOCAL; + isicom_normal->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK; + tty_set_operations(isicom_normal, &isicom_ops); + + retval = tty_register_driver(isicom_normal); + if (retval) { + pr_debug("Couldn't register the dialin driver\n"); + goto err_puttty; + } + + retval = pci_register_driver(&isicom_driver); + if (retval < 0) { + pr_err("Unable to register pci driver.\n"); + goto err_unrtty; + } + + mod_timer(&tx, jiffies + 1); + + return 0; +err_unrtty: + tty_unregister_driver(isicom_normal); +err_puttty: + put_tty_driver(isicom_normal); +error: + return retval; +} + +static void __exit isicom_exit(void) +{ + del_timer_sync(&tx); + + pci_unregister_driver(&isicom_driver); + tty_unregister_driver(isicom_normal); + put_tty_driver(isicom_normal); +} + +module_init(isicom_init); +module_exit(isicom_exit); + +MODULE_AUTHOR("MultiTech"); +MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("isi608.bin"); +MODULE_FIRMWARE("isi608em.bin"); +MODULE_FIRMWARE("isi616em.bin"); +MODULE_FIRMWARE("isi4608.bin"); +MODULE_FIRMWARE("isi4616.bin"); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c new file mode 100644 index 000000000000..35b0c38590e6 --- /dev/null +++ b/drivers/tty/moxa.c @@ -0,0 +1,2092 @@ +/*****************************************************************************/ +/* + * moxa.c -- MOXA Intellio family multiport serial driver. + * + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com). + * Copyright (c) 2007 Jiri Slaby + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * MOXA Intellio Series Driver + * for : LINUX + * date : 1999/1/7 + * version : 5.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "moxa.h" + +#define MOXA_VERSION "6.0k" + +#define MOXA_FW_HDRLEN 32 + +#define MOXAMAJOR 172 + +#define MAX_BOARDS 4 /* Don't change this value */ +#define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ +#define MAX_PORTS (MAX_BOARDS * MAX_PORTS_PER_BOARD) + +#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \ + (brd)->boardType == MOXA_BOARD_C320_PCI) + +/* + * Define the Moxa PCI vendor and device IDs. + */ +#define MOXA_BUS_TYPE_ISA 0 +#define MOXA_BUS_TYPE_PCI 1 + +enum { + MOXA_BOARD_C218_PCI = 1, + MOXA_BOARD_C218_ISA, + MOXA_BOARD_C320_PCI, + MOXA_BOARD_C320_ISA, + MOXA_BOARD_CP204J, +}; + +static char *moxa_brdname[] = +{ + "C218 Turbo PCI series", + "C218 Turbo ISA series", + "C320 Turbo PCI series", + "C320 Turbo ISA series", + "CP-204J series", +}; + +#ifdef CONFIG_PCI +static struct pci_device_id moxa_pcibrds[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218), + .driver_data = MOXA_BOARD_C218_PCI }, + { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320), + .driver_data = MOXA_BOARD_C320_PCI }, + { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J), + .driver_data = MOXA_BOARD_CP204J }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, moxa_pcibrds); +#endif /* CONFIG_PCI */ + +struct moxa_port; + +static struct moxa_board_conf { + int boardType; + int numPorts; + int busType; + + unsigned int ready; + + struct moxa_port *ports; + + void __iomem *basemem; + void __iomem *intNdx; + void __iomem *intPend; + void __iomem *intTable; +} moxa_boards[MAX_BOARDS]; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +struct moxaq_str { + int inq; + int outq; +}; + +struct moxa_port { + struct tty_port port; + struct moxa_board_conf *board; + void __iomem *tableAddr; + + int type; + int cflag; + unsigned long statusflags; + + u8 DCDState; /* Protected by the port lock */ + u8 lineCtrl; + u8 lowChkFlag; +}; + +struct mon_str { + int tick; + int rxcnt[MAX_PORTS]; + int txcnt[MAX_PORTS]; +}; + +/* statusflags */ +#define TXSTOPPED 1 +#define LOWWAIT 2 +#define EMPTYWAIT 3 + +#define SERIAL_DO_RESTART + +#define WAKEUP_CHARS 256 + +static int ttymajor = MOXAMAJOR; +static struct mon_str moxaLog; +static unsigned int moxaFuncTout = HZ / 2; +static unsigned int moxaLowWaterChk; +static DEFINE_MUTEX(moxa_openlock); +static DEFINE_SPINLOCK(moxa_lock); + +static unsigned long baseaddr[MAX_BOARDS]; +static unsigned int type[MAX_BOARDS]; +static unsigned int numports[MAX_BOARDS]; + +MODULE_AUTHOR("William Chen"); +MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("c218tunx.cod"); +MODULE_FIRMWARE("cp204unx.cod"); +MODULE_FIRMWARE("c320tunx.cod"); + +module_param_array(type, uint, NULL, 0); +MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); +module_param_array(baseaddr, ulong, NULL, 0); +MODULE_PARM_DESC(baseaddr, "base address"); +module_param_array(numports, uint, NULL, 0); +MODULE_PARM_DESC(numports, "numports (ignored for C218)"); + +module_param(ttymajor, int, 0); + +/* + * static functions: + */ +static int moxa_open(struct tty_struct *, struct file *); +static void moxa_close(struct tty_struct *, struct file *); +static int moxa_write(struct tty_struct *, const unsigned char *, int); +static int moxa_write_room(struct tty_struct *); +static void moxa_flush_buffer(struct tty_struct *); +static int moxa_chars_in_buffer(struct tty_struct *); +static void moxa_set_termios(struct tty_struct *, struct ktermios *); +static void moxa_stop(struct tty_struct *); +static void moxa_start(struct tty_struct *); +static void moxa_hangup(struct tty_struct *); +static int moxa_tiocmget(struct tty_struct *tty); +static int moxa_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static void moxa_poll(unsigned long); +static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); +static void moxa_shutdown(struct tty_port *); +static int moxa_carrier_raised(struct tty_port *); +static void moxa_dtr_rts(struct tty_port *, int); +/* + * moxa board interface functions: + */ +static void MoxaPortEnable(struct moxa_port *); +static void MoxaPortDisable(struct moxa_port *); +static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t); +static int MoxaPortGetLineOut(struct moxa_port *, int *, int *); +static void MoxaPortLineCtrl(struct moxa_port *, int, int); +static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); +static int MoxaPortLineStatus(struct moxa_port *); +static void MoxaPortFlushData(struct moxa_port *, int); +static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int); +static int MoxaPortReadData(struct moxa_port *); +static int MoxaPortTxQueue(struct moxa_port *); +static int MoxaPortRxQueue(struct moxa_port *); +static int MoxaPortTxFree(struct moxa_port *); +static void MoxaPortTxDisable(struct moxa_port *); +static void MoxaPortTxEnable(struct moxa_port *); +static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *); +static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *); +static void MoxaSetFifo(struct moxa_port *port, int enable); + +/* + * I/O functions + */ + +static DEFINE_SPINLOCK(moxafunc_lock); + +static void moxa_wait_finish(void __iomem *ofsAddr) +{ + unsigned long end = jiffies + moxaFuncTout; + + while (readw(ofsAddr + FuncCode) != 0) + if (time_after(jiffies, end)) + return; + if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit()) + printk(KERN_WARNING "moxa function expired\n"); +} + +static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg) +{ + unsigned long flags; + spin_lock_irqsave(&moxafunc_lock, flags); + writew(arg, ofsAddr + FuncArg); + writew(cmd, ofsAddr + FuncCode); + moxa_wait_finish(ofsAddr); + spin_unlock_irqrestore(&moxafunc_lock, flags); +} + +static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg) +{ + unsigned long flags; + u16 ret; + spin_lock_irqsave(&moxafunc_lock, flags); + writew(arg, ofsAddr + FuncArg); + writew(cmd, ofsAddr + FuncCode); + moxa_wait_finish(ofsAddr); + ret = readw(ofsAddr + FuncArg); + spin_unlock_irqrestore(&moxafunc_lock, flags); + return ret; +} + +static void moxa_low_water_check(void __iomem *ofsAddr) +{ + u16 rptr, wptr, mask, len; + + if (readb(ofsAddr + FlagStat) & Xoff_state) { + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + if (len <= Low_water) + moxafunc(ofsAddr, FC_SendXon, 0); + } +} + +/* + * TTY operations + */ + +static int moxa_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct moxa_port *ch = tty->driver_data; + void __user *argp = (void __user *)arg; + int status, ret = 0; + + if (tty->index == MAX_PORTS) { + if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE && + cmd != MOXA_GETMSTATUS) + return -EINVAL; + } else if (!ch) + return -ENODEV; + + switch (cmd) { + case MOXA_GETDATACOUNT: + moxaLog.tick = jiffies; + if (copy_to_user(argp, &moxaLog, sizeof(moxaLog))) + ret = -EFAULT; + break; + case MOXA_FLUSH_QUEUE: + MoxaPortFlushData(ch, arg); + break; + case MOXA_GET_IOQUEUE: { + struct moxaq_str __user *argm = argp; + struct moxaq_str tmp; + struct moxa_port *p; + unsigned int i, j; + + for (i = 0; i < MAX_BOARDS; i++) { + p = moxa_boards[i].ports; + for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + memset(&tmp, 0, sizeof(tmp)); + spin_lock_bh(&moxa_lock); + if (moxa_boards[i].ready) { + tmp.inq = MoxaPortRxQueue(p); + tmp.outq = MoxaPortTxQueue(p); + } + spin_unlock_bh(&moxa_lock); + if (copy_to_user(argm, &tmp, sizeof(tmp))) + return -EFAULT; + } + } + break; + } case MOXA_GET_OQUEUE: + status = MoxaPortTxQueue(ch); + ret = put_user(status, (unsigned long __user *)argp); + break; + case MOXA_GET_IQUEUE: + status = MoxaPortRxQueue(ch); + ret = put_user(status, (unsigned long __user *)argp); + break; + case MOXA_GETMSTATUS: { + struct mxser_mstatus __user *argm = argp; + struct mxser_mstatus tmp; + struct moxa_port *p; + unsigned int i, j; + + for (i = 0; i < MAX_BOARDS; i++) { + p = moxa_boards[i].ports; + for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + struct tty_struct *ttyp; + memset(&tmp, 0, sizeof(tmp)); + spin_lock_bh(&moxa_lock); + if (!moxa_boards[i].ready) { + spin_unlock_bh(&moxa_lock); + goto copy; + } + + status = MoxaPortLineStatus(p); + spin_unlock_bh(&moxa_lock); + + if (status & 1) + tmp.cts = 1; + if (status & 2) + tmp.dsr = 1; + if (status & 4) + tmp.dcd = 1; + + ttyp = tty_port_tty_get(&p->port); + if (!ttyp || !ttyp->termios) + tmp.cflag = p->cflag; + else + tmp.cflag = ttyp->termios->c_cflag; + tty_kref_put(tty); +copy: + if (copy_to_user(argm, &tmp, sizeof(tmp))) + return -EFAULT; + } + } + break; + } + case TIOCGSERIAL: + mutex_lock(&ch->port.mutex); + ret = moxa_get_serial_info(ch, argp); + mutex_unlock(&ch->port.mutex); + break; + case TIOCSSERIAL: + mutex_lock(&ch->port.mutex); + ret = moxa_set_serial_info(ch, argp); + mutex_unlock(&ch->port.mutex); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static int moxa_break_ctl(struct tty_struct *tty, int state) +{ + struct moxa_port *port = tty->driver_data; + + moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, + Magic_code); + return 0; +} + +static const struct tty_operations moxa_ops = { + .open = moxa_open, + .close = moxa_close, + .write = moxa_write, + .write_room = moxa_write_room, + .flush_buffer = moxa_flush_buffer, + .chars_in_buffer = moxa_chars_in_buffer, + .ioctl = moxa_ioctl, + .set_termios = moxa_set_termios, + .stop = moxa_stop, + .start = moxa_start, + .hangup = moxa_hangup, + .break_ctl = moxa_break_ctl, + .tiocmget = moxa_tiocmget, + .tiocmset = moxa_tiocmset, +}; + +static const struct tty_port_operations moxa_port_ops = { + .carrier_raised = moxa_carrier_raised, + .dtr_rts = moxa_dtr_rts, + .shutdown = moxa_shutdown, +}; + +static struct tty_driver *moxaDriver; +static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0); + +/* + * HW init + */ + +static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model) +{ + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + if (model != 1) + goto err; + break; + case MOXA_BOARD_CP204J: + if (model != 3) + goto err; + break; + default: + if (model != 2) + goto err; + break; + } + return 0; +err: + return -EINVAL; +} + +static int moxa_check_fw(const void *ptr) +{ + const __le16 *lptr = ptr; + + if (*lptr != cpu_to_le16(0x7980)) + return -EINVAL; + + return 0; +} + +static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + u16 tmp; + + writeb(HW_reset, baseAddr + Control_reg); /* reset */ + msleep(10); + memset_io(baseAddr, 0, 4096); + memcpy_toio(baseAddr, buf, len); /* download BIOS */ + writeb(0, baseAddr + Control_reg); /* restart */ + + msleep(2000); + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + tmp = readw(baseAddr + C218_key); + if (tmp != C218_KeyCode) + goto err; + break; + case MOXA_BOARD_CP204J: + tmp = readw(baseAddr + C218_key); + if (tmp != CP204J_KeyCode) + goto err; + break; + default: + tmp = readw(baseAddr + C320_key); + if (tmp != C320_KeyCode) + goto err; + tmp = readw(baseAddr + C320_status); + if (tmp != STS_init) { + printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic " + "module not found\n"); + return -EIO; + } + break; + } + + return 0; +err: + printk(KERN_ERR "MOXA: bios upload failed -- board not found\n"); + return -EIO; +} + +static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + + if (len < 7168) { + printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n"); + return -EINVAL; + } + + writew(len - 7168 - 2, baseAddr + C320bapi_len); + writeb(1, baseAddr + Control_reg); /* Select Page 1 */ + memcpy_toio(baseAddr + DynPage_addr, ptr, 7168); + writeb(2, baseAddr + Control_reg); /* Select Page 2 */ + memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168); + + return 0; +} + +static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr, + size_t len) +{ + void __iomem *baseAddr = brd->basemem; + const __le16 *uptr = ptr; + size_t wlen, len2, j; + unsigned long key, loadbuf, loadlen, checksum, checksum_ok; + unsigned int i, retry; + u16 usum, keycode; + + keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode : + C218_KeyCode; + + switch (brd->boardType) { + case MOXA_BOARD_CP204J: + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + key = C218_key; + loadbuf = C218_LoadBuf; + loadlen = C218DLoad_len; + checksum = C218check_sum; + checksum_ok = C218chksum_ok; + break; + default: + key = C320_key; + keycode = C320_KeyCode; + loadbuf = C320_LoadBuf; + loadlen = C320DLoad_len; + checksum = C320check_sum; + checksum_ok = C320chksum_ok; + break; + } + + usum = 0; + wlen = len >> 1; + for (i = 0; i < wlen; i++) + usum += le16_to_cpu(uptr[i]); + retry = 0; + do { + wlen = len >> 1; + j = 0; + while (wlen) { + len2 = (wlen > 2048) ? 2048 : wlen; + wlen -= len2; + memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1); + j += len2 << 1; + + writew(len2, baseAddr + loadlen); + writew(0, baseAddr + key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + key) == keycode) + break; + msleep(10); + } + if (readw(baseAddr + key) != keycode) + return -EIO; + } + writew(0, baseAddr + loadlen); + writew(usum, baseAddr + checksum); + writew(0, baseAddr + key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + key) == keycode) + break; + msleep(10); + } + retry++; + } while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + checksum_ok) != 1) + return -EIO; + + writew(0, baseAddr + key); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + + if (MOXA_IS_320(brd)) { + if (brd->busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ + writew(0x3800, baseAddr + TMS320_PORT1); + writew(0x3900, baseAddr + TMS320_PORT2); + writew(28499, baseAddr + TMS320_CLOCK); + } else { + writew(0x3200, baseAddr + TMS320_PORT1); + writew(0x3400, baseAddr + TMS320_PORT2); + writew(19999, baseAddr + TMS320_CLOCK); + } + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 500; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + + if (MOXA_IS_320(brd)) { + j = readw(baseAddr + Module_cnt); + if (j <= 0) + return -EIO; + brd->numPorts = j * 8; + writew(j, baseAddr + Module_no); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + msleep(10); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return -EIO; + } + brd->intNdx = baseAddr + IRQindex; + brd->intPend = baseAddr + IRQpending; + brd->intTable = baseAddr + IRQtable; + + return 0; +} + +static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr, + size_t len) +{ + void __iomem *ofsAddr, *baseAddr = brd->basemem; + struct moxa_port *port; + int retval, i; + + if (len % 2) { + printk(KERN_ERR "MOXA: bios length is not even\n"); + return -EINVAL; + } + + retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */ + if (retval) + return retval; + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + case MOXA_BOARD_CP204J: + port = brd->ports; + for (i = 0; i < brd->numPorts; i++, port++) { + port->board = brd; + port->DCDState = 0; + port->tableAddr = baseAddr + Extern_table + + Extern_size * i; + ofsAddr = port->tableAddr; + writew(C218rx_mask, ofsAddr + RX_mask); + writew(C218tx_mask, ofsAddr + TX_mask); + writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); + + writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); + + } + break; + default: + port = brd->ports; + for (i = 0; i < brd->numPorts; i++, port++) { + port->board = brd; + port->DCDState = 0; + port->tableAddr = baseAddr + Extern_table + + Extern_size * i; + ofsAddr = port->tableAddr; + switch (brd->numPorts) { + case 8: + writew(C320p8rx_mask, ofsAddr + RX_mask); + writew(C320p8tx_mask, ofsAddr + TX_mask); + writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); + + break; + case 16: + writew(C320p16rx_mask, ofsAddr + RX_mask); + writew(C320p16tx_mask, ofsAddr + TX_mask); + writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); + break; + + case 24: + writew(C320p24rx_mask, ofsAddr + RX_mask); + writew(C320p24tx_mask, ofsAddr + TX_mask); + writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + break; + case 32: + writew(C320p32rx_mask, ofsAddr + RX_mask); + writew(C320p32tx_mask, ofsAddr + TX_mask); + writew(C320p32tx_ofs, ofsAddr + Ofs_txb); + writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); + writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); + writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + break; + } + } + break; + } + return 0; +} + +static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw) +{ + const void *ptr = fw->data; + char rsn[64]; + u16 lens[5]; + size_t len; + unsigned int a, lenp, lencnt; + int ret = -EINVAL; + struct { + __le32 magic; /* 0x34303430 */ + u8 reserved1[2]; + u8 type; /* UNIX = 3 */ + u8 model; /* C218T=1, C320T=2, CP204=3 */ + u8 reserved2[8]; + __le16 len[5]; + } const *hdr = ptr; + + BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens)); + + if (fw->size < MOXA_FW_HDRLEN) { + strcpy(rsn, "too short (even header won't fit)"); + goto err; + } + if (hdr->magic != cpu_to_le32(0x30343034)) { + sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic)); + goto err; + } + if (hdr->type != 3) { + sprintf(rsn, "not for linux, type is %u", hdr->type); + goto err; + } + if (moxa_check_fw_model(brd, hdr->model)) { + sprintf(rsn, "not for this card, model is %u", hdr->model); + goto err; + } + + len = MOXA_FW_HDRLEN; + lencnt = hdr->model == 2 ? 5 : 3; + for (a = 0; a < ARRAY_SIZE(lens); a++) { + lens[a] = le16_to_cpu(hdr->len[a]); + if (lens[a] && len + lens[a] <= fw->size && + moxa_check_fw(&fw->data[len])) + printk(KERN_WARNING "MOXA firmware: unexpected input " + "at offset %u, but going on\n", (u32)len); + if (!lens[a] && a < lencnt) { + sprintf(rsn, "too few entries in fw file"); + goto err; + } + len += lens[a]; + } + + if (len != fw->size) { + sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size, + (u32)len); + goto err; + } + + ptr += MOXA_FW_HDRLEN; + lenp = 0; /* bios */ + + strcpy(rsn, "read above"); + + ret = moxa_load_bios(brd, ptr, lens[lenp]); + if (ret) + goto err; + + /* we skip the tty section (lens[1]), since we don't need it */ + ptr += lens[lenp] + lens[lenp + 1]; + lenp += 2; /* comm */ + + if (hdr->model == 2) { + ret = moxa_load_320b(brd, ptr, lens[lenp]); + if (ret) + goto err; + /* skip another tty */ + ptr += lens[lenp] + lens[lenp + 1]; + lenp += 2; + } + + ret = moxa_load_code(brd, ptr, lens[lenp]); + if (ret) + goto err; + + return 0; +err: + printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn); + return ret; +} + +static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) +{ + const struct firmware *fw; + const char *file; + struct moxa_port *p; + unsigned int i; + int ret; + + brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports), + GFP_KERNEL); + if (brd->ports == NULL) { + printk(KERN_ERR "cannot allocate memory for ports\n"); + ret = -ENOMEM; + goto err; + } + + for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { + tty_port_init(&p->port); + p->port.ops = &moxa_port_ops; + p->type = PORT_16550A; + p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + } + + switch (brd->boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + file = "c218tunx.cod"; + break; + case MOXA_BOARD_CP204J: + file = "cp204unx.cod"; + break; + default: + file = "c320tunx.cod"; + break; + } + + ret = request_firmware(&fw, file, dev); + if (ret) { + printk(KERN_ERR "MOXA: request_firmware failed. Make sure " + "you've placed '%s' file into your firmware " + "loader directory (e.g. /lib/firmware)\n", + file); + goto err_free; + } + + ret = moxa_load_fw(brd, fw); + + release_firmware(fw); + + if (ret) + goto err_free; + + spin_lock_bh(&moxa_lock); + brd->ready = 1; + if (!timer_pending(&moxaTimer)) + mod_timer(&moxaTimer, jiffies + HZ / 50); + spin_unlock_bh(&moxa_lock); + + return 0; +err_free: + kfree(brd->ports); +err: + return ret; +} + +static void moxa_board_deinit(struct moxa_board_conf *brd) +{ + unsigned int a, opened; + + mutex_lock(&moxa_openlock); + spin_lock_bh(&moxa_lock); + brd->ready = 0; + spin_unlock_bh(&moxa_lock); + + /* pci hot-un-plug support */ + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { + struct tty_struct *tty = tty_port_tty_get( + &brd->ports[a].port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + } + while (1) { + opened = 0; + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + opened++; + mutex_unlock(&moxa_openlock); + if (!opened) + break; + msleep(50); + mutex_lock(&moxa_openlock); + } + + iounmap(brd->basemem); + brd->basemem = NULL; + kfree(brd->ports); +} + +#ifdef CONFIG_PCI +static int __devinit moxa_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct moxa_board_conf *board; + unsigned int i; + int board_type = ent->driver_data; + int retval; + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "can't enable pci device\n"); + goto err; + } + + for (i = 0; i < MAX_BOARDS; i++) + if (moxa_boards[i].basemem == NULL) + break; + + retval = -ENODEV; + if (i >= MAX_BOARDS) { + dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards " + "found. Board is ignored.\n", MAX_BOARDS); + goto err; + } + + board = &moxa_boards[i]; + + retval = pci_request_region(pdev, 2, "moxa-base"); + if (retval) { + dev_err(&pdev->dev, "can't request pci region 2\n"); + goto err; + } + + board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000); + if (board->basemem == NULL) { + dev_err(&pdev->dev, "can't remap io space 2\n"); + goto err_reg; + } + + board->boardType = board_type; + switch (board_type) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + board->numPorts = 8; + break; + + case MOXA_BOARD_CP204J: + board->numPorts = 4; + break; + default: + board->numPorts = 0; + break; + } + board->busType = MOXA_BUS_TYPE_PCI; + + retval = moxa_init_board(board, &pdev->dev); + if (retval) + goto err_base; + + pci_set_drvdata(pdev, board); + + dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n", + moxa_brdname[board_type - 1], board->numPorts); + + return 0; +err_base: + iounmap(board->basemem); + board->basemem = NULL; +err_reg: + pci_release_region(pdev, 2); +err: + return retval; +} + +static void __devexit moxa_pci_remove(struct pci_dev *pdev) +{ + struct moxa_board_conf *brd = pci_get_drvdata(pdev); + + moxa_board_deinit(brd); + + pci_release_region(pdev, 2); +} + +static struct pci_driver moxa_pci_driver = { + .name = "moxa", + .id_table = moxa_pcibrds, + .probe = moxa_pci_probe, + .remove = __devexit_p(moxa_pci_remove) +}; +#endif /* CONFIG_PCI */ + +static int __init moxa_init(void) +{ + unsigned int isabrds = 0; + int retval = 0; + struct moxa_board_conf *brd = moxa_boards; + unsigned int i; + + printk(KERN_INFO "MOXA Intellio family driver version %s\n", + MOXA_VERSION); + moxaDriver = alloc_tty_driver(MAX_PORTS + 1); + if (!moxaDriver) + return -ENOMEM; + + moxaDriver->owner = THIS_MODULE; + moxaDriver->name = "ttyMX"; + moxaDriver->major = ttymajor; + moxaDriver->minor_start = 0; + moxaDriver->type = TTY_DRIVER_TYPE_SERIAL; + moxaDriver->subtype = SERIAL_TYPE_NORMAL; + moxaDriver->init_termios = tty_std_termios; + moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + moxaDriver->init_termios.c_ispeed = 9600; + moxaDriver->init_termios.c_ospeed = 9600; + moxaDriver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(moxaDriver, &moxa_ops); + + if (tty_register_driver(moxaDriver)) { + printk(KERN_ERR "can't register MOXA Smartio tty driver!\n"); + put_tty_driver(moxaDriver); + return -1; + } + + /* Find the boards defined from module args. */ + + for (i = 0; i < MAX_BOARDS; i++) { + if (!baseaddr[i]) + break; + if (type[i] == MOXA_BOARD_C218_ISA || + type[i] == MOXA_BOARD_C320_ISA) { + pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n", + isabrds + 1, moxa_brdname[type[i] - 1], + baseaddr[i]); + brd->boardType = type[i]; + brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 : + numports[i]; + brd->busType = MOXA_BUS_TYPE_ISA; + brd->basemem = ioremap_nocache(baseaddr[i], 0x4000); + if (!brd->basemem) { + printk(KERN_ERR "MOXA: can't remap %lx\n", + baseaddr[i]); + continue; + } + if (moxa_init_board(brd, NULL)) { + iounmap(brd->basemem); + brd->basemem = NULL; + continue; + } + + printk(KERN_INFO "MOXA isa board found at 0x%.8lu and " + "ready (%u ports, firmware loaded)\n", + baseaddr[i], brd->numPorts); + + brd++; + isabrds++; + } + } + +#ifdef CONFIG_PCI + retval = pci_register_driver(&moxa_pci_driver); + if (retval) { + printk(KERN_ERR "Can't register MOXA pci driver!\n"); + if (isabrds) + retval = 0; + } +#endif + + return retval; +} + +static void __exit moxa_exit(void) +{ + unsigned int i; + +#ifdef CONFIG_PCI + pci_unregister_driver(&moxa_pci_driver); +#endif + + for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */ + if (moxa_boards[i].ready) + moxa_board_deinit(&moxa_boards[i]); + + del_timer_sync(&moxaTimer); + + if (tty_unregister_driver(moxaDriver)) + printk(KERN_ERR "Couldn't unregister MOXA Intellio family " + "serial driver\n"); + put_tty_driver(moxaDriver); +} + +module_init(moxa_init); +module_exit(moxa_exit); + +static void moxa_shutdown(struct tty_port *port) +{ + struct moxa_port *ch = container_of(port, struct moxa_port, port); + MoxaPortDisable(ch); + MoxaPortFlushData(ch, 2); + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); +} + +static int moxa_carrier_raised(struct tty_port *port) +{ + struct moxa_port *ch = container_of(port, struct moxa_port, port); + int dcd; + + spin_lock_irq(&port->lock); + dcd = ch->DCDState; + spin_unlock_irq(&port->lock); + return dcd; +} + +static void moxa_dtr_rts(struct tty_port *port, int onoff) +{ + struct moxa_port *ch = container_of(port, struct moxa_port, port); + MoxaPortLineCtrl(ch, onoff, onoff); +} + + +static int moxa_open(struct tty_struct *tty, struct file *filp) +{ + struct moxa_board_conf *brd; + struct moxa_port *ch; + int port; + int retval; + + port = tty->index; + if (port == MAX_PORTS) { + return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; + } + if (mutex_lock_interruptible(&moxa_openlock)) + return -ERESTARTSYS; + brd = &moxa_boards[port / MAX_PORTS_PER_BOARD]; + if (!brd->ready) { + mutex_unlock(&moxa_openlock); + return -ENODEV; + } + + if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) { + mutex_unlock(&moxa_openlock); + return -ENODEV; + } + + ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; + ch->port.count++; + tty->driver_data = ch; + tty_port_tty_set(&ch->port, tty); + mutex_lock(&ch->port.mutex); + if (!(ch->port.flags & ASYNC_INITIALIZED)) { + ch->statusflags = 0; + moxa_set_tty_param(tty, tty->termios); + MoxaPortLineCtrl(ch, 1, 1); + MoxaPortEnable(ch); + MoxaSetFifo(ch, ch->type == PORT_16550A); + ch->port.flags |= ASYNC_INITIALIZED; + } + mutex_unlock(&ch->port.mutex); + mutex_unlock(&moxa_openlock); + + retval = tty_port_block_til_ready(&ch->port, tty, filp); + if (retval == 0) + set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags); + return retval; +} + +static void moxa_close(struct tty_struct *tty, struct file *filp) +{ + struct moxa_port *ch = tty->driver_data; + ch->cflag = tty->termios->c_cflag; + tty_port_close(&ch->port, tty, filp); +} + +static int moxa_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct moxa_port *ch = tty->driver_data; + int len; + + if (ch == NULL) + return 0; + + spin_lock_bh(&moxa_lock); + len = MoxaPortWriteData(tty, buf, count); + spin_unlock_bh(&moxa_lock); + + set_bit(LOWWAIT, &ch->statusflags); + return len; +} + +static int moxa_write_room(struct tty_struct *tty) +{ + struct moxa_port *ch; + + if (tty->stopped) + return 0; + ch = tty->driver_data; + if (ch == NULL) + return 0; + return MoxaPortTxFree(ch); +} + +static void moxa_flush_buffer(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + + if (ch == NULL) + return; + MoxaPortFlushData(ch, 1); + tty_wakeup(tty); +} + +static int moxa_chars_in_buffer(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + int chars; + + chars = MoxaPortTxQueue(ch); + if (chars) + /* + * Make it possible to wakeup anything waiting for output + * in tty_ioctl.c, etc. + */ + set_bit(EMPTYWAIT, &ch->statusflags); + return chars; +} + +static int moxa_tiocmget(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + int flag = 0, dtr, rts; + + MoxaPortGetLineOut(ch, &dtr, &rts); + if (dtr) + flag |= TIOCM_DTR; + if (rts) + flag |= TIOCM_RTS; + dtr = MoxaPortLineStatus(ch); + if (dtr & 1) + flag |= TIOCM_CTS; + if (dtr & 2) + flag |= TIOCM_DSR; + if (dtr & 4) + flag |= TIOCM_CD; + return flag; +} + +static int moxa_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct moxa_port *ch; + int port; + int dtr, rts; + + port = tty->index; + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (!ch) { + mutex_unlock(&moxa_openlock); + return -EINVAL; + } + + MoxaPortGetLineOut(ch, &dtr, &rts); + if (set & TIOCM_RTS) + rts = 1; + if (set & TIOCM_DTR) + dtr = 1; + if (clear & TIOCM_RTS) + rts = 0; + if (clear & TIOCM_DTR) + dtr = 0; + MoxaPortLineCtrl(ch, dtr, rts); + mutex_unlock(&moxa_openlock); + return 0; +} + +static void moxa_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct moxa_port *ch = tty->driver_data; + + if (ch == NULL) + return; + moxa_set_tty_param(tty, old_termios); + if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty)) + wake_up_interruptible(&ch->port.open_wait); +} + +static void moxa_stop(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + + if (ch == NULL) + return; + MoxaPortTxDisable(ch); + set_bit(TXSTOPPED, &ch->statusflags); +} + + +static void moxa_start(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + + if (ch == NULL) + return; + + if (!(ch->statusflags & TXSTOPPED)) + return; + + MoxaPortTxEnable(ch); + clear_bit(TXSTOPPED, &ch->statusflags); +} + +static void moxa_hangup(struct tty_struct *tty) +{ + struct moxa_port *ch = tty->driver_data; + tty_port_hangup(&ch->port); +} + +static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) +{ + struct tty_struct *tty; + unsigned long flags; + dcd = !!dcd; + + spin_lock_irqsave(&p->port.lock, flags); + if (dcd != p->DCDState) { + p->DCDState = dcd; + spin_unlock_irqrestore(&p->port.lock, flags); + tty = tty_port_tty_get(&p->port); + if (tty && C_CLOCAL(tty) && !dcd) + tty_hangup(tty); + tty_kref_put(tty); + } + else + spin_unlock_irqrestore(&p->port.lock, flags); +} + +static int moxa_poll_port(struct moxa_port *p, unsigned int handle, + u16 __iomem *ip) +{ + struct tty_struct *tty = tty_port_tty_get(&p->port); + void __iomem *ofsAddr; + unsigned int inited = p->port.flags & ASYNC_INITIALIZED; + u16 intr; + + if (tty) { + if (test_bit(EMPTYWAIT, &p->statusflags) && + MoxaPortTxQueue(p) == 0) { + clear_bit(EMPTYWAIT, &p->statusflags); + tty_wakeup(tty); + } + if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped && + MoxaPortTxQueue(p) <= WAKEUP_CHARS) { + clear_bit(LOWWAIT, &p->statusflags); + tty_wakeup(tty); + } + + if (inited && !test_bit(TTY_THROTTLED, &tty->flags) && + MoxaPortRxQueue(p) > 0) { /* RX */ + MoxaPortReadData(p); + tty_schedule_flip(tty); + } + } else { + clear_bit(EMPTYWAIT, &p->statusflags); + MoxaPortFlushData(p, 0); /* flush RX */ + } + + if (!handle) /* nothing else to do */ + goto put; + + intr = readw(ip); /* port irq status */ + if (intr == 0) + goto put; + + writew(0, ip); /* ACK port */ + ofsAddr = p->tableAddr; + if (intr & IntrTx) /* disable tx intr */ + writew(readw(ofsAddr + HostStat) & ~WakeupTx, + ofsAddr + HostStat); + + if (!inited) + goto put; + + if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */ + tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_schedule_flip(tty); + } + + if (intr & IntrLine) + moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state); +put: + tty_kref_put(tty); + + return 0; +} + +static void moxa_poll(unsigned long ignored) +{ + struct moxa_board_conf *brd; + u16 __iomem *ip; + unsigned int card, port, served = 0; + + spin_lock(&moxa_lock); + for (card = 0; card < MAX_BOARDS; card++) { + brd = &moxa_boards[card]; + if (!brd->ready) + continue; + + served++; + + ip = NULL; + if (readb(brd->intPend) == 0xff) + ip = brd->intTable + readb(brd->intNdx); + + for (port = 0; port < brd->numPorts; port++) + moxa_poll_port(&brd->ports[port], !!ip, ip + port); + + if (ip) + writeb(0, brd->intPend); /* ACK */ + + if (moxaLowWaterChk) { + struct moxa_port *p = brd->ports; + for (port = 0; port < brd->numPorts; port++, p++) + if (p->lowChkFlag) { + p->lowChkFlag = 0; + moxa_low_water_check(p->tableAddr); + } + } + } + moxaLowWaterChk = 0; + + if (served) + mod_timer(&moxaTimer, jiffies + HZ / 50); + spin_unlock(&moxa_lock); +} + +/******************************************************************************/ + +static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios) +{ + register struct ktermios *ts = tty->termios; + struct moxa_port *ch = tty->driver_data; + int rts, cts, txflow, rxflow, xany, baud; + + rts = cts = txflow = rxflow = xany = 0; + if (ts->c_cflag & CRTSCTS) + rts = cts = 1; + if (ts->c_iflag & IXON) + txflow = 1; + if (ts->c_iflag & IXOFF) + rxflow = 1; + if (ts->c_iflag & IXANY) + xany = 1; + + /* Clear the features we don't support */ + ts->c_cflag &= ~CMSPAR; + MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany); + baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty)); + if (baud == -1) + baud = tty_termios_baud_rate(old_termios); + /* Not put the baud rate into the termios data */ + tty_encode_baud_rate(tty, baud, baud); +} + +/***************************************************************************** + * Driver level functions: * + *****************************************************************************/ + +static void MoxaPortFlushData(struct moxa_port *port, int mode) +{ + void __iomem *ofsAddr; + if (mode < 0 || mode > 2) + return; + ofsAddr = port->tableAddr; + moxafunc(ofsAddr, FC_FlushQueue, mode); + if (mode != 1) { + port->lowChkFlag = 0; + moxa_low_water_check(ofsAddr); + } +} + +/* + * Moxa Port Number Description: + * + * MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And, + * the port number using in MOXA driver functions will be 0 to 31 for + * first MOXA board, 32 to 63 for second, 64 to 95 for third and 96 + * to 127 for fourth. For example, if you setup three MOXA boards, + * first board is C218, second board is C320-16 and third board is + * C320-32. The port number of first board (C218 - 8 ports) is from + * 0 to 7. The port number of second board (C320 - 16 ports) is form + * 32 to 47. The port number of third board (C320 - 32 ports) is from + * 64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to + * 127 will be invalid. + * + * + * Moxa Functions Description: + * + * Function 1: Driver initialization routine, this routine must be + * called when initialized driver. + * Syntax: + * void MoxaDriverInit(); + * + * + * Function 2: Moxa driver private IOCTL command processing. + * Syntax: + * int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); + * + * unsigned int cmd : IOCTL command + * unsigned long arg : IOCTL argument + * int port : port number (0 - 127) + * + * return: 0 (OK) + * -EINVAL + * -ENOIOCTLCMD + * + * + * Function 6: Enable this port to start Tx/Rx data. + * Syntax: + * void MoxaPortEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 7: Disable this port + * Syntax: + * void MoxaPortDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 10: Setting baud rate of this port. + * Syntax: + * speed_t MoxaPortSetBaud(int port, speed_t baud); + * int port : port number (0 - 127) + * long baud : baud rate (50 - 115200) + * + * return: 0 : this port is invalid or baud < 50 + * 50 - 115200 : the real baud rate set to the port, if + * the argument baud is large than maximun + * available baud rate, the real setting + * baud rate will be the maximun baud rate. + * + * + * Function 12: Configure the port. + * Syntax: + * int MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud); + * int port : port number (0 - 127) + * struct ktermios * termio : termio structure pointer + * speed_t baud : baud rate + * + * return: -1 : this port is invalid or termio == NULL + * 0 : setting O.K. + * + * + * Function 13: Get the DTR/RTS state of this port. + * Syntax: + * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); + * int port : port number (0 - 127) + * int * dtrState : pointer to INT to receive the current DTR + * state. (if NULL, this function will not + * write to this address) + * int * rtsState : pointer to INT to receive the current RTS + * state. (if NULL, this function will not + * write to this address) + * + * return: -1 : this port is invalid + * 0 : O.K. + * + * + * Function 14: Setting the DTR/RTS output state of this port. + * Syntax: + * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); + * int port : port number (0 - 127) + * int dtrState : DTR output state (0: off, 1: on) + * int rtsState : RTS output state (0: off, 1: on) + * + * + * Function 15: Setting the flow control of this port. + * Syntax: + * void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow, + * int txFlow,int xany); + * int port : port number (0 - 127) + * int rtsFlow : H/W RTS flow control (0: no, 1: yes) + * int ctsFlow : H/W CTS flow control (0: no, 1: yes) + * int rxFlow : S/W Rx XON/XOFF flow control (0: no, 1: yes) + * int txFlow : S/W Tx XON/XOFF flow control (0: no, 1: yes) + * int xany : S/W XANY flow control (0: no, 1: yes) + * + * + * Function 16: Get ths line status of this port + * Syntax: + * int MoxaPortLineStatus(int port); + * int port : port number (0 - 127) + * + * return: Bit 0 - CTS state (0: off, 1: on) + * Bit 1 - DSR state (0: off, 1: on) + * Bit 2 - DCD state (0: off, 1: on) + * + * + * Function 19: Flush the Rx/Tx buffer data of this port. + * Syntax: + * void MoxaPortFlushData(int port, int mode); + * int port : port number (0 - 127) + * int mode + * 0 : flush the Rx buffer + * 1 : flush the Tx buffer + * 2 : flush the Rx and Tx buffer + * + * + * Function 20: Write data. + * Syntax: + * int MoxaPortWriteData(int port, unsigned char * buffer, int length); + * int port : port number (0 - 127) + * unsigned char * buffer : pointer to write data buffer. + * int length : write data length + * + * return: 0 - length : real write data length + * + * + * Function 21: Read data. + * Syntax: + * int MoxaPortReadData(int port, struct tty_struct *tty); + * int port : port number (0 - 127) + * struct tty_struct *tty : tty for data + * + * return: 0 - length : real read data length + * + * + * Function 24: Get the Tx buffer current queued data bytes + * Syntax: + * int MoxaPortTxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current queued data bytes + * + * + * Function 25: Get the Tx buffer current free space + * Syntax: + * int MoxaPortTxFree(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current free space + * + * + * Function 26: Get the Rx buffer current queued data bytes + * Syntax: + * int MoxaPortRxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer current queued data bytes + * + * + * Function 28: Disable port data transmission. + * Syntax: + * void MoxaPortTxDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 29: Enable port data transmission. + * Syntax: + * void MoxaPortTxEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 31: Get the received BREAK signal count and reset it. + * Syntax: + * int MoxaPortResetBrkCnt(int port); + * int port : port number (0 - 127) + * + * return: 0 - .. : BREAK signal count + * + * + */ + +static void MoxaPortEnable(struct moxa_port *port) +{ + void __iomem *ofsAddr; + u16 lowwater = 512; + + ofsAddr = port->tableAddr; + writew(lowwater, ofsAddr + Low_water); + if (MOXA_IS_320(port->board)) + moxafunc(ofsAddr, FC_SetBreakIrq, 0); + else + writew(readw(ofsAddr + HostStat) | WakeupBreak, + ofsAddr + HostStat); + + moxafunc(ofsAddr, FC_SetLineIrq, Magic_code); + moxafunc(ofsAddr, FC_FlushQueue, 2); + + moxafunc(ofsAddr, FC_EnableCH, Magic_code); + MoxaPortLineStatus(port); +} + +static void MoxaPortDisable(struct moxa_port *port) +{ + void __iomem *ofsAddr = port->tableAddr; + + moxafunc(ofsAddr, FC_SetFlowCtl, 0); /* disable flow control */ + moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code); + writew(0, ofsAddr + HostStat); + moxafunc(ofsAddr, FC_DisableCH, Magic_code); +} + +static speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud) +{ + void __iomem *ofsAddr = port->tableAddr; + unsigned int clock, val; + speed_t max; + + max = MOXA_IS_320(port->board) ? 460800 : 921600; + if (baud < 50) + return 0; + if (baud > max) + baud = max; + clock = 921600; + val = clock / baud; + moxafunc(ofsAddr, FC_SetBaud, val); + baud = clock / val; + return baud; +} + +static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, + speed_t baud) +{ + void __iomem *ofsAddr; + tcflag_t cflag; + tcflag_t mode = 0; + + ofsAddr = port->tableAddr; + cflag = termio->c_cflag; /* termio->c_cflag */ + + mode = termio->c_cflag & CSIZE; + if (mode == CS5) + mode = MX_CS5; + else if (mode == CS6) + mode = MX_CS6; + else if (mode == CS7) + mode = MX_CS7; + else if (mode == CS8) + mode = MX_CS8; + + if (termio->c_cflag & CSTOPB) { + if (mode == MX_CS5) + mode |= MX_STOP15; + else + mode |= MX_STOP2; + } else + mode |= MX_STOP1; + + if (termio->c_cflag & PARENB) { + if (termio->c_cflag & PARODD) + mode |= MX_PARODD; + else + mode |= MX_PAREVEN; + } else + mode |= MX_PARNONE; + + moxafunc(ofsAddr, FC_SetDataMode, (u16)mode); + + if (MOXA_IS_320(port->board) && baud >= 921600) + return -1; + + baud = MoxaPortSetBaud(port, baud); + + if (termio->c_iflag & (IXON | IXOFF | IXANY)) { + spin_lock_irq(&moxafunc_lock); + writeb(termio->c_cc[VSTART], ofsAddr + FuncArg); + writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1); + writeb(FC_SetXonXoff, ofsAddr + FuncCode); + moxa_wait_finish(ofsAddr); + spin_unlock_irq(&moxafunc_lock); + + } + return baud; +} + +static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState, + int *rtsState) +{ + if (dtrState) + *dtrState = !!(port->lineCtrl & DTR_ON); + if (rtsState) + *rtsState = !!(port->lineCtrl & RTS_ON); + + return 0; +} + +static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts) +{ + u8 mode = 0; + + if (dtr) + mode |= DTR_ON; + if (rts) + mode |= RTS_ON; + port->lineCtrl = mode; + moxafunc(port->tableAddr, FC_LineControl, mode); +} + +static void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts, + int txflow, int rxflow, int txany) +{ + int mode = 0; + + if (rts) + mode |= RTS_FlowCtl; + if (cts) + mode |= CTS_FlowCtl; + if (txflow) + mode |= Tx_FlowCtl; + if (rxflow) + mode |= Rx_FlowCtl; + if (txany) + mode |= IXM_IXANY; + moxafunc(port->tableAddr, FC_SetFlowCtl, mode); +} + +static int MoxaPortLineStatus(struct moxa_port *port) +{ + void __iomem *ofsAddr; + int val; + + ofsAddr = port->tableAddr; + if (MOXA_IS_320(port->board)) + val = moxafuncret(ofsAddr, FC_LineStatus, 0); + else + val = readw(ofsAddr + FlagStat) >> 4; + val &= 0x0B; + if (val & 8) + val |= 4; + moxa_new_dcdstate(port, val & 8); + val &= 7; + return val; +} + +static int MoxaPortWriteData(struct tty_struct *tty, + const unsigned char *buffer, int len) +{ + struct moxa_port *port = tty->driver_data; + void __iomem *baseAddr, *ofsAddr, *ofs; + unsigned int c, total; + u16 head, tail, tx_mask, spage, epage; + u16 pageno, pageofs, bufhead; + + ofsAddr = port->tableAddr; + baseAddr = port->board->basemem; + tx_mask = readw(ofsAddr + TX_mask); + spage = readw(ofsAddr + Page_txb); + epage = readw(ofsAddr + EndPage_txb); + tail = readw(ofsAddr + TXwptr); + head = readw(ofsAddr + TXrptr); + c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask); + if (c > len) + c = len; + moxaLog.txcnt[port->port.tty->index] += c; + total = c; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_txb); + writew(spage, baseAddr + Control_reg); + while (c > 0) { + if (head > tail) + len = head - tail - 1; + else + len = tx_mask + 1 - tail; + len = (c > len) ? len : c; + ofs = baseAddr + DynPage_addr + bufhead + tail; + memcpy_toio(ofs, buffer, len); + buffer += len; + tail = (tail + len) & tx_mask; + c -= len; + } + } else { + pageno = spage + (tail >> 13); + pageofs = tail & Page_mask; + while (c > 0) { + len = Page_size - pageofs; + if (len > c) + len = c; + writeb(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + memcpy_toio(ofs, buffer, len); + buffer += len; + if (++pageno == epage) + pageno = spage; + pageofs = 0; + c -= len; + } + tail = (tail + total) & tx_mask; + } + writew(tail, ofsAddr + TXwptr); + writeb(1, ofsAddr + CD180TXirq); /* start to send */ + return total; +} + +static int MoxaPortReadData(struct moxa_port *port) +{ + struct tty_struct *tty = port->port.tty; + unsigned char *dst; + void __iomem *baseAddr, *ofsAddr, *ofs; + unsigned int count, len, total; + u16 tail, rx_mask, spage, epage; + u16 pageno, pageofs, bufhead, head; + + ofsAddr = port->tableAddr; + baseAddr = port->board->basemem; + head = readw(ofsAddr + RXrptr); + tail = readw(ofsAddr + RXwptr); + rx_mask = readw(ofsAddr + RX_mask); + spage = readw(ofsAddr + Page_rxb); + epage = readw(ofsAddr + EndPage_rxb); + count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1); + if (count == 0) + return 0; + + total = count; + moxaLog.rxcnt[tty->index] += total; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_rxb); + writew(spage, baseAddr + Control_reg); + while (count > 0) { + ofs = baseAddr + DynPage_addr + bufhead + head; + len = (tail >= head) ? (tail - head) : + (rx_mask + 1 - head); + len = tty_prepare_flip_string(tty, &dst, + min(len, count)); + memcpy_fromio(dst, ofs, len); + head = (head + len) & rx_mask; + count -= len; + } + } else { + pageno = spage + (head >> 13); + pageofs = head & Page_mask; + while (count > 0) { + writew(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + len = tty_prepare_flip_string(tty, &dst, + min(Page_size - pageofs, count)); + memcpy_fromio(dst, ofs, len); + + count -= len; + pageofs = (pageofs + len) & Page_mask; + if (pageofs == 0 && ++pageno == epage) + pageno = spage; + } + head = (head + total) & rx_mask; + } + writew(head, ofsAddr + RXrptr); + if (readb(ofsAddr + FlagStat) & Xoff_state) { + moxaLowWaterChk = 1; + port->lowChkFlag = 1; + } + return total; +} + + +static int MoxaPortTxQueue(struct moxa_port *port) +{ + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; + + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + return (wptr - rptr) & mask; +} + +static int MoxaPortTxFree(struct moxa_port *port) +{ + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; + + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + return mask - ((wptr - rptr) & mask); +} + +static int MoxaPortRxQueue(struct moxa_port *port) +{ + void __iomem *ofsAddr = port->tableAddr; + u16 rptr, wptr, mask; + + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + return (wptr - rptr) & mask; +} + +static void MoxaPortTxDisable(struct moxa_port *port) +{ + moxafunc(port->tableAddr, FC_SetXoffState, Magic_code); +} + +static void MoxaPortTxEnable(struct moxa_port *port) +{ + moxafunc(port->tableAddr, FC_SetXonState, Magic_code); +} + +static int moxa_get_serial_info(struct moxa_port *info, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp = { + .type = info->type, + .line = info->port.tty->index, + .flags = info->port.flags, + .baud_base = 921600, + .close_delay = info->port.close_delay + }; + return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; +} + + +static int moxa_set_serial_info(struct moxa_port *info, + struct serial_struct __user *new_info) +{ + struct serial_struct new_serial; + + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + if (new_serial.irq != 0 || new_serial.port != 0 || + new_serial.custom_divisor != 0 || + new_serial.baud_base != 921600) + return -EPERM; + + if (!capable(CAP_SYS_ADMIN)) { + if (((new_serial.flags & ~ASYNC_USR_MASK) != + (info->port.flags & ~ASYNC_USR_MASK))) + return -EPERM; + } else + info->port.close_delay = new_serial.close_delay * HZ / 100; + + new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); + new_serial.flags |= (info->port.flags & ASYNC_FLAGS); + + MoxaSetFifo(info, new_serial.type == PORT_16550A); + + info->type = new_serial.type; + return 0; +} + + + +/***************************************************************************** + * Static local functions: * + *****************************************************************************/ + +static void MoxaSetFifo(struct moxa_port *port, int enable) +{ + void __iomem *ofsAddr = port->tableAddr; + + if (!enable) { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1); + } else { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16); + } +} diff --git a/drivers/tty/moxa.h b/drivers/tty/moxa.h new file mode 100644 index 000000000000..87d16ce57be7 --- /dev/null +++ b/drivers/tty/moxa.h @@ -0,0 +1,304 @@ +#ifndef MOXA_H_FILE +#define MOXA_H_FILE + +#define MOXA 0x400 +#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ +#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_IOQUEUE (MOXA + 27) +#define MOXA_FLUSH_QUEUE (MOXA + 28) +#define MOXA_GETMSTATUS (MOXA + 65) + +/* + * System Configuration + */ + +#define Magic_code 0x404 + +/* + * for C218 BIOS initialization + */ +#define C218_ConfBase 0x800 +#define C218_status (C218_ConfBase + 0) /* BIOS running status */ +#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ +#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ +#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ +#define C218check_sum (C218_ConfBase + 8) /* BYTE */ +#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ +#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ +#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ +#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ +#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ + +#define C218_LoadBuf 0x0F00 +#define C218_KeyCode 0x218 +#define CP204J_KeyCode 0x204 + +/* + * for C320 BIOS initialization + */ +#define C320_ConfBase 0x800 +#define C320_LoadBuf 0x0f00 +#define STS_init 0x05 /* for C320_status */ + +#define C320_status C320_ConfBase + 0 /* BIOS running status */ +#define C320_diag C320_ConfBase + 2 /* diagnostic status */ +#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ +#define C320DLoad_len C320_ConfBase + 6 /* WORD */ +#define C320check_sum C320_ConfBase + 8 /* WORD */ +#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ +#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ +#define C320UART_no C320_ConfBase + 0x0e /* WORD */ + +#define C320_KeyCode 0x320 + +#define FixPage_addr 0x0000 /* starting addr of static page */ +#define DynPage_addr 0x2000 /* starting addr of dynamic page */ +#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ +#define Control_reg 0x1ff0 /* select page and reset control */ +#define HW_reset 0x80 + +/* + * Function Codes + */ +#define FC_CardReset 0x80 +#define FC_ChannelReset 1 /* C320 firmware not supported */ +#define FC_EnableCH 2 +#define FC_DisableCH 3 +#define FC_SetParam 4 +#define FC_SetMode 5 +#define FC_SetRate 6 +#define FC_LineControl 7 +#define FC_LineStatus 8 +#define FC_XmitControl 9 +#define FC_FlushQueue 10 +#define FC_SendBreak 11 +#define FC_StopBreak 12 +#define FC_LoopbackON 13 +#define FC_LoopbackOFF 14 +#define FC_ClrIrqTable 15 +#define FC_SendXon 16 +#define FC_SetTermIrq 17 /* C320 firmware not supported */ +#define FC_SetCntIrq 18 /* C320 firmware not supported */ +#define FC_SetBreakIrq 19 +#define FC_SetLineIrq 20 +#define FC_SetFlowCtl 21 +#define FC_GenIrq 22 +#define FC_InCD180 23 +#define FC_OutCD180 24 +#define FC_InUARTreg 23 +#define FC_OutUARTreg 24 +#define FC_SetXonXoff 25 +#define FC_OutCD180CCR 26 +#define FC_ExtIQueue 27 +#define FC_ExtOQueue 28 +#define FC_ClrLineIrq 29 +#define FC_HWFlowCtl 30 +#define FC_GetClockRate 35 +#define FC_SetBaud 36 +#define FC_SetDataMode 41 +#define FC_GetCCSR 43 +#define FC_GetDataError 45 +#define FC_RxControl 50 +#define FC_ImmSend 51 +#define FC_SetXonState 52 +#define FC_SetXoffState 53 +#define FC_SetRxFIFOTrig 54 +#define FC_SetTxFIFOCnt 55 +#define FC_UnixRate 56 +#define FC_UnixResetTimer 57 + +#define RxFIFOTrig1 0 +#define RxFIFOTrig4 1 +#define RxFIFOTrig8 2 +#define RxFIFOTrig14 3 + +/* + * Dual-Ported RAM + */ +#define DRAM_global 0 +#define INT_data (DRAM_global + 0) +#define Config_base (DRAM_global + 0x108) + +#define IRQindex (INT_data + 0) +#define IRQpending (INT_data + 4) +#define IRQtable (INT_data + 8) + +/* + * Interrupt Status + */ +#define IntrRx 0x01 /* receiver data O.K. */ +#define IntrTx 0x02 /* transmit buffer empty */ +#define IntrFunc 0x04 /* function complete */ +#define IntrBreak 0x08 /* received break */ +#define IntrLine 0x10 /* line status change + for transmitter */ +#define IntrIntr 0x20 /* received INTR code */ +#define IntrQuit 0x40 /* received QUIT code */ +#define IntrEOF 0x80 /* received EOF code */ + +#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ +#define IntrTxTrigger 0x200 /* tx data count below trigger value */ + +#define Magic_no (Config_base + 0) +#define Card_model_no (Config_base + 2) +#define Total_ports (Config_base + 4) +#define Module_cnt (Config_base + 8) +#define Module_no (Config_base + 10) +#define Timer_10ms (Config_base + 14) +#define Disable_IRQ (Config_base + 20) +#define TMS320_PORT1 (Config_base + 22) +#define TMS320_PORT2 (Config_base + 24) +#define TMS320_CLOCK (Config_base + 26) + +/* + * DATA BUFFER in DRAM + */ +#define Extern_table 0x400 /* Base address of the external table + (24 words * 64) total 3K bytes + (24 words * 128) total 6K bytes */ +#define Extern_size 0x60 /* 96 bytes */ +#define RXrptr 0x00 /* read pointer for RX buffer */ +#define RXwptr 0x02 /* write pointer for RX buffer */ +#define TXrptr 0x04 /* read pointer for TX buffer */ +#define TXwptr 0x06 /* write pointer for TX buffer */ +#define HostStat 0x08 /* IRQ flag and general flag */ +#define FlagStat 0x0A +#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ + /* x x x x | | | | */ + /* | | | + CTS flow */ + /* | | +--- RTS flow */ + /* | +------ TX Xon/Xoff */ + /* +--------- RX Xon/Xoff */ +#define Break_cnt 0x0E /* received break count */ +#define CD180TXirq 0x10 /* if non-0: enable TX irq */ +#define RX_mask 0x12 +#define TX_mask 0x14 +#define Ofs_rxb 0x16 +#define Ofs_txb 0x18 +#define Page_rxb 0x1A +#define Page_txb 0x1C +#define EndPage_rxb 0x1E +#define EndPage_txb 0x20 +#define Data_error 0x22 +#define RxTrigger 0x28 +#define TxTrigger 0x2a + +#define rRXwptr 0x34 +#define Low_water 0x36 + +#define FuncCode 0x40 +#define FuncArg 0x42 +#define FuncArg1 0x44 + +#define C218rx_size 0x2000 /* 8K bytes */ +#define C218tx_size 0x8000 /* 32K bytes */ + +#define C218rx_mask (C218rx_size - 1) +#define C218tx_mask (C218tx_size - 1) + +#define C320p8rx_size 0x2000 +#define C320p8tx_size 0x8000 +#define C320p8rx_mask (C320p8rx_size - 1) +#define C320p8tx_mask (C320p8tx_size - 1) + +#define C320p16rx_size 0x2000 +#define C320p16tx_size 0x4000 +#define C320p16rx_mask (C320p16rx_size - 1) +#define C320p16tx_mask (C320p16tx_size - 1) + +#define C320p24rx_size 0x2000 +#define C320p24tx_size 0x2000 +#define C320p24rx_mask (C320p24rx_size - 1) +#define C320p24tx_mask (C320p24tx_size - 1) + +#define C320p32rx_size 0x1000 +#define C320p32tx_size 0x1000 +#define C320p32rx_mask (C320p32rx_size - 1) +#define C320p32tx_mask (C320p32tx_size - 1) + +#define Page_size 0x2000U +#define Page_mask (Page_size - 1) +#define C218rx_spage 3 +#define C218tx_spage 4 +#define C218rx_pageno 1 +#define C218tx_pageno 4 +#define C218buf_pageno 5 + +#define C320p8rx_spage 3 +#define C320p8tx_spage 4 +#define C320p8rx_pgno 1 +#define C320p8tx_pgno 4 +#define C320p8buf_pgno 5 + +#define C320p16rx_spage 3 +#define C320p16tx_spage 4 +#define C320p16rx_pgno 1 +#define C320p16tx_pgno 2 +#define C320p16buf_pgno 3 + +#define C320p24rx_spage 3 +#define C320p24tx_spage 4 +#define C320p24rx_pgno 1 +#define C320p24tx_pgno 1 +#define C320p24buf_pgno 2 + +#define C320p32rx_spage 3 +#define C320p32tx_ofs C320p32rx_size +#define C320p32tx_spage 3 +#define C320p32buf_pgno 1 + +/* + * Host Status + */ +#define WakeupRx 0x01 +#define WakeupTx 0x02 +#define WakeupBreak 0x08 +#define WakeupLine 0x10 +#define WakeupIntr 0x20 +#define WakeupQuit 0x40 +#define WakeupEOF 0x80 /* used in VTIME control */ +#define WakeupRxTrigger 0x100 +#define WakeupTxTrigger 0x200 +/* + * Flag status + */ +#define Rx_over 0x01 +#define Xoff_state 0x02 +#define Tx_flowOff 0x04 +#define Tx_enable 0x08 +#define CTS_state 0x10 +#define DSR_state 0x20 +#define DCD_state 0x80 +/* + * FlowControl + */ +#define CTS_FlowCtl 1 +#define RTS_FlowCtl 2 +#define Tx_FlowCtl 4 +#define Rx_FlowCtl 8 +#define IXM_IXANY 0x10 + +#define LowWater 128 + +#define DTR_ON 1 +#define RTS_ON 2 +#define CTS_ON 1 +#define DSR_ON 2 +#define DCD_ON 8 + +/* mode definition */ +#define MX_CS8 0x03 +#define MX_CS7 0x02 +#define MX_CS6 0x01 +#define MX_CS5 0x00 + +#define MX_STOP1 0x00 +#define MX_STOP15 0x04 +#define MX_STOP2 0x08 + +#define MX_PARNONE 0x00 +#define MX_PAREVEN 0x40 +#define MX_PARODD 0xC0 + +#endif diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c new file mode 100644 index 000000000000..d188f378684d --- /dev/null +++ b/drivers/tty/mxser.c @@ -0,0 +1,2757 @@ +/* + * mxser.c -- MOXA Smartio/Industio family multiport serial driver. + * + * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com). + * Copyright (C) 2006-2008 Jiri Slaby + * + * This code is loosely based on the 1.8 moxa driver which is based on + * Linux serial driver, written by Linus Torvalds, Theodore T'so and + * others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox + * . The original 1.8 code is available on + * www.moxa.com. + * - Fixed x86_64 cleanness + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "mxser.h" + +#define MXSER_VERSION "2.0.5" /* 1.14 */ +#define MXSERMAJOR 174 + +#define MXSER_BOARDS 4 /* Max. boards */ +#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ +#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) +#define MXSER_ISR_PASS_LIMIT 100 + +/*CheckIsMoxaMust return value*/ +#define MOXA_OTHER_UART 0x00 +#define MOXA_MUST_MU150_HWID 0x01 +#define MOXA_MUST_MU860_HWID 0x02 + +#define WAKEUP_CHARS 256 + +#define UART_MCR_AFE 0x20 +#define UART_LSR_SPECIAL 0x1E + +#define PCI_DEVICE_ID_POS104UL 0x1044 +#define PCI_DEVICE_ID_CB108 0x1080 +#define PCI_DEVICE_ID_CP102UF 0x1023 +#define PCI_DEVICE_ID_CP112UL 0x1120 +#define PCI_DEVICE_ID_CB114 0x1142 +#define PCI_DEVICE_ID_CP114UL 0x1143 +#define PCI_DEVICE_ID_CB134I 0x1341 +#define PCI_DEVICE_ID_CP138U 0x1380 + + +#define C168_ASIC_ID 1 +#define C104_ASIC_ID 2 +#define C102_ASIC_ID 0xB +#define CI132_ASIC_ID 4 +#define CI134_ASIC_ID 3 +#define CI104J_ASIC_ID 5 + +#define MXSER_HIGHBAUD 1 +#define MXSER_HAS2 2 + +/* This is only for PCI */ +static const struct { + int type; + int tx_fifo; + int rx_fifo; + int xmit_fifo_size; + int rx_high_water; + int rx_trigger; + int rx_low_water; + long max_baud; +} Gpci_uart_info[] = { + {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L}, + {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L}, + {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L} +}; +#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) + +struct mxser_cardinfo { + char *name; + unsigned int nports; + unsigned int flags; +}; + +static const struct mxser_cardinfo mxser_cards[] = { +/* 0*/ { "C168 series", 8, }, + { "C104 series", 4, }, + { "CI-104J series", 4, }, + { "C168H/PCI series", 8, }, + { "C104H/PCI series", 4, }, +/* 5*/ { "C102 series", 4, MXSER_HAS2 }, /* C102-ISA */ + { "CI-132 series", 4, MXSER_HAS2 }, + { "CI-134 series", 4, }, + { "CP-132 series", 2, }, + { "CP-114 series", 4, }, +/*10*/ { "CT-114 series", 4, }, + { "CP-102 series", 2, MXSER_HIGHBAUD }, + { "CP-104U series", 4, }, + { "CP-168U series", 8, }, + { "CP-132U series", 2, }, +/*15*/ { "CP-134U series", 4, }, + { "CP-104JU series", 4, }, + { "Moxa UC7000 Serial", 8, }, /* RC7000 */ + { "CP-118U series", 8, }, + { "CP-102UL series", 2, }, +/*20*/ { "CP-102U series", 2, }, + { "CP-118EL series", 8, }, + { "CP-168EL series", 8, }, + { "CP-104EL series", 4, }, + { "CB-108 series", 8, }, +/*25*/ { "CB-114 series", 4, }, + { "CB-134I series", 4, }, + { "CP-138U series", 8, }, + { "POS-104UL series", 4, }, + { "CP-114UL series", 4, }, +/*30*/ { "CP-102UF series", 2, }, + { "CP-112UL series", 2, }, +}; + +/* driver_data correspond to the lines in the structure above + see also ISA probe function before you change something */ +static struct pci_device_id mxser_pcibrds[] = { + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 9 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 10 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 11 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 24 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 25 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 26 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 30 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 31 }, + { } +}; +MODULE_DEVICE_TABLE(pci, mxser_pcibrds); + +static unsigned long ioaddr[MXSER_BOARDS]; +static int ttymajor = MXSERMAJOR; + +/* Variables for insmod */ + +MODULE_AUTHOR("Casper Yang"); +MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); +module_param_array(ioaddr, ulong, NULL, 0); +MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board"); +module_param(ttymajor, int, 0); +MODULE_LICENSE("GPL"); + +struct mxser_log { + int tick; + unsigned long rxcnt[MXSER_PORTS]; + unsigned long txcnt[MXSER_PORTS]; +}; + +struct mxser_mon { + unsigned long rxcnt; + unsigned long txcnt; + unsigned long up_rxcnt; + unsigned long up_txcnt; + int modem_status; + unsigned char hold_reason; +}; + +struct mxser_mon_ext { + unsigned long rx_cnt[32]; + unsigned long tx_cnt[32]; + unsigned long up_rxcnt[32]; + unsigned long up_txcnt[32]; + int modem_status[32]; + + long baudrate[32]; + int databits[32]; + int stopbits[32]; + int parity[32]; + int flowctrl[32]; + int fifo[32]; + int iftype[32]; +}; + +struct mxser_board; + +struct mxser_port { + struct tty_port port; + struct mxser_board *board; + + unsigned long ioaddr; + unsigned long opmode_ioaddr; + int max_baud; + + int rx_high_water; + int rx_trigger; /* Rx fifo trigger level */ + int rx_low_water; + int baud_base; /* max. speed */ + int type; /* UART type */ + + int x_char; /* xon/xoff character */ + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + + unsigned char stop_rx; + unsigned char ldisc_stop_rx; + + int custom_divisor; + unsigned char err_shadow; + + struct async_icount icount; /* kernel counters for 4 input interrupts */ + int timeout; + + int read_status_mask; + int ignore_status_mask; + int xmit_fifo_size; + int xmit_head; + int xmit_tail; + int xmit_cnt; + + struct ktermios normal_termios; + + struct mxser_mon mon_data; + + spinlock_t slock; +}; + +struct mxser_board { + unsigned int idx; + int irq; + const struct mxser_cardinfo *info; + unsigned long vector; + unsigned long vector_mask; + + int chip_flag; + int uart_type; + + struct mxser_port ports[MXSER_PORTS_PER_BOARD]; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_board mxser_boards[MXSER_BOARDS]; +static struct tty_driver *mxvar_sdriver; +static struct mxser_log mxvar_log; +static int mxser_set_baud_method[MXSER_PORTS + 1]; + +static void mxser_enable_must_enchance_mode(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr |= MOXA_MUST_EFR_EFRB_ENABLE; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +#ifdef CONFIG_PCI +static void mxser_disable_must_enchance_mode(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} +#endif + +static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK0; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_XON1_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK0; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_XOFF1_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_set_must_fifo_value(struct mxser_port *info) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(info->ioaddr + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR); + + efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK1; + + outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER); + outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER); + outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER); + outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER); + outb(oldlcr, info->ioaddr + UART_LCR); +} + +static void mxser_set_must_enum_value(unsigned long baseio, u8 value) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK2; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(value, baseio + MOXA_MUST_ENUM_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +#ifdef CONFIG_PCI +static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_BANK_MASK; + efr |= MOXA_MUST_EFR_BANK2; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + *pId = inb(baseio + MOXA_MUST_HWID_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} +#endif + +static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_enable_must_tx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_TX_MASK; + efr |= MOXA_MUST_EFR_SF_TX1; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_disable_must_tx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_TX_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_enable_must_rx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_RX_MASK; + efr |= MOXA_MUST_EFR_SF_RX1; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +static void mxser_disable_must_rx_software_flow_control(unsigned long baseio) +{ + u8 oldlcr; + u8 efr; + + oldlcr = inb(baseio + UART_LCR); + outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR); + + efr = inb(baseio + MOXA_MUST_EFR_REGISTER); + efr &= ~MOXA_MUST_EFR_SF_RX_MASK; + + outb(efr, baseio + MOXA_MUST_EFR_REGISTER); + outb(oldlcr, baseio + UART_LCR); +} + +#ifdef CONFIG_PCI +static int __devinit CheckIsMoxaMust(unsigned long io) +{ + u8 oldmcr, hwid; + int i; + + outb(0, io + UART_LCR); + mxser_disable_must_enchance_mode(io); + oldmcr = inb(io + UART_MCR); + outb(0, io + UART_MCR); + mxser_set_must_xon1_value(io, 0x11); + if ((hwid = inb(io + UART_MCR)) != 0) { + outb(oldmcr, io + UART_MCR); + return MOXA_OTHER_UART; + } + + mxser_get_must_hardware_id(io, &hwid); + for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ + if (hwid == Gpci_uart_info[i].type) + return (int)hwid; + } + return MOXA_OTHER_UART; +} +#endif + +static void process_txrx_fifo(struct mxser_port *info) +{ + int i; + + if ((info->type == PORT_16450) || (info->type == PORT_8250)) { + info->rx_trigger = 1; + info->rx_high_water = 1; + info->rx_low_water = 1; + info->xmit_fifo_size = 1; + } else + for (i = 0; i < UART_INFO_NUM; i++) + if (info->board->chip_flag == Gpci_uart_info[i].type) { + info->rx_trigger = Gpci_uart_info[i].rx_trigger; + info->rx_low_water = Gpci_uart_info[i].rx_low_water; + info->rx_high_water = Gpci_uart_info[i].rx_high_water; + info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size; + break; + } +} + +static unsigned char mxser_get_msr(int baseaddr, int mode, int port) +{ + static unsigned char mxser_msr[MXSER_PORTS + 1]; + unsigned char status = 0; + + status = inb(baseaddr + UART_MSR); + + mxser_msr[port] &= 0x0F; + mxser_msr[port] |= status; + status = mxser_msr[port]; + if (mode) + mxser_msr[port] = 0; + + return status; +} + +static int mxser_carrier_raised(struct tty_port *port) +{ + struct mxser_port *mp = container_of(port, struct mxser_port, port); + return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; +} + +static void mxser_dtr_rts(struct tty_port *port, int on) +{ + struct mxser_port *mp = container_of(port, struct mxser_port, port); + unsigned long flags; + + spin_lock_irqsave(&mp->slock, flags); + if (on) + outb(inb(mp->ioaddr + UART_MCR) | + UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR); + else + outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS), + mp->ioaddr + UART_MCR); + spin_unlock_irqrestore(&mp->slock, flags); +} + +static int mxser_set_baud(struct tty_struct *tty, long newspd) +{ + struct mxser_port *info = tty->driver_data; + int quot = 0, baud; + unsigned char cval; + + if (!info->ioaddr) + return -1; + + if (newspd > info->max_baud) + return -1; + + if (newspd == 134) { + quot = 2 * info->baud_base / 269; + tty_encode_baud_rate(tty, 134, 134); + } else if (newspd) { + quot = info->baud_base / newspd; + if (quot == 0) + quot = 1; + baud = info->baud_base/quot; + tty_encode_baud_rate(tty, baud, baud); + } else { + quot = 0; + } + + info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); + info->timeout += HZ / 50; /* Add .02 seconds of slop */ + + if (quot) { + info->MCR |= UART_MCR_DTR; + outb(info->MCR, info->ioaddr + UART_MCR); + } else { + info->MCR &= ~UART_MCR_DTR; + outb(info->MCR, info->ioaddr + UART_MCR); + return 0; + } + + cval = inb(info->ioaddr + UART_LCR); + + outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ + + outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ + outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ + outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ + +#ifdef BOTHER + if (C_BAUD(tty) == BOTHER) { + quot = info->baud_base % newspd; + quot *= 8; + if (quot % newspd > newspd / 2) { + quot /= newspd; + quot++; + } else + quot /= newspd; + + mxser_set_must_enum_value(info->ioaddr, quot); + } else +#endif + mxser_set_must_enum_value(info->ioaddr, 0); + + return 0; +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static int mxser_change_speed(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct mxser_port *info = tty->driver_data; + unsigned cflag, cval, fcr; + int ret = 0; + unsigned char status; + + cflag = tty->termios->c_cflag; + if (!info->ioaddr) + return ret; + + if (mxser_set_baud_method[tty->index] == 0) + mxser_set_baud(tty, tty_get_baud_rate(tty)); + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + case CS8: + cval = 0x03; + break; + default: + cval = 0x00; + break; /* too keep GCC shut... */ + } + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; + + if ((info->type == PORT_8250) || (info->type == PORT_16450)) { + if (info->board->chip_flag) { + fcr = UART_FCR_ENABLE_FIFO; + fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; + mxser_set_must_fifo_value(info); + } else + fcr = 0; + } else { + fcr = UART_FCR_ENABLE_FIFO; + if (info->board->chip_flag) { + fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; + mxser_set_must_fifo_value(info); + } else { + switch (info->rx_trigger) { + case 1: + fcr |= UART_FCR_TRIGGER_1; + break; + case 4: + fcr |= UART_FCR_TRIGGER_4; + break; + case 8: + fcr |= UART_FCR_TRIGGER_8; + break; + default: + fcr |= UART_FCR_TRIGGER_14; + break; + } + } + } + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + info->MCR &= ~UART_MCR_AFE; + if (cflag & CRTSCTS) { + info->port.flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + if ((info->type == PORT_16550A) || (info->board->chip_flag)) { + info->MCR |= UART_MCR_AFE; + } else { + status = inb(info->ioaddr + UART_MSR); + if (tty->hw_stopped) { + if (status & UART_MSR_CTS) { + tty->hw_stopped = 0; + if (info->type != PORT_16550A && + !info->board->chip_flag) { + outb(info->IER & ~UART_IER_THRI, + info->ioaddr + + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + + UART_IER); + } + tty_wakeup(tty); + } + } else { + if (!(status & UART_MSR_CTS)) { + tty->hw_stopped = 1; + if ((info->type != PORT_16550A) && + (!info->board->chip_flag)) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->ioaddr + + UART_IER); + } + } + } + } + } else { + info->port.flags &= ~ASYNC_CTS_FLOW; + } + outb(info->MCR, info->ioaddr + UART_MCR); + if (cflag & CLOCAL) { + info->port.flags &= ~ASYNC_CHECK_CD; + } else { + info->port.flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + outb(info->IER, info->ioaddr + UART_IER); + + /* + * Set up parity check flag + */ + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + info->read_status_mask |= UART_LSR_BI; + + info->ignore_status_mask = 0; + + if (I_IGNBRK(tty)) { + info->ignore_status_mask |= UART_LSR_BI; + info->read_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(tty)) { + info->ignore_status_mask |= + UART_LSR_OE | + UART_LSR_PE | + UART_LSR_FE; + info->read_status_mask |= + UART_LSR_OE | + UART_LSR_PE | + UART_LSR_FE; + } + } + if (info->board->chip_flag) { + mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); + mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); + if (I_IXON(tty)) { + mxser_enable_must_rx_software_flow_control( + info->ioaddr); + } else { + mxser_disable_must_rx_software_flow_control( + info->ioaddr); + } + if (I_IXOFF(tty)) { + mxser_enable_must_tx_software_flow_control( + info->ioaddr); + } else { + mxser_disable_must_tx_software_flow_control( + info->ioaddr); + } + } + + + outb(fcr, info->ioaddr + UART_FCR); /* set fcr */ + outb(cval, info->ioaddr + UART_LCR); + + return ret; +} + +static void mxser_check_modem_status(struct tty_struct *tty, + struct mxser_port *port, int status) +{ + /* update input line counters */ + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + port->icount.dcd++; + if (status & UART_MSR_DCTS) + port->icount.cts++; + port->mon_data.modem_status = status; + wake_up_interruptible(&port->port.delta_msr_wait); + + if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if (status & UART_MSR_DCD) + wake_up_interruptible(&port->port.open_wait); + } + + if (port->port.flags & ASYNC_CTS_FLOW) { + if (tty->hw_stopped) { + if (status & UART_MSR_CTS) { + tty->hw_stopped = 0; + + if ((port->type != PORT_16550A) && + (!port->board->chip_flag)) { + outb(port->IER & ~UART_IER_THRI, + port->ioaddr + UART_IER); + port->IER |= UART_IER_THRI; + outb(port->IER, port->ioaddr + + UART_IER); + } + tty_wakeup(tty); + } + } else { + if (!(status & UART_MSR_CTS)) { + tty->hw_stopped = 1; + if (port->type != PORT_16550A && + !port->board->chip_flag) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + + UART_IER); + } + } + } + } +} + +static int mxser_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct mxser_port *info = container_of(port, struct mxser_port, port); + unsigned long page; + unsigned long flags; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + spin_lock_irqsave(&info->slock, flags); + + if (!info->ioaddr || !info->type) { + set_bit(TTY_IO_ERROR, &tty->flags); + free_page(page); + spin_unlock_irqrestore(&info->slock, flags); + return 0; + } + info->port.xmit_buf = (unsigned char *) page; + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in mxser_change_speed()) + */ + if (info->board->chip_flag) + outb((UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT | + MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR); + else + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->ioaddr + UART_FCR); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (inb(info->ioaddr + UART_LSR) == 0xff) { + spin_unlock_irqrestore(&info->slock, flags); + if (capable(CAP_SYS_ADMIN)) { + set_bit(TTY_IO_ERROR, &tty->flags); + return 0; + } else + return -ENODEV; + } + + /* + * Clear the interrupt registers. + */ + (void) inb(info->ioaddr + UART_LSR); + (void) inb(info->ioaddr + UART_RX); + (void) inb(info->ioaddr + UART_IIR); + (void) inb(info->ioaddr + UART_MSR); + + /* + * Now, initialize the UART + */ + outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + outb(info->MCR, info->ioaddr + UART_MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + + if (info->board->chip_flag) + info->IER |= MOXA_MUST_IER_EGDAI; + outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void) inb(info->ioaddr + UART_LSR); + (void) inb(info->ioaddr + UART_RX); + (void) inb(info->ioaddr + UART_IIR); + (void) inb(info->ioaddr + UART_MSR); + + clear_bit(TTY_IO_ERROR, &tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ + mxser_change_speed(tty, NULL); + spin_unlock_irqrestore(&info->slock, flags); + + return 0; +} + +/* + * This routine will shutdown a serial port + */ +static void mxser_shutdown_port(struct tty_port *port) +{ + struct mxser_port *info = container_of(port, struct mxser_port, port); + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->port.delta_msr_wait); + + /* + * Free the xmit buffer, if necessary + */ + if (info->port.xmit_buf) { + free_page((unsigned long) info->port.xmit_buf); + info->port.xmit_buf = NULL; + } + + info->IER = 0; + outb(0x00, info->ioaddr + UART_IER); + + /* clear Rx/Tx FIFO's */ + if (info->board->chip_flag) + outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | + MOXA_MUST_FCR_GDA_MODE_ENABLE, + info->ioaddr + UART_FCR); + else + outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + info->ioaddr + UART_FCR); + + /* read data port to reset things */ + (void) inb(info->ioaddr + UART_RX); + + + if (info->board->chip_flag) + SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr); + + spin_unlock_irqrestore(&info->slock, flags); +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int mxser_open(struct tty_struct *tty, struct file *filp) +{ + struct mxser_port *info; + int line; + + line = tty->index; + if (line == MXSER_PORTS) + return 0; + if (line < 0 || line > MXSER_PORTS) + return -ENODEV; + info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD]; + if (!info->ioaddr) + return -ENODEV; + + tty->driver_data = info; + return tty_port_open(&info->port, tty, filp); +} + +static void mxser_flush_buffer(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + char fcr; + unsigned long flags; + + + spin_lock_irqsave(&info->slock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + fcr = inb(info->ioaddr + UART_FCR); + outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->ioaddr + UART_FCR); + outb(fcr, info->ioaddr + UART_FCR); + + spin_unlock_irqrestore(&info->slock, flags); + + tty_wakeup(tty); +} + + +static void mxser_close_port(struct tty_port *port) +{ + struct mxser_port *info = container_of(port, struct mxser_port, port); + unsigned long timeout; + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + if (info->board->chip_flag) + info->IER &= ~MOXA_MUST_RECV_ISR; + + outb(info->IER, info->ioaddr + UART_IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies + HZ; + while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) { + schedule_timeout_interruptible(5); + if (time_after(jiffies, timeout)) + break; + } +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; + + if (tty->index == MXSER_PORTS || info == NULL) + return; + if (tty_port_close_start(port, tty, filp) == 0) + return; + mutex_lock(&port->mutex); + mxser_close_port(port); + mxser_flush_buffer(tty); + mxser_shutdown_port(port); + clear_bit(ASYNCB_INITIALIZED, &port->flags); + mutex_unlock(&port->mutex); + /* Right now the tty_port set is done outside of the close_end helper + as we don't yet have everyone using refcounts */ + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); +} + +static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + int c, total = 0; + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + if (!info->port.xmit_buf) + return 0; + + while (1) { + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + memcpy(info->port.xmit_buf + info->xmit_head, buf, c); + spin_lock_irqsave(&info->slock, flags); + info->xmit_head = (info->xmit_head + c) & + (SERIAL_XMIT_SIZE - 1); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->slock, flags); + + buf += c; + count -= c; + total += c; + } + + if (info->xmit_cnt && !tty->stopped) { + if (!tty->hw_stopped || + (info->type == PORT_16550A) || + (info->board->chip_flag)) { + spin_lock_irqsave(&info->slock, flags); + outb(info->IER & ~UART_IER_THRI, info->ioaddr + + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + spin_unlock_irqrestore(&info->slock, flags); + } + } + return total; +} + +static int mxser_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + if (!info->port.xmit_buf) + return 0; + + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) + return 0; + + spin_lock_irqsave(&info->slock, flags); + info->port.xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + spin_unlock_irqrestore(&info->slock, flags); + if (!tty->stopped) { + if (!tty->hw_stopped || + (info->type == PORT_16550A) || + info->board->chip_flag) { + spin_lock_irqsave(&info->slock, flags); + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + spin_unlock_irqrestore(&info->slock, flags); + } + } + return 1; +} + + +static void mxser_flush_chars(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf || + (tty->hw_stopped && info->type != PORT_16550A && + !info->board->chip_flag)) + return; + + spin_lock_irqsave(&info->slock, flags); + + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + + spin_unlock_irqrestore(&info->slock, flags); +} + +static int mxser_write_room(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + int ret; + + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + return ret < 0 ? 0 : ret; +} + +static int mxser_chars_in_buffer(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + return info->xmit_cnt; +} + +/* + * ------------------------------------------------------------ + * friends of mxser_ioctl() + * ------------------------------------------------------------ + */ +static int mxser_get_serial_info(struct tty_struct *tty, + struct serial_struct __user *retinfo) +{ + struct mxser_port *info = tty->driver_data; + struct serial_struct tmp = { + .type = info->type, + .line = tty->index, + .port = info->ioaddr, + .irq = info->board->irq, + .flags = info->port.flags, + .baud_base = info->baud_base, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, + .custom_divisor = info->custom_divisor, + .hub6 = 0 + }; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int mxser_set_serial_info(struct tty_struct *tty, + struct serial_struct __user *new_info) +{ + struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; + struct serial_struct new_serial; + speed_t baud; + unsigned long sl_flags; + unsigned int flags; + int retval = 0; + + if (!new_info || !info->ioaddr) + return -ENODEV; + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + if (new_serial.irq != info->board->irq || + new_serial.port != info->ioaddr) + return -EINVAL; + + flags = port->flags & ASYNC_SPD_MASK; + + if (!capable(CAP_SYS_ADMIN)) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.close_delay != info->port.close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) + return -EPERM; + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + } else { + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + port->close_delay = new_serial.close_delay * HZ / 100; + port->closing_wait = new_serial.closing_wait * HZ / 100; + tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && + (new_serial.baud_base != info->baud_base || + new_serial.custom_divisor != + info->custom_divisor)) { + if (new_serial.custom_divisor == 0) + return -EINVAL; + baud = new_serial.baud_base / new_serial.custom_divisor; + tty_encode_baud_rate(tty, baud, baud); + } + } + + info->type = new_serial.type; + + process_txrx_fifo(info); + + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + if (flags != (port->flags & ASYNC_SPD_MASK)) { + spin_lock_irqsave(&info->slock, sl_flags); + mxser_change_speed(tty, NULL); + spin_unlock_irqrestore(&info->slock, sl_flags); + } + } else { + retval = mxser_activate(port, tty); + if (retval == 0) + set_bit(ASYNCB_INITIALIZED, &port->flags); + } + return retval; +} + +/* + * mxser_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 mxser_get_lsr_info(struct mxser_port *info, + unsigned int __user *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + status = inb(info->ioaddr + UART_LSR); + spin_unlock_irqrestore(&info->slock, flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result, value); +} + +static int mxser_tiocmget(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + unsigned char control, status; + unsigned long flags; + + + if (tty->index == MXSER_PORTS) + return -ENOIOCTLCMD; + if (test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + + control = info->MCR; + + spin_lock_irqsave(&info->slock, flags); + status = inb(info->ioaddr + UART_MSR); + if (status & UART_MSR_ANY_DELTA) + mxser_check_modem_status(tty, info, status); + spin_unlock_irqrestore(&info->slock, flags); + return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | + ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | + ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | + ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | + ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | + ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); +} + +static int mxser_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + + if (tty->index == MXSER_PORTS) + return -ENOIOCTLCMD; + if (test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + + spin_lock_irqsave(&info->slock, flags); + + if (set & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (set & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; + + if (clear & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (clear & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; + + outb(info->MCR, info->ioaddr + UART_MCR); + spin_unlock_irqrestore(&info->slock, flags); + return 0; +} + +static int __init mxser_program_mode(int port) +{ + int id, i, j, n; + + outb(0, port); + outb(0, port); + outb(0, port); + (void)inb(port); + (void)inb(port); + outb(0, port); + (void)inb(port); + + id = inb(port + 1) & 0x1F; + if ((id != C168_ASIC_ID) && + (id != C104_ASIC_ID) && + (id != C102_ASIC_ID) && + (id != CI132_ASIC_ID) && + (id != CI134_ASIC_ID) && + (id != CI104J_ASIC_ID)) + return -1; + for (i = 0, j = 0; i < 4; i++) { + n = inb(port + 2); + if (n == 'M') { + j = 1; + } else if ((j == 1) && (n == 1)) { + j = 2; + break; + } else + j = 0; + } + if (j != 2) + id = -2; + return id; +} + +static void __init mxser_normal_mode(int port) +{ + int i, n; + + outb(0xA5, port + 1); + outb(0x80, port + 3); + outb(12, port + 0); /* 9600 bps */ + outb(0, port + 1); + outb(0x03, port + 3); /* 8 data bits */ + outb(0x13, port + 4); /* loop back mode */ + for (i = 0; i < 16; i++) { + n = inb(port + 5); + if ((n & 0x61) == 0x60) + break; + if ((n & 1) == 1) + (void)inb(port); + } + outb(0x00, port + 4); +} + +#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ +#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ +#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ +#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ +#define EN_CCMD 0x000 /* Chip's command register */ +#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ +#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ +#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ +#define EN0_DCFG 0x00E /* Data configuration reg WR */ +#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ +#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ +#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ +static int __init mxser_read_register(int port, unsigned short *regs) +{ + int i, k, value, id; + unsigned int j; + + id = mxser_program_mode(port); + if (id < 0) + return id; + for (i = 0; i < 14; i++) { + k = (i & 0x3F) | 0x180; + for (j = 0x100; j > 0; j >>= 1) { + outb(CHIP_CS, port); + if (k & j) { + outb(CHIP_CS | CHIP_DO, port); + outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ + } else { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ + } + } + (void)inb(port); + value = 0; + for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); + if (inb(port) & CHIP_DI) + value |= j; + } + regs[i] = value; + outb(0, port); + } + mxser_normal_mode(port); + return id; +} + +static int mxser_ioctl_special(unsigned int cmd, void __user *argp) +{ + struct mxser_port *ip; + struct tty_port *port; + struct tty_struct *tty; + int result, status; + unsigned int i, j; + int ret = 0; + + switch (cmd) { + case MOXA_GET_MAJOR: + if (printk_ratelimit()) + printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl " + "%x (GET_MAJOR), fix your userspace\n", + current->comm, cmd); + return put_user(ttymajor, (int __user *)argp); + + case MOXA_CHKPORTENABLE: + result = 0; + for (i = 0; i < MXSER_BOARDS; i++) + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) + if (mxser_boards[i].ports[j].ioaddr) + result |= (1 << i); + return put_user(result, (unsigned long __user *)argp); + case MOXA_GETDATACOUNT: + /* The receive side is locked by port->slock but it isn't + clear that an exact snapshot is worth copying here */ + if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) + ret = -EFAULT; + return ret; + case MOXA_GETMSTATUS: { + struct mxser_mstatus ms, __user *msu = argp; + for (i = 0; i < MXSER_BOARDS; i++) + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { + ip = &mxser_boards[i].ports[j]; + port = &ip->port; + memset(&ms, 0, sizeof(ms)); + + mutex_lock(&port->mutex); + if (!ip->ioaddr) + goto copy; + + tty = tty_port_tty_get(port); + + if (!tty || !tty->termios) + ms.cflag = ip->normal_termios.c_cflag; + else + ms.cflag = tty->termios->c_cflag; + tty_kref_put(tty); + spin_lock_irq(&ip->slock); + status = inb(ip->ioaddr + UART_MSR); + spin_unlock_irq(&ip->slock); + if (status & UART_MSR_DCD) + ms.dcd = 1; + if (status & UART_MSR_DSR) + ms.dsr = 1; + if (status & UART_MSR_CTS) + ms.cts = 1; + copy: + mutex_unlock(&port->mutex); + if (copy_to_user(msu, &ms, sizeof(ms))) + return -EFAULT; + msu++; + } + return 0; + } + case MOXA_ASPP_MON_EXT: { + struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */ + unsigned int cflag, iflag, p; + u8 opmode; + + me = kzalloc(sizeof(*me), GFP_KERNEL); + if (!me) + return -ENOMEM; + + for (i = 0, p = 0; i < MXSER_BOARDS; i++) { + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) { + if (p >= ARRAY_SIZE(me->rx_cnt)) { + i = MXSER_BOARDS; + break; + } + ip = &mxser_boards[i].ports[j]; + port = &ip->port; + + mutex_lock(&port->mutex); + if (!ip->ioaddr) { + mutex_unlock(&port->mutex); + continue; + } + + spin_lock_irq(&ip->slock); + status = mxser_get_msr(ip->ioaddr, 0, p); + + if (status & UART_MSR_TERI) + ip->icount.rng++; + if (status & UART_MSR_DDSR) + ip->icount.dsr++; + if (status & UART_MSR_DDCD) + ip->icount.dcd++; + if (status & UART_MSR_DCTS) + ip->icount.cts++; + + ip->mon_data.modem_status = status; + me->rx_cnt[p] = ip->mon_data.rxcnt; + me->tx_cnt[p] = ip->mon_data.txcnt; + me->up_rxcnt[p] = ip->mon_data.up_rxcnt; + me->up_txcnt[p] = ip->mon_data.up_txcnt; + me->modem_status[p] = + ip->mon_data.modem_status; + spin_unlock_irq(&ip->slock); + + tty = tty_port_tty_get(&ip->port); + + if (!tty || !tty->termios) { + cflag = ip->normal_termios.c_cflag; + iflag = ip->normal_termios.c_iflag; + me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios); + } else { + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; + me->baudrate[p] = tty_get_baud_rate(tty); + } + tty_kref_put(tty); + + me->databits[p] = cflag & CSIZE; + me->stopbits[p] = cflag & CSTOPB; + me->parity[p] = cflag & (PARENB | PARODD | + CMSPAR); + + if (cflag & CRTSCTS) + me->flowctrl[p] |= 0x03; + + if (iflag & (IXON | IXOFF)) + me->flowctrl[p] |= 0x0C; + + if (ip->type == PORT_16550A) + me->fifo[p] = 1; + + opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); + opmode &= OP_MODE_MASK; + me->iftype[p] = opmode; + mutex_unlock(&port->mutex); + } + } + if (copy_to_user(argp, me, sizeof(*me))) + ret = -EFAULT; + kfree(me); + return ret; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, + struct async_icount *cprev) +{ + struct async_icount cnow; + unsigned long flags; + int ret; + + spin_lock_irqsave(&info->slock, flags); + cnow = info->icount; /* atomic copy */ + spin_unlock_irqrestore(&info->slock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} + +static int mxser_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; + struct async_icount cnow; + unsigned long flags; + void __user *argp = (void __user *)arg; + int retval; + + if (tty->index == MXSER_PORTS) + return mxser_ioctl_special(cmd, argp); + + if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) { + int p; + unsigned long opmode; + static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f }; + int shiftbit; + unsigned char val, mask; + + p = tty->index % 4; + if (cmd == MOXA_SET_OP_MODE) { + if (get_user(opmode, (int __user *) argp)) + return -EFAULT; + if (opmode != RS232_MODE && + opmode != RS485_2WIRE_MODE && + opmode != RS422_MODE && + opmode != RS485_4WIRE_MODE) + return -EFAULT; + mask = ModeMask[p]; + shiftbit = p * 2; + spin_lock_irq(&info->slock); + val = inb(info->opmode_ioaddr); + val &= mask; + val |= (opmode << shiftbit); + outb(val, info->opmode_ioaddr); + spin_unlock_irq(&info->slock); + } else { + shiftbit = p * 2; + spin_lock_irq(&info->slock); + opmode = inb(info->opmode_ioaddr) >> shiftbit; + spin_unlock_irq(&info->slock); + opmode &= OP_MODE_MASK; + if (put_user(opmode, (int __user *)argp)) + return -EFAULT; + } + return 0; + } + + if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && + test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + + switch (cmd) { + case TIOCGSERIAL: + mutex_lock(&port->mutex); + retval = mxser_get_serial_info(tty, argp); + mutex_unlock(&port->mutex); + return retval; + case TIOCSSERIAL: + mutex_lock(&port->mutex); + retval = mxser_set_serial_info(tty, argp); + mutex_unlock(&port->mutex); + return retval; + case TIOCSERGETLSR: /* Get line status register */ + return mxser_get_lsr_info(info, argp); + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + spin_lock_irqsave(&info->slock, flags); + cnow = info->icount; /* note the counters on entry */ + spin_unlock_irqrestore(&info->slock, flags); + + return wait_event_interruptible(info->port.delta_msr_wait, + mxser_cflags_changed(info, arg, &cnow)); + case MOXA_HighSpeedOn: + return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); + case MOXA_SDS_RSTICOUNTER: + spin_lock_irq(&info->slock); + info->mon_data.rxcnt = 0; + info->mon_data.txcnt = 0; + spin_unlock_irq(&info->slock); + return 0; + + case MOXA_ASPP_OQUEUE:{ + int len, lsr; + + len = mxser_chars_in_buffer(tty); + spin_lock_irq(&info->slock); + lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE; + spin_unlock_irq(&info->slock); + len += (lsr ? 0 : 1); + + return put_user(len, (int __user *)argp); + } + case MOXA_ASPP_MON: { + int mcr, status; + + spin_lock_irq(&info->slock); + status = mxser_get_msr(info->ioaddr, 1, tty->index); + mxser_check_modem_status(tty, info, status); + + mcr = inb(info->ioaddr + UART_MCR); + spin_unlock_irq(&info->slock); + + if (mcr & MOXA_MUST_MCR_XON_FLAG) + info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; + else + info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD; + + if (mcr & MOXA_MUST_MCR_TX_XON) + info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT; + else + info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; + + if (tty->hw_stopped) + info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; + else + info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; + + if (copy_to_user(argp, &info->mon_data, + sizeof(struct mxser_mon))) + return -EFAULT; + + return 0; + } + case MOXA_ASPP_LSTATUS: { + if (put_user(info->err_shadow, (unsigned char __user *)argp)) + return -EFAULT; + + info->err_shadow = 0; + return 0; + } + case MOXA_SET_BAUD_METHOD: { + int method; + + if (get_user(method, (int __user *)argp)) + return -EFAULT; + mxser_set_baud_method[tty->index] = method; + return put_user(method, (int __user *)argp); + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + +static int mxser_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) + +{ + struct mxser_port *info = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->slock, flags); + + icount->frame = cnow.frame; + icount->brk = cnow.brk; + icount->overrun = cnow.overrun; + icount->buf_overrun = cnow.buf_overrun; + icount->parity = cnow.parity; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + return 0; +} + +static void mxser_stoprx(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + + info->ldisc_stop_rx = 1; + if (I_IXOFF(tty)) { + if (info->board->chip_flag) { + info->IER &= ~MOXA_MUST_RECV_ISR; + outb(info->IER, info->ioaddr + UART_IER); + } else { + info->x_char = STOP_CHAR(tty); + outb(0, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + } + + if (tty->termios->c_cflag & CRTSCTS) { + info->MCR &= ~UART_MCR_RTS; + outb(info->MCR, info->ioaddr + UART_MCR); + } +} + +/* + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + */ +static void mxser_throttle(struct tty_struct *tty) +{ + mxser_stoprx(tty); +} + +static void mxser_unthrottle(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + + /* startrx */ + info->ldisc_stop_rx = 0; + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else { + if (info->board->chip_flag) { + info->IER |= MOXA_MUST_RECV_ISR; + outb(info->IER, info->ioaddr + UART_IER); + } else { + info->x_char = START_CHAR(tty); + outb(0, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + } + } + + if (tty->termios->c_cflag & CRTSCTS) { + info->MCR |= UART_MCR_RTS; + outb(info->MCR, info->ioaddr + UART_MCR); + } +} + +/* + * mxser_stop() and mxser_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + */ +static void mxser_stop(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + spin_unlock_irqrestore(&info->slock, flags); +} + +static void mxser_start(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + if (info->xmit_cnt && info->port.xmit_buf) { + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + spin_unlock_irqrestore(&info->slock, flags); +} + +static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + mxser_change_speed(tty, old_termios); + spin_unlock_irqrestore(&info->slock, flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mxser_start(tty); + } + + /* Handle sw stopped */ + if ((old_termios->c_iflag & IXON) && + !(tty->termios->c_iflag & IXON)) { + tty->stopped = 0; + + if (info->board->chip_flag) { + spin_lock_irqsave(&info->slock, flags); + mxser_disable_must_rx_software_flow_control( + info->ioaddr); + spin_unlock_irqrestore(&info->slock, flags); + } + + mxser_start(tty); + } +} + +/* + * mxser_wait_until_sent() --- wait until the transmitter is empty + */ +static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct mxser_port *info = tty->driver_data; + unsigned long orig_jiffies, char_time; + unsigned long flags; + int lsr; + + if (info->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + 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. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2 * info->timeout) + timeout = 2 * info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...", + timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + spin_lock_irqsave(&info->slock, flags); + while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + spin_unlock_irqrestore(&info->slock, flags); + schedule_timeout_interruptible(char_time); + spin_lock_irqsave(&info->slock, flags); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + spin_unlock_irqrestore(&info->slock, flags); + set_current_state(TASK_RUNNING); + +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * This routine is called by tty_hangup() when a hangup is signaled. + */ +static void mxser_hangup(struct tty_struct *tty) +{ + struct mxser_port *info = tty->driver_data; + + mxser_flush_buffer(tty); + tty_port_hangup(&info->port); +} + +/* + * mxser_rs_break() --- routine which turns the break handling on or off + */ +static int mxser_rs_break(struct tty_struct *tty, int break_state) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + if (break_state == -1) + outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, + info->ioaddr + UART_LCR); + else + outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, + info->ioaddr + UART_LCR); + spin_unlock_irqrestore(&info->slock, flags); + return 0; +} + +static void mxser_receive_chars(struct tty_struct *tty, + struct mxser_port *port, int *status) +{ + unsigned char ch, gdl; + int ignored = 0; + int cnt = 0; + int recv_room; + int max = 256; + + recv_room = tty->receive_room; + if (recv_room == 0 && !port->ldisc_stop_rx) + mxser_stoprx(tty); + if (port->board->chip_flag != MOXA_OTHER_UART) { + + if (*status & UART_LSR_SPECIAL) + goto intr_old; + if (port->board->chip_flag == MOXA_MUST_MU860_HWID && + (*status & MOXA_MUST_LSR_RERR)) + goto intr_old; + if (*status & MOXA_MUST_LSR_RERR) + goto intr_old; + + gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); + + if (port->board->chip_flag == MOXA_MUST_MU150_HWID) + gdl &= MOXA_MUST_GDL_MASK; + if (gdl >= recv_room) { + if (!port->ldisc_stop_rx) + mxser_stoprx(tty); + } + while (gdl--) { + ch = inb(port->ioaddr + UART_RX); + tty_insert_flip_char(tty, ch, 0); + cnt++; + } + goto end_intr; + } +intr_old: + + do { + if (max-- < 0) + break; + + ch = inb(port->ioaddr + UART_RX); + if (port->board->chip_flag && (*status & UART_LSR_OE)) + outb(0x23, port->ioaddr + UART_FCR); + *status &= port->read_status_mask; + if (*status & port->ignore_status_mask) { + if (++ignored > 100) + break; + } else { + char flag = 0; + if (*status & UART_LSR_SPECIAL) { + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + port->icount.brk++; + + if (port->port.flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) { + flag = TTY_PARITY; + port->icount.parity++; + } else if (*status & UART_LSR_FE) { + flag = TTY_FRAME; + port->icount.frame++; + } else if (*status & UART_LSR_OE) { + flag = TTY_OVERRUN; + port->icount.overrun++; + } else + flag = TTY_BREAK; + } + tty_insert_flip_char(tty, ch, flag); + cnt++; + if (cnt >= recv_room) { + if (!port->ldisc_stop_rx) + mxser_stoprx(tty); + break; + } + + } + + if (port->board->chip_flag) + break; + + *status = inb(port->ioaddr + UART_LSR); + } while (*status & UART_LSR_DR); + +end_intr: + mxvar_log.rxcnt[tty->index] += cnt; + port->mon_data.rxcnt += cnt; + port->mon_data.up_rxcnt += cnt; + + /* + * We are called from an interrupt context with &port->slock + * being held. Drop it temporarily in order to prevent + * recursive locking. + */ + spin_unlock(&port->slock); + tty_flip_buffer_push(tty); + spin_lock(&port->slock); +} + +static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) +{ + int count, cnt; + + if (port->x_char) { + outb(port->x_char, port->ioaddr + UART_TX); + port->x_char = 0; + mxvar_log.txcnt[tty->index]++; + port->mon_data.txcnt++; + port->mon_data.up_txcnt++; + port->icount.tx++; + return; + } + + if (port->port.xmit_buf == NULL) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || + (tty->hw_stopped && + (port->type != PORT_16550A) && + (!port->board->chip_flag))) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + UART_IER); + return; + } + + cnt = port->xmit_cnt; + count = port->xmit_fifo_size; + do { + outb(port->port.xmit_buf[port->xmit_tail++], + port->ioaddr + UART_TX); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt); + + port->mon_data.txcnt += (cnt - port->xmit_cnt); + port->mon_data.up_txcnt += (cnt - port->xmit_cnt); + port->icount.tx += (cnt - port->xmit_cnt); + + if (port->xmit_cnt < WAKEUP_CHARS) + tty_wakeup(tty); + + if (port->xmit_cnt <= 0) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + UART_IER); + } +} + +/* + * This is the serial driver's generic interrupt routine + */ +static irqreturn_t mxser_interrupt(int irq, void *dev_id) +{ + int status, iir, i; + struct mxser_board *brd = NULL; + struct mxser_port *port; + int max, irqbits, bits, msr; + unsigned int int_cnt, pass_counter = 0; + int handled = IRQ_NONE; + struct tty_struct *tty; + + for (i = 0; i < MXSER_BOARDS; i++) + if (dev_id == &mxser_boards[i]) { + brd = dev_id; + break; + } + + if (i == MXSER_BOARDS) + goto irq_stop; + if (brd == NULL) + goto irq_stop; + max = brd->info->nports; + while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { + irqbits = inb(brd->vector) & brd->vector_mask; + if (irqbits == brd->vector_mask) + break; + + handled = IRQ_HANDLED; + for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { + if (irqbits == brd->vector_mask) + break; + if (bits & irqbits) + continue; + port = &brd->ports[i]; + + int_cnt = 0; + spin_lock(&port->slock); + do { + iir = inb(port->ioaddr + UART_IIR); + if (iir & UART_IIR_NO_INT) + break; + iir &= MOXA_MUST_IIR_MASK; + tty = tty_port_tty_get(&port->port); + if (!tty || + (port->port.flags & ASYNC_CLOSING) || + !(port->port.flags & + ASYNC_INITIALIZED)) { + status = inb(port->ioaddr + UART_LSR); + outb(0x27, port->ioaddr + UART_FCR); + inb(port->ioaddr + UART_MSR); + tty_kref_put(tty); + break; + } + + status = inb(port->ioaddr + UART_LSR); + + if (status & UART_LSR_PE) + port->err_shadow |= NPPI_NOTIFY_PARITY; + if (status & UART_LSR_FE) + port->err_shadow |= NPPI_NOTIFY_FRAMING; + if (status & UART_LSR_OE) + port->err_shadow |= + NPPI_NOTIFY_HW_OVERRUN; + if (status & UART_LSR_BI) + port->err_shadow |= NPPI_NOTIFY_BREAK; + + if (port->board->chip_flag) { + if (iir == MOXA_MUST_IIR_GDA || + iir == MOXA_MUST_IIR_RDA || + iir == MOXA_MUST_IIR_RTO || + iir == MOXA_MUST_IIR_LSR) + mxser_receive_chars(tty, port, + &status); + + } else { + status &= port->read_status_mask; + if (status & UART_LSR_DR) + mxser_receive_chars(tty, port, + &status); + } + msr = inb(port->ioaddr + UART_MSR); + if (msr & UART_MSR_ANY_DELTA) + mxser_check_modem_status(tty, port, msr); + + if (port->board->chip_flag) { + if (iir == 0x02 && (status & + UART_LSR_THRE)) + mxser_transmit_chars(tty, port); + } else { + if (status & UART_LSR_THRE) + mxser_transmit_chars(tty, port); + } + tty_kref_put(tty); + } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); + spin_unlock(&port->slock); + } + } + +irq_stop: + return handled; +} + +static const struct tty_operations mxser_ops = { + .open = mxser_open, + .close = mxser_close, + .write = mxser_write, + .put_char = mxser_put_char, + .flush_chars = mxser_flush_chars, + .write_room = mxser_write_room, + .chars_in_buffer = mxser_chars_in_buffer, + .flush_buffer = mxser_flush_buffer, + .ioctl = mxser_ioctl, + .throttle = mxser_throttle, + .unthrottle = mxser_unthrottle, + .set_termios = mxser_set_termios, + .stop = mxser_stop, + .start = mxser_start, + .hangup = mxser_hangup, + .break_ctl = mxser_rs_break, + .wait_until_sent = mxser_wait_until_sent, + .tiocmget = mxser_tiocmget, + .tiocmset = mxser_tiocmset, + .get_icount = mxser_get_icount, +}; + +struct tty_port_operations mxser_port_ops = { + .carrier_raised = mxser_carrier_raised, + .dtr_rts = mxser_dtr_rts, + .activate = mxser_activate, + .shutdown = mxser_shutdown_port, +}; + +/* + * The MOXA Smartio/Industio serial driver boot-time initialization code! + */ + +static void mxser_release_ISA_res(struct mxser_board *brd) +{ + free_irq(brd->irq, brd); + release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); + release_region(brd->vector, 1); +} + +static int __devinit mxser_initbrd(struct mxser_board *brd, + struct pci_dev *pdev) +{ + struct mxser_port *info; + unsigned int i; + int retval; + + printk(KERN_INFO "mxser: max. baud rate = %d bps\n", + brd->ports[0].max_baud); + + for (i = 0; i < brd->info->nports; i++) { + info = &brd->ports[i]; + tty_port_init(&info->port); + info->port.ops = &mxser_port_ops; + info->board = brd; + info->stop_rx = 0; + info->ldisc_stop_rx = 0; + + /* Enhance mode enabled here */ + if (brd->chip_flag != MOXA_OTHER_UART) + mxser_enable_must_enchance_mode(info->ioaddr); + + info->port.flags = ASYNC_SHARE_IRQ; + info->type = brd->uart_type; + + process_txrx_fifo(info); + + info->custom_divisor = info->baud_base * 16; + info->port.close_delay = 5 * HZ / 10; + info->port.closing_wait = 30 * HZ; + info->normal_termios = mxvar_sdriver->init_termios; + memset(&info->mon_data, 0, sizeof(struct mxser_mon)); + info->err_shadow = 0; + spin_lock_init(&info->slock); + + /* before set INT ISR, disable all int */ + outb(inb(info->ioaddr + UART_IER) & 0xf0, + info->ioaddr + UART_IER); + } + + retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", + brd); + if (retval) + printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " + "conflict with another device.\n", + brd->info->name, brd->irq); + + return retval; +} + +static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) +{ + int id, i, bits; + unsigned short regs[16], irq; + unsigned char scratch, scratch2; + + brd->chip_flag = MOXA_OTHER_UART; + + id = mxser_read_register(cap, regs); + switch (id) { + case C168_ASIC_ID: + brd->info = &mxser_cards[0]; + break; + case C104_ASIC_ID: + brd->info = &mxser_cards[1]; + break; + case CI104J_ASIC_ID: + brd->info = &mxser_cards[2]; + break; + case C102_ASIC_ID: + brd->info = &mxser_cards[5]; + break; + case CI132_ASIC_ID: + brd->info = &mxser_cards[6]; + break; + case CI134_ASIC_ID: + brd->info = &mxser_cards[7]; + break; + default: + return 0; + } + + irq = 0; + /* some ISA cards have 2 ports, but we want to see them as 4-port (why?) + Flag-hack checks if configuration should be read as 2-port here. */ + if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + if (irq != (regs[9] & 0xFF00)) + goto err_irqconflict; + } else if (brd->info->nports == 4) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + irq = irq | (irq >> 8); + if (irq != regs[9]) + goto err_irqconflict; + } else if (brd->info->nports == 8) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + irq = irq | (irq >> 8); + if ((irq != regs[9]) || (irq != regs[10])) + goto err_irqconflict; + } + + if (!irq) { + printk(KERN_ERR "mxser: interrupt number unset\n"); + return -EIO; + } + brd->irq = ((int)(irq & 0xF000) >> 12); + for (i = 0; i < 8; i++) + brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8; + if ((regs[12] & 0x80) == 0) { + printk(KERN_ERR "mxser: invalid interrupt vector\n"); + return -EIO; + } + brd->vector = (int)regs[11]; /* interrupt vector */ + if (id == 1) + brd->vector_mask = 0x00FF; + else + brd->vector_mask = 0x000F; + for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { + if (regs[12] & bits) { + brd->ports[i].baud_base = 921600; + brd->ports[i].max_baud = 921600; + } else { + brd->ports[i].baud_base = 115200; + brd->ports[i].max_baud = 115200; + } + } + scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); + outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); + outb(0, cap + UART_EFR); /* EFR is the same as FCR */ + outb(scratch2, cap + UART_LCR); + outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); + scratch = inb(cap + UART_IIR); + + if (scratch & 0xC0) + brd->uart_type = PORT_16550A; + else + brd->uart_type = PORT_16450; + if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports, + "mxser(IO)")) { + printk(KERN_ERR "mxser: can't request ports I/O region: " + "0x%.8lx-0x%.8lx\n", + brd->ports[0].ioaddr, brd->ports[0].ioaddr + + 8 * brd->info->nports - 1); + return -EIO; + } + if (!request_region(brd->vector, 1, "mxser(vector)")) { + release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); + printk(KERN_ERR "mxser: can't request interrupt vector region: " + "0x%.8lx-0x%.8lx\n", + brd->ports[0].ioaddr, brd->ports[0].ioaddr + + 8 * brd->info->nports - 1); + return -EIO; + } + return brd->info->nports; + +err_irqconflict: + printk(KERN_ERR "mxser: invalid interrupt number\n"); + return -EIO; +} + +static int __devinit mxser_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ +#ifdef CONFIG_PCI + struct mxser_board *brd; + unsigned int i, j; + unsigned long ioaddress; + int retval = -EINVAL; + + for (i = 0; i < MXSER_BOARDS; i++) + if (mxser_boards[i].info == NULL) + break; + + if (i >= MXSER_BOARDS) { + dev_err(&pdev->dev, "too many boards found (maximum %d), board " + "not configured\n", MXSER_BOARDS); + goto err; + } + + brd = &mxser_boards[i]; + brd->idx = i * MXSER_PORTS_PER_BOARD; + dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n", + mxser_cards[ent->driver_data].name, + pdev->bus->number, PCI_SLOT(pdev->devfn)); + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "PCI enable failed\n"); + goto err; + } + + /* io address */ + ioaddress = pci_resource_start(pdev, 2); + retval = pci_request_region(pdev, 2, "mxser(IO)"); + if (retval) + goto err_dis; + + brd->info = &mxser_cards[ent->driver_data]; + for (i = 0; i < brd->info->nports; i++) + brd->ports[i].ioaddr = ioaddress + 8 * i; + + /* vector */ + ioaddress = pci_resource_start(pdev, 3); + retval = pci_request_region(pdev, 3, "mxser(vector)"); + if (retval) + goto err_zero; + brd->vector = ioaddress; + + /* irq */ + brd->irq = pdev->irq; + + brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr); + brd->uart_type = PORT_16550A; + brd->vector_mask = 0; + + for (i = 0; i < brd->info->nports; i++) { + for (j = 0; j < UART_INFO_NUM; j++) { + if (Gpci_uart_info[j].type == brd->chip_flag) { + brd->ports[i].max_baud = + Gpci_uart_info[j].max_baud; + + /* exception....CP-102 */ + if (brd->info->flags & MXSER_HIGHBAUD) + brd->ports[i].max_baud = 921600; + break; + } + } + } + + if (brd->chip_flag == MOXA_MUST_MU860_HWID) { + for (i = 0; i < brd->info->nports; i++) { + if (i < 4) + brd->ports[i].opmode_ioaddr = ioaddress + 4; + else + brd->ports[i].opmode_ioaddr = ioaddress + 0x0c; + } + outb(0, ioaddress + 4); /* default set to RS232 mode */ + outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ + } + + for (i = 0; i < brd->info->nports; i++) { + brd->vector_mask |= (1 << i); + brd->ports[i].baud_base = 921600; + } + + /* mxser_initbrd will hook ISR. */ + retval = mxser_initbrd(brd, pdev); + if (retval) + goto err_rel3; + + for (i = 0; i < brd->info->nports; i++) + tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev); + + pci_set_drvdata(pdev, brd); + + return 0; +err_rel3: + pci_release_region(pdev, 3); +err_zero: + brd->info = NULL; + pci_release_region(pdev, 2); +err_dis: + pci_disable_device(pdev); +err: + return retval; +#else + return -ENODEV; +#endif +} + +static void __devexit mxser_remove(struct pci_dev *pdev) +{ +#ifdef CONFIG_PCI + struct mxser_board *brd = pci_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < brd->info->nports; i++) + tty_unregister_device(mxvar_sdriver, brd->idx + i); + + free_irq(pdev->irq, brd); + pci_release_region(pdev, 2); + pci_release_region(pdev, 3); + pci_disable_device(pdev); + brd->info = NULL; +#endif +} + +static struct pci_driver mxser_driver = { + .name = "mxser", + .id_table = mxser_pcibrds, + .probe = mxser_probe, + .remove = __devexit_p(mxser_remove) +}; + +static int __init mxser_module_init(void) +{ + struct mxser_board *brd; + unsigned int b, i, m; + int retval; + + mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); + if (!mxvar_sdriver) + return -ENOMEM; + + printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", + MXSER_VERSION); + + /* Initialize the tty_driver structure */ + mxvar_sdriver->owner = THIS_MODULE; + mxvar_sdriver->magic = TTY_DRIVER_MAGIC; + mxvar_sdriver->name = "ttyMI"; + mxvar_sdriver->major = ttymajor; + mxvar_sdriver->minor_start = 0; + mxvar_sdriver->num = MXSER_PORTS + 1; + mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; + mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; + mxvar_sdriver->init_termios = tty_std_termios; + mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(mxvar_sdriver, &mxser_ops); + + retval = tty_register_driver(mxvar_sdriver); + if (retval) { + printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " + "tty driver !\n"); + goto err_put; + } + + /* Start finding ISA boards here */ + for (m = 0, b = 0; b < MXSER_BOARDS; b++) { + if (!ioaddr[b]) + continue; + + brd = &mxser_boards[m]; + retval = mxser_get_ISA_conf(ioaddr[b], brd); + if (retval <= 0) { + brd->info = NULL; + continue; + } + + printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n", + brd->info->name, ioaddr[b]); + + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(brd, NULL) < 0) { + brd->info = NULL; + continue; + } + + brd->idx = m * MXSER_PORTS_PER_BOARD; + for (i = 0; i < brd->info->nports; i++) + tty_register_device(mxvar_sdriver, brd->idx + i, NULL); + + m++; + } + + retval = pci_register_driver(&mxser_driver); + if (retval) { + printk(KERN_ERR "mxser: can't register pci driver\n"); + if (!m) { + retval = -ENODEV; + goto err_unr; + } /* else: we have some ISA cards under control */ + } + + return 0; +err_unr: + tty_unregister_driver(mxvar_sdriver); +err_put: + put_tty_driver(mxvar_sdriver); + return retval; +} + +static void __exit mxser_module_exit(void) +{ + unsigned int i, j; + + pci_unregister_driver(&mxser_driver); + + for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ + if (mxser_boards[i].info != NULL) + for (j = 0; j < mxser_boards[i].info->nports; j++) + tty_unregister_device(mxvar_sdriver, + mxser_boards[i].idx + j); + tty_unregister_driver(mxvar_sdriver); + put_tty_driver(mxvar_sdriver); + + for (i = 0; i < MXSER_BOARDS; i++) + if (mxser_boards[i].info != NULL) + mxser_release_ISA_res(&mxser_boards[i]); +} + +module_init(mxser_module_init); +module_exit(mxser_module_exit); diff --git a/drivers/tty/mxser.h b/drivers/tty/mxser.h new file mode 100644 index 000000000000..41878a69203d --- /dev/null +++ b/drivers/tty/mxser.h @@ -0,0 +1,150 @@ +#ifndef _MXSER_H +#define _MXSER_H + +/* + * Semi-public control interfaces + */ + +/* + * MOXA ioctls + */ + +#define MOXA 0x400 +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_DIAGNOSE (MOXA + 50) +#define MOXA_CHKPORTENABLE (MOXA + 60) +#define MOXA_HighSpeedOn (MOXA + 61) +#define MOXA_GET_MAJOR (MOXA + 63) +#define MOXA_GETMSTATUS (MOXA + 65) +#define MOXA_SET_OP_MODE (MOXA + 66) +#define MOXA_GET_OP_MODE (MOXA + 67) + +#define RS232_MODE 0 +#define RS485_2WIRE_MODE 1 +#define RS422_MODE 2 +#define RS485_4WIRE_MODE 3 +#define OP_MODE_MASK 3 + +#define MOXA_SDS_RSTICOUNTER (MOXA + 69) +#define MOXA_ASPP_OQUEUE (MOXA + 70) +#define MOXA_ASPP_MON (MOXA + 73) +#define MOXA_ASPP_LSTATUS (MOXA + 74) +#define MOXA_ASPP_MON_EXT (MOXA + 75) +#define MOXA_SET_BAUD_METHOD (MOXA + 76) + +/* --------------------------------------------------- */ + +#define NPPI_NOTIFY_PARITY 0x01 +#define NPPI_NOTIFY_FRAMING 0x02 +#define NPPI_NOTIFY_HW_OVERRUN 0x04 +#define NPPI_NOTIFY_SW_OVERRUN 0x08 +#define NPPI_NOTIFY_BREAK 0x10 + +#define NPPI_NOTIFY_CTSHOLD 0x01 /* Tx hold by CTS low */ +#define NPPI_NOTIFY_DSRHOLD 0x02 /* Tx hold by DSR low */ +#define NPPI_NOTIFY_XOFFHOLD 0x08 /* Tx hold by Xoff received */ +#define NPPI_NOTIFY_XOFFXENT 0x10 /* Xoff Sent */ + +/* follow just for Moxa Must chip define. */ +/* */ +/* when LCR register (offset 0x03) write following value, */ +/* the Must chip will enter enchance mode. And write value */ +/* on EFR (offset 0x02) bit 6,7 to change bank. */ +#define MOXA_MUST_ENTER_ENCHANCE 0xBF + +/* when enhance mode enable, access on general bank register */ +#define MOXA_MUST_GDL_REGISTER 0x07 +#define MOXA_MUST_GDL_MASK 0x7F +#define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 + +#define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ +/* enchance register bank select and enchance mode setting register */ +/* when LCR register equal to 0xBF */ +#define MOXA_MUST_EFR_REGISTER 0x02 +/* enchance mode enable */ +#define MOXA_MUST_EFR_EFRB_ENABLE 0x10 +/* enchance reister bank set 0, 1, 2 */ +#define MOXA_MUST_EFR_BANK0 0x00 +#define MOXA_MUST_EFR_BANK1 0x40 +#define MOXA_MUST_EFR_BANK2 0x80 +#define MOXA_MUST_EFR_BANK3 0xC0 +#define MOXA_MUST_EFR_BANK_MASK 0xC0 + +/* set XON1 value register, when LCR=0xBF and change to bank0 */ +#define MOXA_MUST_XON1_REGISTER 0x04 + +/* set XON2 value register, when LCR=0xBF and change to bank0 */ +#define MOXA_MUST_XON2_REGISTER 0x05 + +/* set XOFF1 value register, when LCR=0xBF and change to bank0 */ +#define MOXA_MUST_XOFF1_REGISTER 0x06 + +/* set XOFF2 value register, when LCR=0xBF and change to bank0 */ +#define MOXA_MUST_XOFF2_REGISTER 0x07 + +#define MOXA_MUST_RBRTL_REGISTER 0x04 +#define MOXA_MUST_RBRTH_REGISTER 0x05 +#define MOXA_MUST_RBRTI_REGISTER 0x06 +#define MOXA_MUST_THRTL_REGISTER 0x07 +#define MOXA_MUST_ENUM_REGISTER 0x04 +#define MOXA_MUST_HWID_REGISTER 0x05 +#define MOXA_MUST_ECR_REGISTER 0x06 +#define MOXA_MUST_CSR_REGISTER 0x07 + +/* good data mode enable */ +#define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 +/* only good data put into RxFIFO */ +#define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 + +/* enable CTS interrupt */ +#define MOXA_MUST_IER_ECTSI 0x80 +/* enable RTS interrupt */ +#define MOXA_MUST_IER_ERTSI 0x40 +/* enable Xon/Xoff interrupt */ +#define MOXA_MUST_IER_XINT 0x20 +/* enable GDA interrupt */ +#define MOXA_MUST_IER_EGDAI 0x10 + +#define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) + +/* GDA interrupt pending */ +#define MOXA_MUST_IIR_GDA 0x1C +#define MOXA_MUST_IIR_RDA 0x04 +#define MOXA_MUST_IIR_RTO 0x0C +#define MOXA_MUST_IIR_LSR 0x06 + +/* recieved Xon/Xoff or specical interrupt pending */ +#define MOXA_MUST_IIR_XSC 0x10 + +/* RTS/CTS change state interrupt pending */ +#define MOXA_MUST_IIR_RTSCTS 0x20 +#define MOXA_MUST_IIR_MASK 0x3E + +#define MOXA_MUST_MCR_XON_FLAG 0x40 +#define MOXA_MUST_MCR_XON_ANY 0x80 +#define MOXA_MUST_MCR_TX_XON 0x08 + +/* software flow control on chip mask value */ +#define MOXA_MUST_EFR_SF_MASK 0x0F +/* send Xon1/Xoff1 */ +#define MOXA_MUST_EFR_SF_TX1 0x08 +/* send Xon2/Xoff2 */ +#define MOXA_MUST_EFR_SF_TX2 0x04 +/* send Xon1,Xon2/Xoff1,Xoff2 */ +#define MOXA_MUST_EFR_SF_TX12 0x0C +/* don't send Xon/Xoff */ +#define MOXA_MUST_EFR_SF_TX_NO 0x00 +/* Tx software flow control mask */ +#define MOXA_MUST_EFR_SF_TX_MASK 0x0C +/* don't receive Xon/Xoff */ +#define MOXA_MUST_EFR_SF_RX_NO 0x00 +/* receive Xon1/Xoff1 */ +#define MOXA_MUST_EFR_SF_RX1 0x02 +/* receive Xon2/Xoff2 */ +#define MOXA_MUST_EFR_SF_RX2 0x01 +/* receive Xon1,Xon2/Xoff1,Xoff2 */ +#define MOXA_MUST_EFR_SF_RX12 0x03 +/* Rx software flow control mask */ +#define MOXA_MUST_EFR_SF_RX_MASK 0x03 + +#endif diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c new file mode 100644 index 000000000000..513ba12064ea --- /dev/null +++ b/drivers/tty/nozomi.c @@ -0,0 +1,1993 @@ +/* + * nozomi.c -- HSDPA driver Broadband Wireless Data Card - Globe Trotter + * + * Written by: Ulf Jakobsson, + * Jan Ã…kerfeldt, + * Stefan Thomasson, + * + * Maintained by: Paul Hardwick (p.hardwick@option.com) + * + * Patches: + * Locking code changes for Vodafone by Sphere Systems Ltd, + * Andrew Bird (ajb@spheresystems.co.uk ) + * & Phil Sanderson + * + * Source has been ported from an implementation made by Filip Aben @ Option + * + * -------------------------------------------------------------------------- + * + * Copyright (c) 2005,2006 Option Wireless Sweden AB + * Copyright (c) 2006 Sphere Systems Ltd + * Copyright (c) 2006 Option Wireless n/v + * All rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * -------------------------------------------------------------------------- + */ + +/* Enable this to have a lot of debug printouts */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \ + __DATE__ " " __TIME__ ")" + +/* Macros definitions */ + +/* Default debug printout level */ +#define NOZOMI_DEBUG_LEVEL 0x00 + +#define P_BUF_SIZE 128 +#define NFO(_err_flag_, args...) \ +do { \ + char tmp[P_BUF_SIZE]; \ + snprintf(tmp, sizeof(tmp), ##args); \ + printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \ + __func__, tmp); \ +} while (0) + +#define DBG1(args...) D_(0x01, ##args) +#define DBG2(args...) D_(0x02, ##args) +#define DBG3(args...) D_(0x04, ##args) +#define DBG4(args...) D_(0x08, ##args) +#define DBG5(args...) D_(0x10, ##args) +#define DBG6(args...) D_(0x20, ##args) +#define DBG7(args...) D_(0x40, ##args) +#define DBG8(args...) D_(0x80, ##args) + +#ifdef DEBUG +/* Do we need this settable at runtime? */ +static int debug = NOZOMI_DEBUG_LEVEL; + +#define D(lvl, args...) do \ + {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \ + while (0) +#define D_(lvl, args...) D(lvl, ##args) + +/* These printouts are always printed */ + +#else +static int debug; +#define D_(lvl, args...) +#endif + +/* TODO: rewrite to optimize macros... */ + +#define TMP_BUF_MAX 256 + +#define DUMP(buf__,len__) \ + do { \ + char tbuf[TMP_BUF_MAX] = {0};\ + if (len__ > 1) {\ + snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\ + if (tbuf[len__-2] == '\r') {\ + tbuf[len__-2] = 'r';\ + } \ + DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\ + } else {\ + DBG1("SENDING: '%s' (%d)", tbuf, len__);\ + } \ +} while (0) + +/* Defines */ +#define NOZOMI_NAME "nozomi" +#define NOZOMI_NAME_TTY "nozomi_tty" +#define DRIVER_DESC "Nozomi driver" + +#define NTTY_TTY_MAXMINORS 256 +#define NTTY_FIFO_BUFFER_SIZE 8192 + +/* Must be power of 2 */ +#define FIFO_BUFFER_SIZE_UL 8192 + +/* Size of tmp send buffer to card */ +#define SEND_BUF_MAX 1024 +#define RECEIVE_BUF_MAX 4 + + +#define R_IIR 0x0000 /* Interrupt Identity Register */ +#define R_FCR 0x0000 /* Flow Control Register */ +#define R_IER 0x0004 /* Interrupt Enable Register */ + +#define CONFIG_MAGIC 0xEFEFFEFE +#define TOGGLE_VALID 0x0000 + +/* Definition of interrupt tokens */ +#define MDM_DL1 0x0001 +#define MDM_UL1 0x0002 +#define MDM_DL2 0x0004 +#define MDM_UL2 0x0008 +#define DIAG_DL1 0x0010 +#define DIAG_DL2 0x0020 +#define DIAG_UL 0x0040 +#define APP1_DL 0x0080 +#define APP1_UL 0x0100 +#define APP2_DL 0x0200 +#define APP2_UL 0x0400 +#define CTRL_DL 0x0800 +#define CTRL_UL 0x1000 +#define RESET 0x8000 + +#define MDM_DL (MDM_DL1 | MDM_DL2) +#define MDM_UL (MDM_UL1 | MDM_UL2) +#define DIAG_DL (DIAG_DL1 | DIAG_DL2) + +/* modem signal definition */ +#define CTRL_DSR 0x0001 +#define CTRL_DCD 0x0002 +#define CTRL_RI 0x0004 +#define CTRL_CTS 0x0008 + +#define CTRL_DTR 0x0001 +#define CTRL_RTS 0x0002 + +#define MAX_PORT 4 +#define NOZOMI_MAX_PORTS 5 +#define NOZOMI_MAX_CARDS (NTTY_TTY_MAXMINORS / MAX_PORT) + +/* Type definitions */ + +/* + * There are two types of nozomi cards, + * one with 2048 memory and with 8192 memory + */ +enum card_type { + F32_2 = 2048, /* 512 bytes downlink + uplink * 2 -> 2048 */ + F32_8 = 8192, /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */ +}; + +/* Initialization states a card can be in */ +enum card_state { + NOZOMI_STATE_UKNOWN = 0, + NOZOMI_STATE_ENABLED = 1, /* pci device enabled */ + NOZOMI_STATE_ALLOCATED = 2, /* config setup done */ + NOZOMI_STATE_READY = 3, /* flowcontrols received */ +}; + +/* Two different toggle channels exist */ +enum channel_type { + CH_A = 0, + CH_B = 1, +}; + +/* Port definition for the card regarding flow control */ +enum ctrl_port_type { + CTRL_CMD = 0, + CTRL_MDM = 1, + CTRL_DIAG = 2, + CTRL_APP1 = 3, + CTRL_APP2 = 4, + CTRL_ERROR = -1, +}; + +/* Ports that the nozomi has */ +enum port_type { + PORT_MDM = 0, + PORT_DIAG = 1, + PORT_APP1 = 2, + PORT_APP2 = 3, + PORT_CTRL = 4, + PORT_ERROR = -1, +}; + +#ifdef __BIG_ENDIAN +/* Big endian */ + +struct toggles { + unsigned int enabled:5; /* + * Toggle fields are valid if enabled is 0, + * else A-channels must always be used. + */ + unsigned int diag_dl:1; + unsigned int mdm_dl:1; + unsigned int mdm_ul:1; +} __attribute__ ((packed)); + +/* Configuration table to read at startup of card */ +/* Is for now only needed during initialization phase */ +struct config_table { + u32 signature; + u16 product_information; + u16 version; + u8 pad3[3]; + struct toggles toggle; + u8 pad1[4]; + u16 dl_mdm_len1; /* + * If this is 64, it can hold + * 60 bytes + 4 that is length field + */ + u16 dl_start; + + u16 dl_diag_len1; + u16 dl_mdm_len2; /* + * If this is 64, it can hold + * 60 bytes + 4 that is length field + */ + u16 dl_app1_len; + + u16 dl_diag_len2; + u16 dl_ctrl_len; + u16 dl_app2_len; + u8 pad2[16]; + u16 ul_mdm_len1; + u16 ul_start; + u16 ul_diag_len; + u16 ul_mdm_len2; + u16 ul_app1_len; + u16 ul_app2_len; + u16 ul_ctrl_len; +} __attribute__ ((packed)); + +/* This stores all control downlink flags */ +struct ctrl_dl { + u8 port; + unsigned int reserved:4; + unsigned int CTS:1; + unsigned int RI:1; + unsigned int DCD:1; + unsigned int DSR:1; +} __attribute__ ((packed)); + +/* This stores all control uplink flags */ +struct ctrl_ul { + u8 port; + unsigned int reserved:6; + unsigned int RTS:1; + unsigned int DTR:1; +} __attribute__ ((packed)); + +#else +/* Little endian */ + +/* This represents the toggle information */ +struct toggles { + unsigned int mdm_ul:1; + unsigned int mdm_dl:1; + unsigned int diag_dl:1; + unsigned int enabled:5; /* + * Toggle fields are valid if enabled is 0, + * else A-channels must always be used. + */ +} __attribute__ ((packed)); + +/* Configuration table to read at startup of card */ +struct config_table { + u32 signature; + u16 version; + u16 product_information; + struct toggles toggle; + u8 pad1[7]; + u16 dl_start; + u16 dl_mdm_len1; /* + * If this is 64, it can hold + * 60 bytes + 4 that is length field + */ + u16 dl_mdm_len2; + u16 dl_diag_len1; + u16 dl_diag_len2; + u16 dl_app1_len; + u16 dl_app2_len; + u16 dl_ctrl_len; + u8 pad2[16]; + u16 ul_start; + u16 ul_mdm_len2; + u16 ul_mdm_len1; + u16 ul_diag_len; + u16 ul_app1_len; + u16 ul_app2_len; + u16 ul_ctrl_len; +} __attribute__ ((packed)); + +/* This stores all control downlink flags */ +struct ctrl_dl { + unsigned int DSR:1; + unsigned int DCD:1; + unsigned int RI:1; + unsigned int CTS:1; + unsigned int reserverd:4; + u8 port; +} __attribute__ ((packed)); + +/* This stores all control uplink flags */ +struct ctrl_ul { + unsigned int DTR:1; + unsigned int RTS:1; + unsigned int reserved:6; + u8 port; +} __attribute__ ((packed)); +#endif + +/* This holds all information that is needed regarding a port */ +struct port { + struct tty_port port; + u8 update_flow_control; + struct ctrl_ul ctrl_ul; + struct ctrl_dl ctrl_dl; + struct kfifo fifo_ul; + void __iomem *dl_addr[2]; + u32 dl_size[2]; + u8 toggle_dl; + void __iomem *ul_addr[2]; + u32 ul_size[2]; + u8 toggle_ul; + u16 token_dl; + + /* mutex to ensure one access patch to this port */ + struct mutex tty_sem; + wait_queue_head_t tty_wait; + struct async_icount tty_icount; + + struct nozomi *dc; +}; + +/* Private data one for each card in the system */ +struct nozomi { + void __iomem *base_addr; + unsigned long flip; + + /* Pointers to registers */ + void __iomem *reg_iir; + void __iomem *reg_fcr; + void __iomem *reg_ier; + + u16 last_ier; + enum card_type card_type; + struct config_table config_table; /* Configuration table */ + struct pci_dev *pdev; + struct port port[NOZOMI_MAX_PORTS]; + u8 *send_buf; + + spinlock_t spin_mutex; /* secures access to registers and tty */ + + unsigned int index_start; + enum card_state state; + u32 open_ttys; +}; + +/* This is a data packet that is read or written to/from card */ +struct buffer { + u32 size; /* size is the length of the data buffer */ + u8 *data; +} __attribute__ ((packed)); + +/* Global variables */ +static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = { + {PCI_DEVICE(0x1931, 0x000c)}, /* Nozomi HSDPA */ + {}, +}; + +MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl); + +static struct nozomi *ndevs[NOZOMI_MAX_CARDS]; +static struct tty_driver *ntty_driver; + +static const struct tty_port_operations noz_tty_port_ops; + +/* + * find card by tty_index + */ +static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty) +{ + return tty ? ndevs[tty->index / MAX_PORT] : NULL; +} + +static inline struct port *get_port_by_tty(const struct tty_struct *tty) +{ + struct nozomi *ndev = get_dc_by_tty(tty); + return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL; +} + +/* + * TODO: + * -Optimize + * -Rewrite cleaner + */ + +static void read_mem32(u32 *buf, const void __iomem *mem_addr_start, + u32 size_bytes) +{ + u32 i = 0; + const u32 __iomem *ptr = mem_addr_start; + u16 *buf16; + + if (unlikely(!ptr || !buf)) + goto out; + + /* shortcut for extremely often used cases */ + switch (size_bytes) { + case 2: /* 2 bytes */ + buf16 = (u16 *) buf; + *buf16 = __le16_to_cpu(readw(ptr)); + goto out; + break; + case 4: /* 4 bytes */ + *(buf) = __le32_to_cpu(readl(ptr)); + goto out; + break; + } + + while (i < size_bytes) { + if (size_bytes - i == 2) { + /* Handle 2 bytes in the end */ + buf16 = (u16 *) buf; + *(buf16) = __le16_to_cpu(readw(ptr)); + i += 2; + } else { + /* Read 4 bytes */ + *(buf) = __le32_to_cpu(readl(ptr)); + i += 4; + } + buf++; + ptr++; + } +out: + return; +} + +/* + * TODO: + * -Optimize + * -Rewrite cleaner + */ +static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf, + u32 size_bytes) +{ + u32 i = 0; + u32 __iomem *ptr = mem_addr_start; + const u16 *buf16; + + if (unlikely(!ptr || !buf)) + return 0; + + /* shortcut for extremely often used cases */ + switch (size_bytes) { + case 2: /* 2 bytes */ + buf16 = (const u16 *)buf; + writew(__cpu_to_le16(*buf16), ptr); + return 2; + break; + case 1: /* + * also needs to write 4 bytes in this case + * so falling through.. + */ + case 4: /* 4 bytes */ + writel(__cpu_to_le32(*buf), ptr); + return 4; + break; + } + + while (i < size_bytes) { + if (size_bytes - i == 2) { + /* 2 bytes */ + buf16 = (const u16 *)buf; + writew(__cpu_to_le16(*buf16), ptr); + i += 2; + } else { + /* 4 bytes */ + writel(__cpu_to_le32(*buf), ptr); + i += 4; + } + buf++; + ptr++; + } + return i; +} + +/* Setup pointers to different channels and also setup buffer sizes. */ +static void setup_memory(struct nozomi *dc) +{ + void __iomem *offset = dc->base_addr + dc->config_table.dl_start; + /* The length reported is including the length field of 4 bytes, + * hence subtract with 4. + */ + const u16 buff_offset = 4; + + /* Modem port dl configuration */ + dc->port[PORT_MDM].dl_addr[CH_A] = offset; + dc->port[PORT_MDM].dl_addr[CH_B] = + (offset += dc->config_table.dl_mdm_len1); + dc->port[PORT_MDM].dl_size[CH_A] = + dc->config_table.dl_mdm_len1 - buff_offset; + dc->port[PORT_MDM].dl_size[CH_B] = + dc->config_table.dl_mdm_len2 - buff_offset; + + /* Diag port dl configuration */ + dc->port[PORT_DIAG].dl_addr[CH_A] = + (offset += dc->config_table.dl_mdm_len2); + dc->port[PORT_DIAG].dl_size[CH_A] = + dc->config_table.dl_diag_len1 - buff_offset; + dc->port[PORT_DIAG].dl_addr[CH_B] = + (offset += dc->config_table.dl_diag_len1); + dc->port[PORT_DIAG].dl_size[CH_B] = + dc->config_table.dl_diag_len2 - buff_offset; + + /* App1 port dl configuration */ + dc->port[PORT_APP1].dl_addr[CH_A] = + (offset += dc->config_table.dl_diag_len2); + dc->port[PORT_APP1].dl_size[CH_A] = + dc->config_table.dl_app1_len - buff_offset; + + /* App2 port dl configuration */ + dc->port[PORT_APP2].dl_addr[CH_A] = + (offset += dc->config_table.dl_app1_len); + dc->port[PORT_APP2].dl_size[CH_A] = + dc->config_table.dl_app2_len - buff_offset; + + /* Ctrl dl configuration */ + dc->port[PORT_CTRL].dl_addr[CH_A] = + (offset += dc->config_table.dl_app2_len); + dc->port[PORT_CTRL].dl_size[CH_A] = + dc->config_table.dl_ctrl_len - buff_offset; + + offset = dc->base_addr + dc->config_table.ul_start; + + /* Modem Port ul configuration */ + dc->port[PORT_MDM].ul_addr[CH_A] = offset; + dc->port[PORT_MDM].ul_size[CH_A] = + dc->config_table.ul_mdm_len1 - buff_offset; + dc->port[PORT_MDM].ul_addr[CH_B] = + (offset += dc->config_table.ul_mdm_len1); + dc->port[PORT_MDM].ul_size[CH_B] = + dc->config_table.ul_mdm_len2 - buff_offset; + + /* Diag port ul configuration */ + dc->port[PORT_DIAG].ul_addr[CH_A] = + (offset += dc->config_table.ul_mdm_len2); + dc->port[PORT_DIAG].ul_size[CH_A] = + dc->config_table.ul_diag_len - buff_offset; + + /* App1 port ul configuration */ + dc->port[PORT_APP1].ul_addr[CH_A] = + (offset += dc->config_table.ul_diag_len); + dc->port[PORT_APP1].ul_size[CH_A] = + dc->config_table.ul_app1_len - buff_offset; + + /* App2 port ul configuration */ + dc->port[PORT_APP2].ul_addr[CH_A] = + (offset += dc->config_table.ul_app1_len); + dc->port[PORT_APP2].ul_size[CH_A] = + dc->config_table.ul_app2_len - buff_offset; + + /* Ctrl ul configuration */ + dc->port[PORT_CTRL].ul_addr[CH_A] = + (offset += dc->config_table.ul_app2_len); + dc->port[PORT_CTRL].ul_size[CH_A] = + dc->config_table.ul_ctrl_len - buff_offset; +} + +/* Dump config table under initalization phase */ +#ifdef DEBUG +static void dump_table(const struct nozomi *dc) +{ + DBG3("signature: 0x%08X", dc->config_table.signature); + DBG3("version: 0x%04X", dc->config_table.version); + DBG3("product_information: 0x%04X", \ + dc->config_table.product_information); + DBG3("toggle enabled: %d", dc->config_table.toggle.enabled); + DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul); + DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl); + DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl); + + DBG3("dl_start: 0x%04X", dc->config_table.dl_start); + DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1, + dc->config_table.dl_mdm_len1); + DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2, + dc->config_table.dl_mdm_len2); + DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1, + dc->config_table.dl_diag_len1); + DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2, + dc->config_table.dl_diag_len2); + DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len, + dc->config_table.dl_app1_len); + DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len, + dc->config_table.dl_app2_len); + DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len, + dc->config_table.dl_ctrl_len); + DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start, + dc->config_table.ul_start); + DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1, + dc->config_table.ul_mdm_len1); + DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2, + dc->config_table.ul_mdm_len2); + DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len, + dc->config_table.ul_diag_len); + DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len, + dc->config_table.ul_app1_len); + DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len, + dc->config_table.ul_app2_len); + DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len, + dc->config_table.ul_ctrl_len); +} +#else +static inline void dump_table(const struct nozomi *dc) { } +#endif + +/* + * Read configuration table from card under intalization phase + * Returns 1 if ok, else 0 + */ +static int nozomi_read_config_table(struct nozomi *dc) +{ + read_mem32((u32 *) &dc->config_table, dc->base_addr + 0, + sizeof(struct config_table)); + + if (dc->config_table.signature != CONFIG_MAGIC) { + dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n", + dc->config_table.signature, CONFIG_MAGIC); + return 0; + } + + if ((dc->config_table.version == 0) + || (dc->config_table.toggle.enabled == TOGGLE_VALID)) { + int i; + DBG1("Second phase, configuring card"); + + setup_memory(dc); + + dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul; + dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl; + dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl; + DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d", + dc->port[PORT_MDM].toggle_ul, + dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl); + + dump_table(dc); + + for (i = PORT_MDM; i < MAX_PORT; i++) { + memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl)); + memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul)); + } + + /* Enable control channel */ + dc->last_ier = dc->last_ier | CTRL_DL; + writew(dc->last_ier, dc->reg_ier); + + dc->state = NOZOMI_STATE_ALLOCATED; + dev_info(&dc->pdev->dev, "Initialization OK!\n"); + return 1; + } + + if ((dc->config_table.version > 0) + && (dc->config_table.toggle.enabled != TOGGLE_VALID)) { + u32 offset = 0; + DBG1("First phase: pushing upload buffers, clearing download"); + + dev_info(&dc->pdev->dev, "Version of card: %d\n", + dc->config_table.version); + + /* Here we should disable all I/O over F32. */ + setup_memory(dc); + + /* + * We should send ALL channel pair tokens back along + * with reset token + */ + + /* push upload modem buffers */ + write_mem32(dc->port[PORT_MDM].ul_addr[CH_A], + (u32 *) &offset, 4); + write_mem32(dc->port[PORT_MDM].ul_addr[CH_B], + (u32 *) &offset, 4); + + writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr); + + DBG1("First phase done"); + } + + return 1; +} + +/* Enable uplink interrupts */ +static void enable_transmit_ul(enum port_type port, struct nozomi *dc) +{ + static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL}; + + if (port < NOZOMI_MAX_PORTS) { + dc->last_ier |= mask[port]; + writew(dc->last_ier, dc->reg_ier); + } else { + dev_err(&dc->pdev->dev, "Called with wrong port?\n"); + } +} + +/* Disable uplink interrupts */ +static void disable_transmit_ul(enum port_type port, struct nozomi *dc) +{ + static const u16 mask[] = + {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL}; + + if (port < NOZOMI_MAX_PORTS) { + dc->last_ier &= mask[port]; + writew(dc->last_ier, dc->reg_ier); + } else { + dev_err(&dc->pdev->dev, "Called with wrong port?\n"); + } +} + +/* Enable downlink interrupts */ +static void enable_transmit_dl(enum port_type port, struct nozomi *dc) +{ + static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL}; + + if (port < NOZOMI_MAX_PORTS) { + dc->last_ier |= mask[port]; + writew(dc->last_ier, dc->reg_ier); + } else { + dev_err(&dc->pdev->dev, "Called with wrong port?\n"); + } +} + +/* Disable downlink interrupts */ +static void disable_transmit_dl(enum port_type port, struct nozomi *dc) +{ + static const u16 mask[] = + {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL}; + + if (port < NOZOMI_MAX_PORTS) { + dc->last_ier &= mask[port]; + writew(dc->last_ier, dc->reg_ier); + } else { + dev_err(&dc->pdev->dev, "Called with wrong port?\n"); + } +} + +/* + * Return 1 - send buffer to card and ack. + * Return 0 - don't ack, don't send buffer to card. + */ +static int send_data(enum port_type index, struct nozomi *dc) +{ + u32 size = 0; + struct port *port = &dc->port[index]; + const u8 toggle = port->toggle_ul; + void __iomem *addr = port->ul_addr[toggle]; + const u32 ul_size = port->ul_size[toggle]; + struct tty_struct *tty = tty_port_tty_get(&port->port); + + /* Get data from tty and place in buf for now */ + size = kfifo_out(&port->fifo_ul, dc->send_buf, + ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX); + + if (size == 0) { + DBG4("No more data to send, disable link:"); + tty_kref_put(tty); + return 0; + } + + /* DUMP(buf, size); */ + + /* Write length + data */ + write_mem32(addr, (u32 *) &size, 4); + write_mem32(addr + 4, (u32 *) dc->send_buf, size); + + if (tty) + tty_wakeup(tty); + + tty_kref_put(tty); + return 1; +} + +/* If all data has been read, return 1, else 0 */ +static int receive_data(enum port_type index, struct nozomi *dc) +{ + u8 buf[RECEIVE_BUF_MAX] = { 0 }; + int size; + u32 offset = 4; + struct port *port = &dc->port[index]; + void __iomem *addr = port->dl_addr[port->toggle_dl]; + struct tty_struct *tty = tty_port_tty_get(&port->port); + int i, ret; + + if (unlikely(!tty)) { + DBG1("tty not open for port: %d?", index); + return 1; + } + + read_mem32((u32 *) &size, addr, 4); + /* DBG1( "%d bytes port: %d", size, index); */ + + if (test_bit(TTY_THROTTLED, &tty->flags)) { + DBG1("No room in tty, don't read data, don't ack interrupt, " + "disable interrupt"); + + /* disable interrupt in downlink... */ + disable_transmit_dl(index, dc); + ret = 0; + goto put; + } + + if (unlikely(size == 0)) { + dev_err(&dc->pdev->dev, "size == 0?\n"); + ret = 1; + goto put; + } + + while (size > 0) { + read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX); + + if (size == 1) { + tty_insert_flip_char(tty, buf[0], TTY_NORMAL); + size = 0; + } else if (size < RECEIVE_BUF_MAX) { + size -= tty_insert_flip_string(tty, (char *) buf, size); + } else { + i = tty_insert_flip_string(tty, \ + (char *) buf, RECEIVE_BUF_MAX); + size -= i; + offset += i; + } + } + + set_bit(index, &dc->flip); + ret = 1; +put: + tty_kref_put(tty); + return ret; +} + +/* Debug for interrupts */ +#ifdef DEBUG +static char *interrupt2str(u16 interrupt) +{ + static char buf[TMP_BUF_MAX]; + char *p = buf; + + interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL; + interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "MDM_DL2 ") : NULL; + + interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "MDM_UL1 ") : NULL; + interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "MDM_UL2 ") : NULL; + + interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "DIAG_DL1 ") : NULL; + interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "DIAG_DL2 ") : NULL; + + interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "DIAG_UL ") : NULL; + + interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "APP1_DL ") : NULL; + interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "APP2_DL ") : NULL; + + interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "APP1_UL ") : NULL; + interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "APP2_UL ") : NULL; + + interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "CTRL_DL ") : NULL; + interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "CTRL_UL ") : NULL; + + interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf), + "RESET ") : NULL; + + return buf; +} +#endif + +/* + * Receive flow control + * Return 1 - If ok, else 0 + */ +static int receive_flow_control(struct nozomi *dc) +{ + enum port_type port = PORT_MDM; + struct ctrl_dl ctrl_dl; + struct ctrl_dl old_ctrl; + u16 enable_ier = 0; + + read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2); + + switch (ctrl_dl.port) { + case CTRL_CMD: + DBG1("The Base Band sends this value as a response to a " + "request for IMSI detach sent over the control " + "channel uplink (see section 7.6.1)."); + break; + case CTRL_MDM: + port = PORT_MDM; + enable_ier = MDM_DL; + break; + case CTRL_DIAG: + port = PORT_DIAG; + enable_ier = DIAG_DL; + break; + case CTRL_APP1: + port = PORT_APP1; + enable_ier = APP1_DL; + break; + case CTRL_APP2: + port = PORT_APP2; + enable_ier = APP2_DL; + if (dc->state == NOZOMI_STATE_ALLOCATED) { + /* + * After card initialization the flow control + * received for APP2 is always the last + */ + dc->state = NOZOMI_STATE_READY; + dev_info(&dc->pdev->dev, "Device READY!\n"); + } + break; + default: + dev_err(&dc->pdev->dev, + "ERROR: flow control received for non-existing port\n"); + return 0; + }; + + DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl), + *((u16 *)&ctrl_dl)); + + old_ctrl = dc->port[port].ctrl_dl; + dc->port[port].ctrl_dl = ctrl_dl; + + if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) { + DBG1("Disable interrupt (0x%04X) on port: %d", + enable_ier, port); + disable_transmit_ul(port, dc); + + } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) { + + if (kfifo_len(&dc->port[port].fifo_ul)) { + DBG1("Enable interrupt (0x%04X) on port: %d", + enable_ier, port); + DBG1("Data in buffer [%d], enable transmit! ", + kfifo_len(&dc->port[port].fifo_ul)); + enable_transmit_ul(port, dc); + } else { + DBG1("No data in buffer..."); + } + } + + if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) { + DBG1(" No change in mctrl"); + return 1; + } + /* Update statistics */ + if (old_ctrl.CTS != ctrl_dl.CTS) + dc->port[port].tty_icount.cts++; + if (old_ctrl.DSR != ctrl_dl.DSR) + dc->port[port].tty_icount.dsr++; + if (old_ctrl.RI != ctrl_dl.RI) + dc->port[port].tty_icount.rng++; + if (old_ctrl.DCD != ctrl_dl.DCD) + dc->port[port].tty_icount.dcd++; + + wake_up_interruptible(&dc->port[port].tty_wait); + + DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)", + port, + dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts, + dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr); + + return 1; +} + +static enum ctrl_port_type port2ctrl(enum port_type port, + const struct nozomi *dc) +{ + switch (port) { + case PORT_MDM: + return CTRL_MDM; + case PORT_DIAG: + return CTRL_DIAG; + case PORT_APP1: + return CTRL_APP1; + case PORT_APP2: + return CTRL_APP2; + default: + dev_err(&dc->pdev->dev, + "ERROR: send flow control " \ + "received for non-existing port\n"); + }; + return CTRL_ERROR; +} + +/* + * Send flow control, can only update one channel at a time + * Return 0 - If we have updated all flow control + * Return 1 - If we need to update more flow control, ack current enable more + */ +static int send_flow_control(struct nozomi *dc) +{ + u32 i, more_flow_control_to_be_updated = 0; + u16 *ctrl; + + for (i = PORT_MDM; i < MAX_PORT; i++) { + if (dc->port[i].update_flow_control) { + if (more_flow_control_to_be_updated) { + /* We have more flow control to be updated */ + return 1; + } + dc->port[i].ctrl_ul.port = port2ctrl(i, dc); + ctrl = (u16 *)&dc->port[i].ctrl_ul; + write_mem32(dc->port[PORT_CTRL].ul_addr[0], \ + (u32 *) ctrl, 2); + dc->port[i].update_flow_control = 0; + more_flow_control_to_be_updated = 1; + } + } + return 0; +} + +/* + * Handle downlink data, ports that are handled are modem and diagnostics + * Return 1 - ok + * Return 0 - toggle fields are out of sync + */ +static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle, + u16 read_iir, u16 mask1, u16 mask2) +{ + if (*toggle == 0 && read_iir & mask1) { + if (receive_data(port, dc)) { + writew(mask1, dc->reg_fcr); + *toggle = !(*toggle); + } + + if (read_iir & mask2) { + if (receive_data(port, dc)) { + writew(mask2, dc->reg_fcr); + *toggle = !(*toggle); + } + } + } else if (*toggle == 1 && read_iir & mask2) { + if (receive_data(port, dc)) { + writew(mask2, dc->reg_fcr); + *toggle = !(*toggle); + } + + if (read_iir & mask1) { + if (receive_data(port, dc)) { + writew(mask1, dc->reg_fcr); + *toggle = !(*toggle); + } + } + } else { + dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n", + *toggle); + return 0; + } + return 1; +} + +/* + * Handle uplink data, this is currently for the modem port + * Return 1 - ok + * Return 0 - toggle field are out of sync + */ +static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir) +{ + u8 *toggle = &(dc->port[port].toggle_ul); + + if (*toggle == 0 && read_iir & MDM_UL1) { + dc->last_ier &= ~MDM_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(port, dc)) { + writew(MDM_UL1, dc->reg_fcr); + dc->last_ier = dc->last_ier | MDM_UL; + writew(dc->last_ier, dc->reg_ier); + *toggle = !*toggle; + } + + if (read_iir & MDM_UL2) { + dc->last_ier &= ~MDM_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(port, dc)) { + writew(MDM_UL2, dc->reg_fcr); + dc->last_ier = dc->last_ier | MDM_UL; + writew(dc->last_ier, dc->reg_ier); + *toggle = !*toggle; + } + } + + } else if (*toggle == 1 && read_iir & MDM_UL2) { + dc->last_ier &= ~MDM_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(port, dc)) { + writew(MDM_UL2, dc->reg_fcr); + dc->last_ier = dc->last_ier | MDM_UL; + writew(dc->last_ier, dc->reg_ier); + *toggle = !*toggle; + } + + if (read_iir & MDM_UL1) { + dc->last_ier &= ~MDM_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(port, dc)) { + writew(MDM_UL1, dc->reg_fcr); + dc->last_ier = dc->last_ier | MDM_UL; + writew(dc->last_ier, dc->reg_ier); + *toggle = !*toggle; + } + } + } else { + writew(read_iir & MDM_UL, dc->reg_fcr); + dev_err(&dc->pdev->dev, "port out of sync!\n"); + return 0; + } + return 1; +} + +static irqreturn_t interrupt_handler(int irq, void *dev_id) +{ + struct nozomi *dc = dev_id; + unsigned int a; + u16 read_iir; + + if (!dc) + return IRQ_NONE; + + spin_lock(&dc->spin_mutex); + read_iir = readw(dc->reg_iir); + + /* Card removed */ + if (read_iir == (u16)-1) + goto none; + /* + * Just handle interrupt enabled in IER + * (by masking with dc->last_ier) + */ + read_iir &= dc->last_ier; + + if (read_iir == 0) + goto none; + + + DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir, + dc->last_ier); + + if (read_iir & RESET) { + if (unlikely(!nozomi_read_config_table(dc))) { + dc->last_ier = 0x0; + writew(dc->last_ier, dc->reg_ier); + dev_err(&dc->pdev->dev, "Could not read status from " + "card, we should disable interface\n"); + } else { + writew(RESET, dc->reg_fcr); + } + /* No more useful info if this was the reset interrupt. */ + goto exit_handler; + } + if (read_iir & CTRL_UL) { + DBG1("CTRL_UL"); + dc->last_ier &= ~CTRL_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_flow_control(dc)) { + writew(CTRL_UL, dc->reg_fcr); + dc->last_ier = dc->last_ier | CTRL_UL; + writew(dc->last_ier, dc->reg_ier); + } + } + if (read_iir & CTRL_DL) { + receive_flow_control(dc); + writew(CTRL_DL, dc->reg_fcr); + } + if (read_iir & MDM_DL) { + if (!handle_data_dl(dc, PORT_MDM, + &(dc->port[PORT_MDM].toggle_dl), read_iir, + MDM_DL1, MDM_DL2)) { + dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n"); + goto exit_handler; + } + } + if (read_iir & MDM_UL) { + if (!handle_data_ul(dc, PORT_MDM, read_iir)) { + dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n"); + goto exit_handler; + } + } + if (read_iir & DIAG_DL) { + if (!handle_data_dl(dc, PORT_DIAG, + &(dc->port[PORT_DIAG].toggle_dl), read_iir, + DIAG_DL1, DIAG_DL2)) { + dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n"); + goto exit_handler; + } + } + if (read_iir & DIAG_UL) { + dc->last_ier &= ~DIAG_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(PORT_DIAG, dc)) { + writew(DIAG_UL, dc->reg_fcr); + dc->last_ier = dc->last_ier | DIAG_UL; + writew(dc->last_ier, dc->reg_ier); + } + } + if (read_iir & APP1_DL) { + if (receive_data(PORT_APP1, dc)) + writew(APP1_DL, dc->reg_fcr); + } + if (read_iir & APP1_UL) { + dc->last_ier &= ~APP1_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(PORT_APP1, dc)) { + writew(APP1_UL, dc->reg_fcr); + dc->last_ier = dc->last_ier | APP1_UL; + writew(dc->last_ier, dc->reg_ier); + } + } + if (read_iir & APP2_DL) { + if (receive_data(PORT_APP2, dc)) + writew(APP2_DL, dc->reg_fcr); + } + if (read_iir & APP2_UL) { + dc->last_ier &= ~APP2_UL; + writew(dc->last_ier, dc->reg_ier); + if (send_data(PORT_APP2, dc)) { + writew(APP2_UL, dc->reg_fcr); + dc->last_ier = dc->last_ier | APP2_UL; + writew(dc->last_ier, dc->reg_ier); + } + } + +exit_handler: + spin_unlock(&dc->spin_mutex); + for (a = 0; a < NOZOMI_MAX_PORTS; a++) { + struct tty_struct *tty; + if (test_and_clear_bit(a, &dc->flip)) { + tty = tty_port_tty_get(&dc->port[a].port); + if (tty) + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + } + return IRQ_HANDLED; +none: + spin_unlock(&dc->spin_mutex); + return IRQ_NONE; +} + +static void nozomi_get_card_type(struct nozomi *dc) +{ + int i; + u32 size = 0; + + for (i = 0; i < 6; i++) + size += pci_resource_len(dc->pdev, i); + + /* Assume card type F32_8 if no match */ + dc->card_type = size == 2048 ? F32_2 : F32_8; + + dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type); +} + +static void nozomi_setup_private_data(struct nozomi *dc) +{ + void __iomem *offset = dc->base_addr + dc->card_type / 2; + unsigned int i; + + dc->reg_fcr = (void __iomem *)(offset + R_FCR); + dc->reg_iir = (void __iomem *)(offset + R_IIR); + dc->reg_ier = (void __iomem *)(offset + R_IER); + dc->last_ier = 0; + dc->flip = 0; + + dc->port[PORT_MDM].token_dl = MDM_DL; + dc->port[PORT_DIAG].token_dl = DIAG_DL; + dc->port[PORT_APP1].token_dl = APP1_DL; + dc->port[PORT_APP2].token_dl = APP2_DL; + + for (i = 0; i < MAX_PORT; i++) + init_waitqueue_head(&dc->port[i].tty_wait); +} + +static ssize_t card_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev)); + + return sprintf(buf, "%d\n", dc->card_type); +} +static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL); + +static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev)); + + return sprintf(buf, "%u\n", dc->open_ttys); +} +static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL); + +static void make_sysfs_files(struct nozomi *dc) +{ + if (device_create_file(&dc->pdev->dev, &dev_attr_card_type)) + dev_err(&dc->pdev->dev, + "Could not create sysfs file for card_type\n"); + if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys)) + dev_err(&dc->pdev->dev, + "Could not create sysfs file for open_ttys\n"); +} + +static void remove_sysfs_files(struct nozomi *dc) +{ + device_remove_file(&dc->pdev->dev, &dev_attr_card_type); + device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys); +} + +/* Allocate memory for one device */ +static int __devinit nozomi_card_init(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + resource_size_t start; + int ret; + struct nozomi *dc = NULL; + int ndev_idx; + int i; + + dev_dbg(&pdev->dev, "Init, new card found\n"); + + for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++) + if (!ndevs[ndev_idx]) + break; + + if (ndev_idx >= ARRAY_SIZE(ndevs)) { + dev_err(&pdev->dev, "no free tty range for this card left\n"); + ret = -EIO; + goto err; + } + + dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL); + if (unlikely(!dc)) { + dev_err(&pdev->dev, "Could not allocate memory\n"); + ret = -ENOMEM; + goto err_free; + } + + dc->pdev = pdev; + + ret = pci_enable_device(dc->pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to enable PCI Device\n"); + goto err_free; + } + + ret = pci_request_regions(dc->pdev, NOZOMI_NAME); + if (ret) { + dev_err(&pdev->dev, "I/O address 0x%04x already in use\n", + (int) /* nozomi_private.io_addr */ 0); + goto err_disable_device; + } + + start = pci_resource_start(dc->pdev, 0); + if (start == 0) { + dev_err(&pdev->dev, "No I/O address for card detected\n"); + ret = -ENODEV; + goto err_rel_regs; + } + + /* Find out what card type it is */ + nozomi_get_card_type(dc); + + dc->base_addr = ioremap_nocache(start, dc->card_type); + if (!dc->base_addr) { + dev_err(&pdev->dev, "Unable to map card MMIO\n"); + ret = -ENODEV; + goto err_rel_regs; + } + + dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL); + if (!dc->send_buf) { + dev_err(&pdev->dev, "Could not allocate send buffer?\n"); + ret = -ENOMEM; + goto err_free_sbuf; + } + + for (i = PORT_MDM; i < MAX_PORT; i++) { + if (kfifo_alloc(&dc->port[i].fifo_ul, + FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) { + dev_err(&pdev->dev, + "Could not allocate kfifo buffer\n"); + ret = -ENOMEM; + goto err_free_kfifo; + } + } + + spin_lock_init(&dc->spin_mutex); + + nozomi_setup_private_data(dc); + + /* Disable all interrupts */ + dc->last_ier = 0; + writew(dc->last_ier, dc->reg_ier); + + ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED, + NOZOMI_NAME, dc); + if (unlikely(ret)) { + dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq); + goto err_free_kfifo; + } + + DBG1("base_addr: %p", dc->base_addr); + + make_sysfs_files(dc); + + dc->index_start = ndev_idx * MAX_PORT; + ndevs[ndev_idx] = dc; + + pci_set_drvdata(pdev, dc); + + /* Enable RESET interrupt */ + dc->last_ier = RESET; + iowrite16(dc->last_ier, dc->reg_ier); + + dc->state = NOZOMI_STATE_ENABLED; + + for (i = 0; i < MAX_PORT; i++) { + struct device *tty_dev; + struct port *port = &dc->port[i]; + port->dc = dc; + mutex_init(&port->tty_sem); + tty_port_init(&port->port); + port->port.ops = &noz_tty_port_ops; + tty_dev = tty_register_device(ntty_driver, dc->index_start + i, + &pdev->dev); + + if (IS_ERR(tty_dev)) { + ret = PTR_ERR(tty_dev); + dev_err(&pdev->dev, "Could not allocate tty?\n"); + goto err_free_tty; + } + } + + return 0; + +err_free_tty: + for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) + tty_unregister_device(ntty_driver, i); +err_free_kfifo: + for (i = 0; i < MAX_PORT; i++) + kfifo_free(&dc->port[i].fifo_ul); +err_free_sbuf: + kfree(dc->send_buf); + iounmap(dc->base_addr); +err_rel_regs: + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); +err_free: + kfree(dc); +err: + return ret; +} + +static void __devexit tty_exit(struct nozomi *dc) +{ + unsigned int i; + + DBG1(" "); + + flush_scheduled_work(); + + for (i = 0; i < MAX_PORT; ++i) { + struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); + if (tty && list_empty(&tty->hangup_work.entry)) + tty_hangup(tty); + tty_kref_put(tty); + } + /* Racy below - surely should wait for scheduled work to be done or + complete off a hangup method ? */ + while (dc->open_ttys) + msleep(1); + for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i) + tty_unregister_device(ntty_driver, i); +} + +/* Deallocate memory for one device */ +static void __devexit nozomi_card_exit(struct pci_dev *pdev) +{ + int i; + struct ctrl_ul ctrl; + struct nozomi *dc = pci_get_drvdata(pdev); + + /* Disable all interrupts */ + dc->last_ier = 0; + writew(dc->last_ier, dc->reg_ier); + + tty_exit(dc); + + /* Send 0x0001, command card to resend the reset token. */ + /* This is to get the reset when the module is reloaded. */ + ctrl.port = 0x00; + ctrl.reserved = 0; + ctrl.RTS = 0; + ctrl.DTR = 1; + DBG1("sending flow control 0x%04X", *((u16 *)&ctrl)); + + /* Setup dc->reg addresses to we can use defines here */ + write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2); + writew(CTRL_UL, dc->reg_fcr); /* push the token to the card. */ + + remove_sysfs_files(dc); + + free_irq(pdev->irq, dc); + + for (i = 0; i < MAX_PORT; i++) + kfifo_free(&dc->port[i].fifo_ul); + + kfree(dc->send_buf); + + iounmap(dc->base_addr); + + pci_release_regions(pdev); + + pci_disable_device(pdev); + + ndevs[dc->index_start / MAX_PORT] = NULL; + + kfree(dc); +} + +static void set_rts(const struct tty_struct *tty, int rts) +{ + struct port *port = get_port_by_tty(tty); + + port->ctrl_ul.RTS = rts; + port->update_flow_control = 1; + enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty)); +} + +static void set_dtr(const struct tty_struct *tty, int dtr) +{ + struct port *port = get_port_by_tty(tty); + + DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr); + + port->ctrl_ul.DTR = dtr; + port->update_flow_control = 1; + enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty)); +} + +/* + * ---------------------------------------------------------------------------- + * TTY code + * ---------------------------------------------------------------------------- + */ + +static int ntty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct port *port = get_port_by_tty(tty); + struct nozomi *dc = get_dc_by_tty(tty); + int ret; + if (!port || !dc || dc->state != NOZOMI_STATE_READY) + return -ENODEV; + ret = tty_init_termios(tty); + if (ret == 0) { + tty_driver_kref_get(driver); + tty->count++; + tty->driver_data = port; + driver->ttys[tty->index] = tty; + } + return ret; +} + +static void ntty_cleanup(struct tty_struct *tty) +{ + tty->driver_data = NULL; +} + +static int ntty_activate(struct tty_port *tport, struct tty_struct *tty) +{ + struct port *port = container_of(tport, struct port, port); + struct nozomi *dc = port->dc; + unsigned long flags; + + DBG1("open: %d", port->token_dl); + spin_lock_irqsave(&dc->spin_mutex, flags); + dc->last_ier = dc->last_ier | port->token_dl; + writew(dc->last_ier, dc->reg_ier); + dc->open_ttys++; + spin_unlock_irqrestore(&dc->spin_mutex, flags); + printk("noz: activated %d: %p\n", tty->index, tport); + return 0; +} + +static int ntty_open(struct tty_struct *tty, struct file *filp) +{ + struct port *port = tty->driver_data; + return tty_port_open(&port->port, tty, filp); +} + +static void ntty_shutdown(struct tty_port *tport) +{ + struct port *port = container_of(tport, struct port, port); + struct nozomi *dc = port->dc; + unsigned long flags; + + DBG1("close: %d", port->token_dl); + spin_lock_irqsave(&dc->spin_mutex, flags); + dc->last_ier &= ~(port->token_dl); + writew(dc->last_ier, dc->reg_ier); + dc->open_ttys--; + spin_unlock_irqrestore(&dc->spin_mutex, flags); + printk("noz: shutdown %p\n", tport); +} + +static void ntty_close(struct tty_struct *tty, struct file *filp) +{ + struct port *port = tty->driver_data; + if (port) + tty_port_close(&port->port, tty, filp); +} + +static void ntty_hangup(struct tty_struct *tty) +{ + struct port *port = tty->driver_data; + tty_port_hangup(&port->port); +} + +/* + * called when the userspace process writes to the tty (/dev/noz*). + * Data is inserted into a fifo, which is then read and transfered to the modem. + */ +static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, + int count) +{ + int rval = -EINVAL; + struct nozomi *dc = get_dc_by_tty(tty); + struct port *port = tty->driver_data; + unsigned long flags; + + /* DBG1( "WRITEx: %d, index = %d", count, index); */ + + if (!dc || !port) + return -ENODEV; + + mutex_lock(&port->tty_sem); + + if (unlikely(!port->port.count)) { + DBG1(" "); + goto exit; + } + + rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count); + + /* notify card */ + if (unlikely(dc == NULL)) { + DBG1("No device context?"); + goto exit; + } + + spin_lock_irqsave(&dc->spin_mutex, flags); + /* CTS is only valid on the modem channel */ + if (port == &(dc->port[PORT_MDM])) { + if (port->ctrl_dl.CTS) { + DBG4("Enable interrupt"); + enable_transmit_ul(tty->index % MAX_PORT, dc); + } else { + dev_err(&dc->pdev->dev, + "CTS not active on modem port?\n"); + } + } else { + enable_transmit_ul(tty->index % MAX_PORT, dc); + } + spin_unlock_irqrestore(&dc->spin_mutex, flags); + +exit: + mutex_unlock(&port->tty_sem); + return rval; +} + +/* + * Calculate how much is left in device + * This method is called by the upper tty layer. + * #according to sources N_TTY.c it expects a value >= 0 and + * does not check for negative values. + * + * If the port is unplugged report lots of room and let the bits + * dribble away so we don't block anything. + */ +static int ntty_write_room(struct tty_struct *tty) +{ + struct port *port = tty->driver_data; + int room = 4096; + const struct nozomi *dc = get_dc_by_tty(tty); + + if (dc) { + mutex_lock(&port->tty_sem); + if (port->port.count) + room = kfifo_avail(&port->fifo_ul); + mutex_unlock(&port->tty_sem); + } + return room; +} + +/* Gets io control parameters */ +static int ntty_tiocmget(struct tty_struct *tty) +{ + const struct port *port = tty->driver_data; + const struct ctrl_dl *ctrl_dl = &port->ctrl_dl; + const struct ctrl_ul *ctrl_ul = &port->ctrl_ul; + + /* Note: these could change under us but it is not clear this + matters if so */ + return (ctrl_ul->RTS ? TIOCM_RTS : 0) | + (ctrl_ul->DTR ? TIOCM_DTR : 0) | + (ctrl_dl->DCD ? TIOCM_CAR : 0) | + (ctrl_dl->RI ? TIOCM_RNG : 0) | + (ctrl_dl->DSR ? TIOCM_DSR : 0) | + (ctrl_dl->CTS ? TIOCM_CTS : 0); +} + +/* Sets io controls parameters */ +static int ntty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct nozomi *dc = get_dc_by_tty(tty); + unsigned long flags; + + spin_lock_irqsave(&dc->spin_mutex, flags); + if (set & TIOCM_RTS) + set_rts(tty, 1); + else if (clear & TIOCM_RTS) + set_rts(tty, 0); + + if (set & TIOCM_DTR) + set_dtr(tty, 1); + else if (clear & TIOCM_DTR) + set_dtr(tty, 0); + spin_unlock_irqrestore(&dc->spin_mutex, flags); + + return 0; +} + +static int ntty_cflags_changed(struct port *port, unsigned long flags, + struct async_icount *cprev) +{ + const struct async_icount cnow = port->tty_icount; + int ret; + + ret = ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((flags & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} + +static int ntty_tiocgicount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct port *port = tty->driver_data; + const struct async_icount cnow = port->tty_icount; + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + return 0; +} + +static int ntty_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct port *port = tty->driver_data; + int rval = -ENOIOCTLCMD; + + DBG1("******** IOCTL, cmd: %d", cmd); + + switch (cmd) { + case TIOCMIWAIT: { + struct async_icount cprev = port->tty_icount; + + rval = wait_event_interruptible(port->tty_wait, + ntty_cflags_changed(port, arg, &cprev)); + break; + } + default: + DBG1("ERR: 0x%08X, %d", cmd, cmd); + break; + }; + + return rval; +} + +/* + * Called by the upper tty layer when tty buffers are ready + * to receive data again after a call to throttle. + */ +static void ntty_unthrottle(struct tty_struct *tty) +{ + struct nozomi *dc = get_dc_by_tty(tty); + unsigned long flags; + + DBG1("UNTHROTTLE"); + spin_lock_irqsave(&dc->spin_mutex, flags); + enable_transmit_dl(tty->index % MAX_PORT, dc); + set_rts(tty, 1); + + spin_unlock_irqrestore(&dc->spin_mutex, flags); +} + +/* + * Called by the upper tty layer when the tty buffers are almost full. + * The driver should stop send more data. + */ +static void ntty_throttle(struct tty_struct *tty) +{ + struct nozomi *dc = get_dc_by_tty(tty); + unsigned long flags; + + DBG1("THROTTLE"); + spin_lock_irqsave(&dc->spin_mutex, flags); + set_rts(tty, 0); + spin_unlock_irqrestore(&dc->spin_mutex, flags); +} + +/* Returns number of chars in buffer, called by tty layer */ +static s32 ntty_chars_in_buffer(struct tty_struct *tty) +{ + struct port *port = tty->driver_data; + struct nozomi *dc = get_dc_by_tty(tty); + s32 rval = 0; + + if (unlikely(!dc || !port)) { + goto exit_in_buffer; + } + + if (unlikely(!port->port.count)) { + dev_err(&dc->pdev->dev, "No tty open?\n"); + goto exit_in_buffer; + } + + rval = kfifo_len(&port->fifo_ul); + +exit_in_buffer: + return rval; +} + +static const struct tty_port_operations noz_tty_port_ops = { + .activate = ntty_activate, + .shutdown = ntty_shutdown, +}; + +static const struct tty_operations tty_ops = { + .ioctl = ntty_ioctl, + .open = ntty_open, + .close = ntty_close, + .hangup = ntty_hangup, + .write = ntty_write, + .write_room = ntty_write_room, + .unthrottle = ntty_unthrottle, + .throttle = ntty_throttle, + .chars_in_buffer = ntty_chars_in_buffer, + .tiocmget = ntty_tiocmget, + .tiocmset = ntty_tiocmset, + .get_icount = ntty_tiocgicount, + .install = ntty_install, + .cleanup = ntty_cleanup, +}; + +/* Module initialization */ +static struct pci_driver nozomi_driver = { + .name = NOZOMI_NAME, + .id_table = nozomi_pci_tbl, + .probe = nozomi_card_init, + .remove = __devexit_p(nozomi_card_exit), +}; + +static __init int nozomi_init(void) +{ + int ret; + + printk(KERN_INFO "Initializing %s\n", VERSION_STRING); + + ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS); + if (!ntty_driver) + return -ENOMEM; + + ntty_driver->owner = THIS_MODULE; + ntty_driver->driver_name = NOZOMI_NAME_TTY; + ntty_driver->name = "noz"; + ntty_driver->major = 0; + ntty_driver->type = TTY_DRIVER_TYPE_SERIAL; + ntty_driver->subtype = SERIAL_TYPE_NORMAL; + ntty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + ntty_driver->init_termios = tty_std_termios; + ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \ + HUPCL | CLOCAL; + ntty_driver->init_termios.c_ispeed = 115200; + ntty_driver->init_termios.c_ospeed = 115200; + tty_set_operations(ntty_driver, &tty_ops); + + ret = tty_register_driver(ntty_driver); + if (ret) { + printk(KERN_ERR "Nozomi: failed to register ntty driver\n"); + goto free_tty; + } + + ret = pci_register_driver(&nozomi_driver); + if (ret) { + printk(KERN_ERR "Nozomi: can't register pci driver\n"); + goto unr_tty; + } + + return 0; +unr_tty: + tty_unregister_driver(ntty_driver); +free_tty: + put_tty_driver(ntty_driver); + return ret; +} + +static __exit void nozomi_exit(void) +{ + printk(KERN_INFO "Unloading %s\n", DRIVER_DESC); + pci_unregister_driver(&nozomi_driver); + tty_unregister_driver(ntty_driver); + put_tty_driver(ntty_driver); +} + +module_init(nozomi_init); +module_exit(nozomi_exit); + +module_param(debug, int, S_IRUGO | S_IWUSR); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c new file mode 100644 index 000000000000..3780da8ad12d --- /dev/null +++ b/drivers/tty/rocket.c @@ -0,0 +1,3199 @@ +/* + * RocketPort device driver for Linux + * + * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000. + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Kernel Synchronization: + * + * This driver has 2 kernel control paths - exception handlers (calls into the driver + * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts + * are not used. + * + * Critical data: + * - rp_table[], accessed through passed "info" pointers, is a global (static) array of + * serial port state information and the xmit_buf circular buffer. Protected by + * a per port spinlock. + * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there + * is data to be transmitted. Protected by atomic bit operations. + * - rp_num_ports, int indicating number of open ports, protected by atomic operations. + * + * rp_write() and rp_write_char() functions use a per port semaphore to protect against + * simultaneous access to the same port by more than one process. + */ + +/****** Defines ******/ +#define ROCKET_PARANOIA_CHECK +#define ROCKET_DISABLE_SIMUSAGE + +#undef ROCKET_SOFT_FLOW +#undef ROCKET_DEBUG_OPEN +#undef ROCKET_DEBUG_INTR +#undef ROCKET_DEBUG_WRITE +#undef ROCKET_DEBUG_FLOW +#undef ROCKET_DEBUG_THROTTLE +#undef ROCKET_DEBUG_WAIT_UNTIL_SENT +#undef ROCKET_DEBUG_RECEIVE +#undef ROCKET_DEBUG_HANGUP +#undef REV_PCI_ORDER +#undef ROCKET_DEBUG_IO + +#define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */ + +/****** Kernel includes ******/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****** RocketPort includes ******/ + +#include "rocket_int.h" +#include "rocket.h" + +#define ROCKET_VERSION "2.09" +#define ROCKET_DATE "12-June-2003" + +/****** RocketPort Local Variables ******/ + +static void rp_do_poll(unsigned long dummy); + +static struct tty_driver *rocket_driver; + +static struct rocket_version driver_version = { + ROCKET_VERSION, ROCKET_DATE +}; + +static struct r_port *rp_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */ +static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */ + /* eg. Bit 0 indicates port 0 has xmit data, ... */ +static atomic_t rp_num_ports_open; /* Number of serial ports open */ +static DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0); + +static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */ +static unsigned long board2; +static unsigned long board3; +static unsigned long board4; +static unsigned long controller; +static int support_low_speed; +static unsigned long modem1; +static unsigned long modem2; +static unsigned long modem3; +static unsigned long modem4; +static unsigned long pc104_1[8]; +static unsigned long pc104_2[8]; +static unsigned long pc104_3[8]; +static unsigned long pc104_4[8]; +static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 }; + +static int rp_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */ +static unsigned long rcktpt_io_addr[NUM_BOARDS]; +static int rcktpt_type[NUM_BOARDS]; +static int is_PCI[NUM_BOARDS]; +static rocketModel_t rocketModel[NUM_BOARDS]; +static int max_board; +static const struct tty_port_operations rocket_port_ops; + +/* + * The following arrays define the interrupt bits corresponding to each AIOP. + * These bits are different between the ISA and regular PCI boards and the + * Universal PCI boards. + */ + +static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { + AIOP_INTR_BIT_0, + AIOP_INTR_BIT_1, + AIOP_INTR_BIT_2, + AIOP_INTR_BIT_3 +}; + +static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { + UPCI_AIOP_INTR_BIT_0, + UPCI_AIOP_INTR_BIT_1, + UPCI_AIOP_INTR_BIT_2, + UPCI_AIOP_INTR_BIT_3 +}; + +static Byte_t RData[RDATASIZE] = { + 0x00, 0x09, 0xf6, 0x82, + 0x02, 0x09, 0x86, 0xfb, + 0x04, 0x09, 0x00, 0x0a, + 0x06, 0x09, 0x01, 0x0a, + 0x08, 0x09, 0x8a, 0x13, + 0x0a, 0x09, 0xc5, 0x11, + 0x0c, 0x09, 0x86, 0x85, + 0x0e, 0x09, 0x20, 0x0a, + 0x10, 0x09, 0x21, 0x0a, + 0x12, 0x09, 0x41, 0xff, + 0x14, 0x09, 0x82, 0x00, + 0x16, 0x09, 0x82, 0x7b, + 0x18, 0x09, 0x8a, 0x7d, + 0x1a, 0x09, 0x88, 0x81, + 0x1c, 0x09, 0x86, 0x7a, + 0x1e, 0x09, 0x84, 0x81, + 0x20, 0x09, 0x82, 0x7c, + 0x22, 0x09, 0x0a, 0x0a +}; + +static Byte_t RRegData[RREGDATASIZE] = { + 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ + 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ + 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ + 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ + 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ + 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ + 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ + 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ + 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ + 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ + 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ + 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ + 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ +}; + +static CONTROLLER_T sController[CTL_SIZE] = { + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}} +}; + +static Byte_t sBitMapClrTbl[8] = { + 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f +}; + +static Byte_t sBitMapSetTbl[8] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; + +static int sClockPrescale = 0x14; + +/* + * Line number is the ttySIx number (x), the Minor number. We + * assign them sequentially, starting at zero. The following + * array keeps track of the line number assigned to a given board/aiop/channel. + */ +static unsigned char lineNumbers[MAX_RP_PORTS]; +static unsigned long nextLineNumber; + +/***** RocketPort Static Prototypes *********/ +static int __init init_ISA(int i); +static void rp_wait_until_sent(struct tty_struct *tty, int timeout); +static void rp_flush_buffer(struct tty_struct *tty); +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); +static unsigned char GetLineNumber(int ctrl, int aiop, int ch); +static unsigned char SetLineNumber(int ctrl, int aiop, int ch); +static void rp_start(struct tty_struct *tty); +static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, + int ChanNum); +static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode); +static void sFlushRxFIFO(CHANNEL_T * ChP); +static void sFlushTxFIFO(CHANNEL_T * ChP); +static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags); +static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); +static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); +static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); +static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd); +static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, + ByteIO_t * AiopIOList, int AiopIOListSize, + int IRQNum, Byte_t Frequency, int PeriodicOnly); +static int sReadAiopID(ByteIO_t io); +static int sReadAiopNumChan(WordIO_t io); + +MODULE_AUTHOR("Theodore Ts'o"); +MODULE_DESCRIPTION("Comtrol RocketPort driver"); +module_param(board1, ulong, 0); +MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); +module_param(board2, ulong, 0); +MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); +module_param(board3, ulong, 0); +MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); +module_param(board4, ulong, 0); +MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); +module_param(controller, ulong, 0); +MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); +module_param(support_low_speed, bool, 0); +MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); +module_param(modem1, ulong, 0); +MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem"); +module_param(modem2, ulong, 0); +MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem"); +module_param(modem3, ulong, 0); +MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem"); +module_param(modem4, ulong, 0); +MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem"); +module_param_array(pc104_1, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,..."); +module_param_array(pc104_2, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,..."); +module_param_array(pc104_3, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,..."); +module_param_array(pc104_4, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,..."); + +static int rp_init(void); +static void rp_cleanup_module(void); + +module_init(rp_init); +module_exit(rp_cleanup_module); + + +MODULE_LICENSE("Dual BSD/GPL"); + +/*************************************************************************/ +/* Module code starts here */ + +static inline int rocket_paranoia_check(struct r_port *info, + const char *routine) +{ +#ifdef ROCKET_PARANOIA_CHECK + if (!info) + return 1; + if (info->magic != RPORT_MAGIC) { + printk(KERN_WARNING "Warning: bad magic number for rocketport " + "struct in %s\n", routine); + return 1; + } +#endif + return 0; +} + + +/* Serial port receive data function. Called (from timer poll) when an AIOPIC signals + * that receive data is present on a serial port. Pulls data from FIFO, moves it into the + * tty layer. + */ +static void rp_do_receive(struct r_port *info, + struct tty_struct *tty, + CHANNEL_t * cp, unsigned int ChanStatus) +{ + unsigned int CharNStat; + int ToRecv, wRecv, space; + unsigned char *cbuf; + + ToRecv = sGetRxCnt(cp); +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv); +#endif + if (ToRecv == 0) + return; + + /* + * if status indicates there are errored characters in the + * FIFO, then enter status mode (a word in FIFO holds + * character and status). + */ + if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { + if (!(ChanStatus & STATMODE)) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Entering STATMODE...\n"); +#endif + ChanStatus |= STATMODE; + sEnRxStatusMode(cp); + } + } + + /* + * if we previously entered status mode, then read down the + * FIFO one word at a time, pulling apart the character and + * the status. Update error counters depending on status + */ + if (ChanStatus & STATMODE) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Ignore %x, read %x...\n", + info->ignore_status_mask, info->read_status_mask); +#endif + while (ToRecv) { + char flag; + + CharNStat = sInW(sGetTxRxDataIO(cp)); +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "%x...\n", CharNStat); +#endif + if (CharNStat & STMBREAKH) + CharNStat &= ~(STMFRAMEH | STMPARITYH); + if (CharNStat & info->ignore_status_mask) { + ToRecv--; + continue; + } + CharNStat &= info->read_status_mask; + if (CharNStat & STMBREAKH) + flag = TTY_BREAK; + else if (CharNStat & STMPARITYH) + flag = TTY_PARITY; + else if (CharNStat & STMFRAMEH) + flag = TTY_FRAME; + else if (CharNStat & STMRCVROVRH) + flag = TTY_OVERRUN; + else + flag = TTY_NORMAL; + tty_insert_flip_char(tty, CharNStat & 0xff, flag); + ToRecv--; + } + + /* + * after we've emptied the FIFO in status mode, turn + * status mode back off + */ + if (sGetRxCnt(cp) == 0) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Status mode off.\n"); +#endif + sDisRxStatusMode(cp); + } + } else { + /* + * we aren't in status mode, so read down the FIFO two + * characters at time by doing repeated word IO + * transfer. + */ + space = tty_prepare_flip_string(tty, &cbuf, ToRecv); + if (space < ToRecv) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); +#endif + if (space <= 0) + return; + ToRecv = space; + } + wRecv = ToRecv >> 1; + if (wRecv) + sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv); + if (ToRecv & 1) + cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); + } + /* Push the data up to the tty layer */ + tty_flip_buffer_push(tty); +} + +/* + * Serial port transmit data function. Called from the timer polling loop as a + * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready + * to be sent out the serial port. Data is buffered in rp_table[line].xmit_buf, it is + * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks. + */ +static void rp_do_transmit(struct r_port *info) +{ + int c; + CHANNEL_t *cp = &info->channel; + struct tty_struct *tty; + unsigned long flags; + +#ifdef ROCKET_DEBUG_INTR + printk(KERN_DEBUG "%s\n", __func__); +#endif + if (!info) + return; + tty = tty_port_tty_get(&info->port); + + if (tty == NULL) { + printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + return; + } + + spin_lock_irqsave(&info->slock, flags); + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + /* Loop sending data to FIFO until done or FIFO full */ + while (1) { + if (tty->stopped || tty->hw_stopped) + break; + c = min(info->xmit_fifo_room, info->xmit_cnt); + c = min(c, XMIT_BUF_SIZE - info->xmit_tail); + if (c <= 0 || info->xmit_fifo_room <= 0) + break; + sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); + if (c & 1) + sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]); + info->xmit_tail += c; + info->xmit_tail &= XMIT_BUF_SIZE - 1; + info->xmit_cnt -= c; + info->xmit_fifo_room -= c; +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "tx %d chars...\n", c); +#endif + } + + if (info->xmit_cnt == 0) + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + if (info->xmit_cnt < WAKEUP_CHARS) { + tty_wakeup(tty); +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } + + spin_unlock_irqrestore(&info->slock, flags); + tty_kref_put(tty); + +#ifdef ROCKET_DEBUG_INTR + printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, + info->xmit_tail, info->xmit_fifo_room); +#endif +} + +/* + * Called when a serial port signals it has read data in it's RX FIFO. + * It checks what interrupts are pending and services them, including + * receiving serial data. + */ +static void rp_handle_port(struct r_port *info) +{ + CHANNEL_t *cp; + struct tty_struct *tty; + unsigned int IntMask, ChanStatus; + + if (!info) + return; + + if ((info->port.flags & ASYNC_INITIALIZED) == 0) { + printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " + "info->flags & NOT_INIT\n"); + return; + } + tty = tty_port_tty_get(&info->port); + if (!tty) { + printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " + "tty==NULL\n"); + return; + } + cp = &info->channel; + + IntMask = sGetChanIntID(cp) & info->intmask; +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "rp_interrupt %02x...\n", IntMask); +#endif + ChanStatus = sGetChanStatus(cp); + if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ + rp_do_receive(info, tty, cp, ChanStatus); + } + if (IntMask & DELTA_CD) { /* CD change */ +#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) + printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, + (ChanStatus & CD_ACT) ? "on" : "off"); +#endif + if (!(ChanStatus & CD_ACT) && info->cd_status) { +#ifdef ROCKET_DEBUG_HANGUP + printk(KERN_INFO "CD drop, calling hangup.\n"); +#endif + tty_hangup(tty); + } + info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; + wake_up_interruptible(&info->port.open_wait); + } +#ifdef ROCKET_DEBUG_INTR + if (IntMask & DELTA_CTS) { /* CTS change */ + printk(KERN_INFO "CTS change...\n"); + } + if (IntMask & DELTA_DSR) { /* DSR change */ + printk(KERN_INFO "DSR change...\n"); + } +#endif + tty_kref_put(tty); +} + +/* + * The top level polling routine. Repeats every 1/100 HZ (10ms). + */ +static void rp_do_poll(unsigned long dummy) +{ + CONTROLLER_t *ctlp; + int ctrl, aiop, ch, line; + unsigned int xmitmask, i; + unsigned int CtlMask; + unsigned char AiopMask; + Word_t bit; + + /* Walk through all the boards (ctrl's) */ + for (ctrl = 0; ctrl < max_board; ctrl++) { + if (rcktpt_io_addr[ctrl] <= 0) + continue; + + /* Get a ptr to the board's control struct */ + ctlp = sCtlNumToCtlPtr(ctrl); + + /* Get the interrupt status from the board */ +#ifdef CONFIG_PCI + if (ctlp->BusType == isPCI) + CtlMask = sPCIGetControllerIntStatus(ctlp); + else +#endif + CtlMask = sGetControllerIntStatus(ctlp); + + /* Check if any AIOP read bits are set */ + for (aiop = 0; CtlMask; aiop++) { + bit = ctlp->AiopIntrBits[aiop]; + if (CtlMask & bit) { + CtlMask &= ~bit; + AiopMask = sGetAiopIntStatus(ctlp, aiop); + + /* Check if any port read bits are set */ + for (ch = 0; AiopMask; AiopMask >>= 1, ch++) { + if (AiopMask & 1) { + + /* Get the line number (/dev/ttyRx number). */ + /* Read the data from the port. */ + line = GetLineNumber(ctrl, aiop, ch); + rp_handle_port(rp_table[line]); + } + } + } + } + + xmitmask = xmit_flags[ctrl]; + + /* + * xmit_flags contains bit-significant flags, indicating there is data + * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port + * 1, ... (32 total possible). The variable i has the aiop and ch + * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc). + */ + if (xmitmask) { + for (i = 0; i < rocketModel[ctrl].numPorts; i++) { + if (xmitmask & (1 << i)) { + aiop = (i & 0x18) >> 3; + ch = i & 0x07; + line = GetLineNumber(ctrl, aiop, ch); + rp_do_transmit(rp_table[line]); + } + } + } + } + + /* + * Reset the timer so we get called at the next clock tick (10ms). + */ + if (atomic_read(&rp_num_ports_open)) + mod_timer(&rocket_timer, jiffies + POLL_PERIOD); +} + +/* + * Initializes the r_port structure for a port, as well as enabling the port on + * the board. + * Inputs: board, aiop, chan numbers + */ +static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) +{ + unsigned rocketMode; + struct r_port *info; + int line; + CONTROLLER_T *ctlp; + + /* Get the next available line number */ + line = SetLineNumber(board, aiop, chan); + + ctlp = sCtlNumToCtlPtr(board); + + /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ + info = kzalloc(sizeof (struct r_port), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", + line); + return; + } + + info->magic = RPORT_MAGIC; + info->line = line; + info->ctlp = ctlp; + info->board = board; + info->aiop = aiop; + info->chan = chan; + tty_port_init(&info->port); + info->port.ops = &rocket_port_ops; + init_completion(&info->close_wait); + info->flags &= ~ROCKET_MODE_MASK; + switch (pc104[board][line]) { + case 422: + info->flags |= ROCKET_MODE_RS422; + break; + case 485: + info->flags |= ROCKET_MODE_RS485; + break; + case 232: + default: + info->flags |= ROCKET_MODE_RS232; + break; + } + + info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; + if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { + printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", + board, aiop, chan); + kfree(info); + return; + } + + rocketMode = info->flags & ROCKET_MODE_MASK; + + if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) + sEnRTSToggle(&info->channel); + else + sDisRTSToggle(&info->channel); + + if (ctlp->boardType == ROCKET_TYPE_PC104) { + switch (rocketMode) { + case ROCKET_MODE_RS485: + sSetInterfaceMode(&info->channel, InterfaceModeRS485); + break; + case ROCKET_MODE_RS422: + sSetInterfaceMode(&info->channel, InterfaceModeRS422); + break; + case ROCKET_MODE_RS232: + default: + if (info->flags & ROCKET_RTS_TOGGLE) + sSetInterfaceMode(&info->channel, InterfaceModeRS232T); + else + sSetInterfaceMode(&info->channel, InterfaceModeRS232); + break; + } + } + spin_lock_init(&info->slock); + mutex_init(&info->write_mtx); + rp_table[line] = info; + tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev : + NULL); +} + +/* + * Configures a rocketport port according to its termio settings. Called from + * user mode into the driver (exception handler). *info CD manipulation is spinlock protected. + */ +static void configure_r_port(struct tty_struct *tty, struct r_port *info, + struct ktermios *old_termios) +{ + unsigned cflag; + unsigned long flags; + unsigned rocketMode; + int bits, baud, divisor; + CHANNEL_t *cp; + struct ktermios *t = tty->termios; + + cp = &info->channel; + cflag = t->c_cflag; + + /* Byte size and parity */ + if ((cflag & CSIZE) == CS8) { + sSetData8(cp); + bits = 10; + } else { + sSetData7(cp); + bits = 9; + } + if (cflag & CSTOPB) { + sSetStop2(cp); + bits++; + } else { + sSetStop1(cp); + } + + if (cflag & PARENB) { + sEnParity(cp); + bits++; + if (cflag & PARODD) { + sSetOddParity(cp); + } else { + sSetEvenParity(cp); + } + } else { + sDisParity(cp); + } + + /* baud rate */ + baud = tty_get_baud_rate(tty); + if (!baud) + baud = 9600; + divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1; + if ((divisor >= 8192 || divisor < 0) && old_termios) { + baud = tty_termios_baud_rate(old_termios); + if (!baud) + baud = 9600; + divisor = (rp_baud_base[info->board] / baud) - 1; + } + if (divisor >= 8192 || divisor < 0) { + baud = 9600; + divisor = (rp_baud_base[info->board] / baud) - 1; + } + info->cps = baud / bits; + sSetBaud(cp, divisor); + + /* FIXME: Should really back compute a baud rate from the divisor */ + tty_encode_baud_rate(tty, baud, baud); + + if (cflag & CRTSCTS) { + info->intmask |= DELTA_CTS; + sEnCTSFlowCtl(cp); + } else { + info->intmask &= ~DELTA_CTS; + sDisCTSFlowCtl(cp); + } + if (cflag & CLOCAL) { + info->intmask &= ~DELTA_CD; + } else { + spin_lock_irqsave(&info->slock, flags); + if (sGetChanStatus(cp) & CD_ACT) + info->cd_status = 1; + else + info->cd_status = 0; + info->intmask |= DELTA_CD; + spin_unlock_irqrestore(&info->slock, flags); + } + + /* + * Handle software flow control in the board + */ +#ifdef ROCKET_SOFT_FLOW + if (I_IXON(tty)) { + sEnTxSoftFlowCtl(cp); + if (I_IXANY(tty)) { + sEnIXANY(cp); + } else { + sDisIXANY(cp); + } + sSetTxXONChar(cp, START_CHAR(tty)); + sSetTxXOFFChar(cp, STOP_CHAR(tty)); + } else { + sDisTxSoftFlowCtl(cp); + sDisIXANY(cp); + sClrTxXOFF(cp); + } +#endif + + /* + * Set up ignore/read mask words + */ + info->read_status_mask = STMRCVROVRH | 0xFF; + if (I_INPCK(tty)) + info->read_status_mask |= STMFRAMEH | STMPARITYH; + if (I_BRKINT(tty) || I_PARMRK(tty)) + info->read_status_mask |= STMBREAKH; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(tty)) + info->ignore_status_mask |= STMFRAMEH | STMPARITYH; + if (I_IGNBRK(tty)) { + info->ignore_status_mask |= STMBREAKH; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too. (For real raw support). + */ + if (I_IGNPAR(tty)) + info->ignore_status_mask |= STMRCVROVRH; + } + + rocketMode = info->flags & ROCKET_MODE_MASK; + + if ((info->flags & ROCKET_RTS_TOGGLE) + || (rocketMode == ROCKET_MODE_RS485)) + sEnRTSToggle(cp); + else + sDisRTSToggle(cp); + + sSetRTS(&info->channel); + + if (cp->CtlP->boardType == ROCKET_TYPE_PC104) { + switch (rocketMode) { + case ROCKET_MODE_RS485: + sSetInterfaceMode(cp, InterfaceModeRS485); + break; + case ROCKET_MODE_RS422: + sSetInterfaceMode(cp, InterfaceModeRS422); + break; + case ROCKET_MODE_RS232: + default: + if (info->flags & ROCKET_RTS_TOGGLE) + sSetInterfaceMode(cp, InterfaceModeRS232T); + else + sSetInterfaceMode(cp, InterfaceModeRS232); + break; + } + } +} + +static int carrier_raised(struct tty_port *port) +{ + struct r_port *info = container_of(port, struct r_port, port); + return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; +} + +static void dtr_rts(struct tty_port *port, int on) +{ + struct r_port *info = container_of(port, struct r_port, port); + if (on) { + sSetDTR(&info->channel); + sSetRTS(&info->channel); + } else { + sClrDTR(&info->channel); + sClrRTS(&info->channel); + } +} + +/* + * Exception handler that opens a serial port. Creates xmit_buf storage, fills in + * port's r_port struct. Initializes the port hardware. + */ +static int rp_open(struct tty_struct *tty, struct file *filp) +{ + struct r_port *info; + struct tty_port *port; + int line = 0, retval; + CHANNEL_t *cp; + unsigned long page; + + line = tty->index; + if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL)) + return -ENXIO; + port = &info->port; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + if (port->flags & ASYNC_CLOSING) { + retval = wait_for_completion_interruptible(&info->close_wait); + free_page(page); + if (retval) + return retval; + return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); + } + + /* + * We must not sleep from here until the port is marked fully in use. + */ + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + + tty->driver_data = info; + tty_port_tty_set(port, tty); + + if (port->count++ == 0) { + atomic_inc(&rp_num_ports_open); + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rocket mod++ = %d...\n", + atomic_read(&rp_num_ports_open)); +#endif + } +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count); +#endif + + /* + * Info->count is now 1; so it's safe to sleep now. + */ + if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { + cp = &info->channel; + sSetRxTrigger(cp, TRIG_1); + if (sGetChanStatus(cp) & CD_ACT) + info->cd_status = 1; + else + info->cd_status = 0; + sDisRxStatusMode(cp); + sFlushRxFIFO(cp); + sFlushTxFIFO(cp); + + sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sSetRxTrigger(cp, TRIG_1); + + sGetChanStatus(cp); + sDisRxStatusMode(cp); + sClrTxXOFF(cp); + + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + + sEnRxFIFO(cp); + sEnTransmit(cp); + + set_bit(ASYNCB_INITIALIZED, &info->port.flags); + + /* + * Set up the tty->alt_speed kludge + */ + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + tty->alt_speed = 460800; + + configure_r_port(tty, info, NULL); + if (tty->termios->c_cflag & CBAUD) { + sSetDTR(cp); + sSetRTS(cp); + } + } + /* Starts (or resets) the maint polling loop */ + mod_timer(&rocket_timer, jiffies + POLL_PERIOD); + + retval = tty_port_block_til_ready(port, tty, filp); + if (retval) { +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval); +#endif + return retval; + } + return 0; +} + +/* + * Exception handler that closes a serial port. info->port.count is considered critical. + */ +static void rp_close(struct tty_struct *tty, struct file *filp) +{ + struct r_port *info = tty->driver_data; + struct tty_port *port = &info->port; + int timeout; + CHANNEL_t *cp; + + if (rocket_paranoia_check(info, "rp_close")) + return; + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count); +#endif + + if (tty_port_close_start(port, tty, filp) == 0) + return; + + mutex_lock(&port->mutex); + cp = &info->channel; + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps; + if (timeout == 0) + timeout = 1; + rp_wait_until_sent(tty, timeout); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + sDisTransmit(cp); + sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + sClrTxXOFF(cp); + sFlushRxFIFO(cp); + sFlushTxFIFO(cp); + sClrRTS(cp); + if (C_HUPCL(tty)) + sClrDTR(cp); + + rp_flush_buffer(tty); + + tty_ldisc_flush(tty); + + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + /* We can't yet use tty_port_close_end as the buffer handling in this + driver is a bit different to the usual */ + + if (port->blocked_open) { + if (port->close_delay) { + msleep_interruptible(jiffies_to_msecs(port->close_delay)); + } + wake_up_interruptible(&port->open_wait); + } else { + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = NULL; + } + } + spin_lock_irq(&port->lock); + info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); + tty->closing = 0; + spin_unlock_irq(&port->lock); + mutex_unlock(&port->mutex); + tty_port_tty_set(port, NULL); + + wake_up_interruptible(&port->close_wait); + complete_all(&info->close_wait); + atomic_dec(&rp_num_ports_open); + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rocket mod-- = %d...\n", + atomic_read(&rp_num_ports_open)); + printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line); +#endif + +} + +static void rp_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned cflag; + + if (rocket_paranoia_check(info, "rp_set_termios")) + return; + + cflag = tty->termios->c_cflag; + + /* + * This driver doesn't support CS5 or CS6 + */ + if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) + tty->termios->c_cflag = + ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); + /* Or CMSPAR */ + tty->termios->c_cflag &= ~CMSPAR; + + configure_r_port(tty, info, old_termios); + + cp = &info->channel; + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { + sClrDTR(cp); + sClrRTS(cp); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { + if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) + sSetRTS(cp); + sSetDTR(cp); + } + + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rp_start(tty); + } +} + +static int rp_break(struct tty_struct *tty, int break_state) +{ + struct r_port *info = tty->driver_data; + unsigned long flags; + + if (rocket_paranoia_check(info, "rp_break")) + return -EINVAL; + + spin_lock_irqsave(&info->slock, flags); + if (break_state == -1) + sSendBreak(&info->channel); + else + sClrBreak(&info->channel); + spin_unlock_irqrestore(&info->slock, flags); + return 0; +} + +/* + * sGetChanRI used to be a macro in rocket_int.h. When the functionality for + * the UPCI boards was added, it was decided to make this a function because + * the macro was getting too complicated. All cases except the first one + * (UPCIRingInd) are taken directly from the original macro. + */ +static int sGetChanRI(CHANNEL_T * ChP) +{ + CONTROLLER_t *CtlP = ChP->CtlP; + int ChanNum = ChP->ChanNum; + int RingInd = 0; + + if (CtlP->UPCIRingInd) + RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]); + else if (CtlP->AltChanRingIndicator) + RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT; + else if (CtlP->boardType == ROCKET_TYPE_PC104) + RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]); + + return RingInd; +} + +/********************************************************************************************/ +/* Here are the routines used by rp_ioctl. These are all called from exception handlers. */ + +/* + * Returns the state of the serial modem control lines. These next 2 functions + * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. + */ +static int rp_tiocmget(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + unsigned int control, result, ChanStatus; + + ChanStatus = sGetChanStatusLo(&info->channel); + control = info->channel.TxControl[3]; + result = ((control & SET_RTS) ? TIOCM_RTS : 0) | + ((control & SET_DTR) ? TIOCM_DTR : 0) | + ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | + (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) | + ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | + ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0); + + return result; +} + +/* + * Sets the modem control lines + */ +static int rp_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct r_port *info = tty->driver_data; + + if (set & TIOCM_RTS) + info->channel.TxControl[3] |= SET_RTS; + if (set & TIOCM_DTR) + info->channel.TxControl[3] |= SET_DTR; + if (clear & TIOCM_RTS) + info->channel.TxControl[3] &= ~SET_RTS; + if (clear & TIOCM_DTR) + info->channel.TxControl[3] &= ~SET_DTR; + + out32(info->channel.IndexAddr, info->channel.TxControl); + return 0; +} + +static int get_config(struct r_port *info, struct rocket_config __user *retinfo) +{ + struct rocket_config tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof (tmp)); + mutex_lock(&info->port.mutex); + tmp.line = info->line; + tmp.flags = info->flags; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; + tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; + mutex_unlock(&info->port.mutex); + + if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) + return -EFAULT; + return 0; +} + +static int set_config(struct tty_struct *tty, struct r_port *info, + struct rocket_config __user *new_info) +{ + struct rocket_config new_serial; + + if (copy_from_user(&new_serial, new_info, sizeof (new_serial))) + return -EFAULT; + + mutex_lock(&info->port.mutex); + if (!capable(CAP_SYS_ADMIN)) + { + if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) { + mutex_unlock(&info->port.mutex); + return -EPERM; + } + info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); + configure_r_port(tty, info, NULL); + mutex_unlock(&info->port.mutex); + return 0; + } + + info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; + + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + tty->alt_speed = 460800; + mutex_unlock(&info->port.mutex); + + configure_r_port(tty, info, NULL); + return 0; +} + +/* + * This function fills in a rocket_ports struct with information + * about what boards/ports are in the system. This info is passed + * to user space. See setrocket.c where the info is used to create + * the /dev/ttyRx ports. + */ +static int get_ports(struct r_port *info, struct rocket_ports __user *retports) +{ + struct rocket_ports tmp; + int board; + + if (!retports) + return -EFAULT; + memset(&tmp, 0, sizeof (tmp)); + tmp.tty_major = rocket_driver->major; + + for (board = 0; board < 4; board++) { + tmp.rocketModel[board].model = rocketModel[board].model; + strcpy(tmp.rocketModel[board].modelString, rocketModel[board].modelString); + tmp.rocketModel[board].numPorts = rocketModel[board].numPorts; + tmp.rocketModel[board].loadrm2 = rocketModel[board].loadrm2; + tmp.rocketModel[board].startingPortNumber = rocketModel[board].startingPortNumber; + } + if (copy_to_user(retports, &tmp, sizeof (*retports))) + return -EFAULT; + return 0; +} + +static int reset_rm2(struct r_port *info, void __user *arg) +{ + int reset; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&reset, arg, sizeof (int))) + return -EFAULT; + if (reset) + reset = 1; + + if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII && + rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII) + return -EINVAL; + + if (info->ctlp->BusType == isISA) + sModemReset(info->ctlp, info->chan, reset); + else + sPCIModemReset(info->ctlp, info->chan, reset); + + return 0; +} + +static int get_version(struct r_port *info, struct rocket_version __user *retvers) +{ + if (copy_to_user(retvers, &driver_version, sizeof (*retvers))) + return -EFAULT; + return 0; +} + +/* IOCTL call handler into the driver */ +static int rp_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct r_port *info = tty->driver_data; + void __user *argp = (void __user *)arg; + int ret = 0; + + if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl")) + return -ENXIO; + + switch (cmd) { + case RCKP_GET_STRUCT: + if (copy_to_user(argp, info, sizeof (struct r_port))) + ret = -EFAULT; + break; + case RCKP_GET_CONFIG: + ret = get_config(info, argp); + break; + case RCKP_SET_CONFIG: + ret = set_config(tty, info, argp); + break; + case RCKP_GET_PORTS: + ret = get_ports(info, argp); + break; + case RCKP_RESET_RM2: + ret = reset_rm2(info, argp); + break; + case RCKP_GET_VERSION: + ret = get_version(info, argp); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void rp_send_xchar(struct tty_struct *tty, char ch) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + + if (rocket_paranoia_check(info, "rp_send_xchar")) + return; + + cp = &info->channel; + if (sGetTxCnt(cp)) + sWriteTxPrioByte(cp, ch); + else + sWriteTxByte(sGetTxRxDataIO(cp), ch); +} + +static void rp_throttle(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + +#ifdef ROCKET_DEBUG_THROTTLE + printk(KERN_INFO "throttle %s: %d....\n", tty->name, + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (rocket_paranoia_check(info, "rp_throttle")) + return; + + cp = &info->channel; + if (I_IXOFF(tty)) + rp_send_xchar(tty, STOP_CHAR(tty)); + + sClrRTS(&info->channel); +} + +static void rp_unthrottle(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; +#ifdef ROCKET_DEBUG_THROTTLE + printk(KERN_INFO "unthrottle %s: %d....\n", tty->name, + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (rocket_paranoia_check(info, "rp_throttle")) + return; + + cp = &info->channel; + if (I_IXOFF(tty)) + rp_send_xchar(tty, START_CHAR(tty)); + + sSetRTS(&info->channel); +} + +/* + * ------------------------------------------------------------ + * rp_stop() and rp_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rp_stop(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + +#ifdef ROCKET_DEBUG_FLOW + printk(KERN_INFO "stop %s: %d %d....\n", tty->name, + info->xmit_cnt, info->xmit_fifo_room); +#endif + + if (rocket_paranoia_check(info, "rp_stop")) + return; + + if (sGetTxCnt(&info->channel)) + sDisTransmit(&info->channel); +} + +static void rp_start(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + +#ifdef ROCKET_DEBUG_FLOW + printk(KERN_INFO "start %s: %d %d....\n", tty->name, + info->xmit_cnt, info->xmit_fifo_room); +#endif + + if (rocket_paranoia_check(info, "rp_stop")) + return; + + sEnTransmit(&info->channel); + set_bit((info->aiop * 8) + info->chan, + (void *) &xmit_flags[info->board]); +} + +/* + * rp_wait_until_sent() --- wait until the transmitter is empty + */ +static void rp_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned long orig_jiffies; + int check_time, exit_time; + int txcnt; + + if (rocket_paranoia_check(info, "rp_wait_until_sent")) + return; + + cp = &info->channel; + + orig_jiffies = jiffies; +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout, + jiffies); + printk(KERN_INFO "cps=%d...\n", info->cps); +#endif + while (1) { + txcnt = sGetTxCnt(cp); + if (!txcnt) { + if (sGetChanStatusLo(cp) & TXSHRMT) + break; + check_time = (HZ / info->cps) / 5; + } else { + check_time = HZ * txcnt / info->cps; + } + if (timeout) { + exit_time = orig_jiffies + timeout - jiffies; + if (exit_time <= 0) + break; + if (exit_time < check_time) + check_time = exit_time; + } + if (check_time == 0) + check_time = 1; +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, + jiffies, check_time); +#endif + msleep_interruptible(jiffies_to_msecs(check_time)); + if (signal_pending(current)) + break; + } + __set_current_state(TASK_RUNNING); +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); +#endif +} + +/* + * rp_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rp_hangup(struct tty_struct *tty) +{ + CHANNEL_t *cp; + struct r_port *info = tty->driver_data; + unsigned long flags; + + if (rocket_paranoia_check(info, "rp_hangup")) + return; + +#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) + printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line); +#endif + rp_flush_buffer(tty); + spin_lock_irqsave(&info->port.lock, flags); + if (info->port.flags & ASYNC_CLOSING) { + spin_unlock_irqrestore(&info->port.lock, flags); + return; + } + if (info->port.count) + atomic_dec(&rp_num_ports_open); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + spin_unlock_irqrestore(&info->port.lock, flags); + + tty_port_hangup(&info->port); + + cp = &info->channel; + sDisRxFIFO(cp); + sDisTransmit(cp); + sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + sClrTxXOFF(cp); + clear_bit(ASYNCB_INITIALIZED, &info->port.flags); + + wake_up_interruptible(&info->port.open_wait); +} + +/* + * Exception handler - write char routine. The RocketPort driver uses a + * double-buffering strategy, with the twist that if the in-memory CPU + * buffer is empty, and there's space in the transmit FIFO, the + * writing routines will write directly to transmit FIFO. + * Write buffer and counters protected by spinlocks + */ +static int rp_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned long flags; + + if (rocket_paranoia_check(info, "rp_put_char")) + return 0; + + /* + * Grab the port write mutex, locking out other processes that try to + * write to this port + */ + mutex_lock(&info->write_mtx); + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "rp_put_char %c...\n", ch); +#endif + + spin_lock_irqsave(&info->slock, flags); + cp = &info->channel; + + if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= XMIT_BUF_SIZE - 1; + info->xmit_cnt++; + set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + } else { + sOutB(sGetTxRxDataIO(cp), ch); + info->xmit_fifo_room--; + } + spin_unlock_irqrestore(&info->slock, flags); + mutex_unlock(&info->write_mtx); + return 1; +} + +/* + * Exception handler - write routine, called when user app writes to the device. + * A per port write mutex is used to protect from another process writing to + * this port at the same time. This other process could be running on the other CPU + * or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). + * Spinlocks protect the info xmit members. + */ +static int rp_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + const unsigned char *b; + int c, retval = 0; + unsigned long flags; + + if (count <= 0 || rocket_paranoia_check(info, "rp_write")) + return 0; + + if (mutex_lock_interruptible(&info->write_mtx)) + return -ERESTARTSYS; + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "rp_write %d chars...\n", count); +#endif + cp = &info->channel; + + if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + /* + * If the write queue for the port is empty, and there is FIFO space, stuff bytes + * into FIFO. Use the write queue for temp storage. + */ + if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { + c = min(count, info->xmit_fifo_room); + b = buf; + + /* Push data into FIFO, 2 bytes at a time */ + sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2); + + /* If there is a byte remaining, write it */ + if (c & 1) + sOutB(sGetTxRxDataIO(cp), b[c - 1]); + + retval += c; + buf += c; + count -= c; + + spin_lock_irqsave(&info->slock, flags); + info->xmit_fifo_room -= c; + spin_unlock_irqrestore(&info->slock, flags); + } + + /* If count is zero, we wrote it all and are done */ + if (!count) + goto end; + + /* Write remaining data into the port's xmit_buf */ + while (1) { + /* Hung up ? */ + if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags)) + goto end; + c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); + c = min(c, XMIT_BUF_SIZE - info->xmit_head); + if (c <= 0) + break; + + b = buf; + memcpy(info->xmit_buf + info->xmit_head, b, c); + + spin_lock_irqsave(&info->slock, flags); + info->xmit_head = + (info->xmit_head + c) & (XMIT_BUF_SIZE - 1); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->slock, flags); + + buf += c; + count -= c; + retval += c; + } + + if ((retval > 0) && !tty->stopped && !tty->hw_stopped) + set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + +end: + if (info->xmit_cnt < WAKEUP_CHARS) { + tty_wakeup(tty); +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } + mutex_unlock(&info->write_mtx); + return retval; +} + +/* + * Return the number of characters that can be sent. We estimate + * only using the in-memory transmit buffer only, and ignore the + * potential space in the transmit FIFO. + */ +static int rp_write_room(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + int ret; + + if (rocket_paranoia_check(info, "rp_write_room")) + return 0; + + ret = XMIT_BUF_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "rp_write_room returns %d...\n", ret); +#endif + return ret; +} + +/* + * Return the number of characters in the buffer. Again, this only + * counts those characters in the in-memory transmit buffer. + */ +static int rp_chars_in_buffer(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + + if (rocket_paranoia_check(info, "rp_chars_in_buffer")) + return 0; + + cp = &info->channel; + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); +#endif + return info->xmit_cnt; +} + +/* + * Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the + * r_port struct for the port. Note that spinlock are used to protect info members, + * do not call this function if the spinlock is already held. + */ +static void rp_flush_buffer(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned long flags; + + if (rocket_paranoia_check(info, "rp_flush_buffer")) + return; + + spin_lock_irqsave(&info->slock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&info->slock, flags); + +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + tty_wakeup(tty); + + cp = &info->channel; + sFlushTxFIFO(cp); +} + +#ifdef CONFIG_PCI + +static struct pci_device_id __devinitdata __used rocket_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(pci, rocket_pci_ids); + +/* + * Called when a PCI card is found. Retrieves and stores model information, + * init's aiopic and serial port hardware. + * Inputs: i is the board number (0-n) + */ +static __init int register_PCI(int i, struct pci_dev *dev) +{ + int num_aiops, aiop, max_num_aiops, num_chan, chan; + unsigned int aiopio[MAX_AIOPS_PER_BOARD]; + char *str, *board_type; + CONTROLLER_t *ctlp; + + int fast_clock = 0; + int altChanRingIndicator = 0; + int ports_per_aiop = 8; + WordIO_t ConfigIO = 0; + ByteIO_t UPCIRingInd = 0; + + if (!dev || pci_enable_device(dev)) + return 0; + + rcktpt_io_addr[i] = pci_resource_start(dev, 0); + + rcktpt_type[i] = ROCKET_TYPE_NORMAL; + rocketModel[i].loadrm2 = 0; + rocketModel[i].startingPortNumber = nextLineNumber; + + /* Depending on the model, set up some config variables */ + switch (dev->device) { + case PCI_DEVICE_ID_RP4QUAD: + str = "Quadcable"; + max_num_aiops = 1; + ports_per_aiop = 4; + rocketModel[i].model = MODEL_RP4QUAD; + strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RP8OCTA: + str = "Octacable"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8OCTA; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_URP8OCTA: + str = "Octacable"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RP8OCTA; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP8INTF: + str = "8"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8INTF; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_URP8INTF: + str = "8"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RP8INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP8J: + str = "8J"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8J; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP4J: + str = "4J"; + max_num_aiops = 1; + ports_per_aiop = 4; + rocketModel[i].model = MODEL_RP4J; + strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RP8SNI: + str = "8 (DB78 Custom)"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8SNI; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP16SNI: + str = "16 (DB78 Custom)"; + max_num_aiops = 2; + rocketModel[i].model = MODEL_RP16SNI; + strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_RP16INTF: + str = "16"; + max_num_aiops = 2; + rocketModel[i].model = MODEL_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_URP16INTF: + str = "16"; + max_num_aiops = 2; + rocketModel[i].model = MODEL_UPCI_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_CRP16INTF: + str = "16"; + max_num_aiops = 2; + rocketModel[i].model = MODEL_CPCI_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_RP32INTF: + str = "32"; + max_num_aiops = 4; + rocketModel[i].model = MODEL_RP32INTF; + strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); + rocketModel[i].numPorts = 32; + break; + case PCI_DEVICE_ID_URP32INTF: + str = "32"; + max_num_aiops = 4; + rocketModel[i].model = MODEL_UPCI_RP32INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); + rocketModel[i].numPorts = 32; + break; + case PCI_DEVICE_ID_RPP4: + str = "Plus Quadcable"; + max_num_aiops = 1; + ports_per_aiop = 4; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RPP4; + strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RPP8: + str = "Plus Octacable"; + max_num_aiops = 2; + ports_per_aiop = 4; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RPP8; + strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP2_232: + str = "Plus 2 (RS-232)"; + max_num_aiops = 1; + ports_per_aiop = 2; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RP2_232; + strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232"); + rocketModel[i].numPorts = 2; + break; + case PCI_DEVICE_ID_RP2_422: + str = "Plus 2 (RS-422)"; + max_num_aiops = 1; + ports_per_aiop = 2; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RP2_422; + strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422"); + rocketModel[i].numPorts = 2; + break; + case PCI_DEVICE_ID_RP6M: + + max_num_aiops = 1; + ports_per_aiop = 6; + str = "6-port"; + + /* If revision is 1, the rocketmodem flash must be loaded. + * If it is 2 it is a "socketed" version. */ + if (dev->revision == 1) { + rcktpt_type[i] = ROCKET_TYPE_MODEMII; + rocketModel[i].loadrm2 = 1; + } else { + rcktpt_type[i] = ROCKET_TYPE_MODEM; + } + + rocketModel[i].model = MODEL_RP6M; + strcpy(rocketModel[i].modelString, "RocketModem 6 port"); + rocketModel[i].numPorts = 6; + break; + case PCI_DEVICE_ID_RP4M: + max_num_aiops = 1; + ports_per_aiop = 4; + str = "4-port"; + if (dev->revision == 1) { + rcktpt_type[i] = ROCKET_TYPE_MODEMII; + rocketModel[i].loadrm2 = 1; + } else { + rcktpt_type[i] = ROCKET_TYPE_MODEM; + } + + rocketModel[i].model = MODEL_RP4M; + strcpy(rocketModel[i].modelString, "RocketModem 4 port"); + rocketModel[i].numPorts = 4; + break; + default: + str = "(unknown/unsupported)"; + max_num_aiops = 0; + break; + } + + /* + * Check for UPCI boards. + */ + + switch (dev->device) { + case PCI_DEVICE_ID_URP32INTF: + case PCI_DEVICE_ID_URP8INTF: + case PCI_DEVICE_ID_URP16INTF: + case PCI_DEVICE_ID_CRP16INTF: + case PCI_DEVICE_ID_URP8OCTA: + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + ConfigIO = pci_resource_start(dev, 1); + if (dev->device == PCI_DEVICE_ID_URP8OCTA) { + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + + /* + * Check for octa or quad cable. + */ + if (! + (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & + PCI_GPIO_CTRL_8PORT)) { + str = "Quadcable"; + ports_per_aiop = 4; + rocketModel[i].numPorts = 4; + } + } + break; + case PCI_DEVICE_ID_UPCI_RM3_8PORT: + str = "8 ports"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RM3_8PORT; + strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); + rocketModel[i].numPorts = 8; + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + ConfigIO = pci_resource_start(dev, 1); + rcktpt_type[i] = ROCKET_TYPE_MODEMIII; + break; + case PCI_DEVICE_ID_UPCI_RM3_4PORT: + str = "4 ports"; + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RM3_4PORT; + strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); + rocketModel[i].numPorts = 4; + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + ConfigIO = pci_resource_start(dev, 1); + rcktpt_type[i] = ROCKET_TYPE_MODEMIII; + break; + default: + break; + } + + switch (rcktpt_type[i]) { + case ROCKET_TYPE_MODEM: + board_type = "RocketModem"; + break; + case ROCKET_TYPE_MODEMII: + board_type = "RocketModem II"; + break; + case ROCKET_TYPE_MODEMIII: + board_type = "RocketModem III"; + break; + default: + board_type = "RocketPort"; + break; + } + + if (fast_clock) { + sClockPrescale = 0x12; /* mod 2 (divide by 3) */ + rp_baud_base[i] = 921600; + } else { + /* + * If support_low_speed is set, use the slow clock + * prescale, which supports 50 bps + */ + if (support_low_speed) { + /* mod 9 (divide by 10) prescale */ + sClockPrescale = 0x19; + rp_baud_base[i] = 230400; + } else { + /* mod 4 (devide by 5) prescale */ + sClockPrescale = 0x14; + rp_baud_base[i] = 460800; + } + } + + for (aiop = 0; aiop < max_num_aiops; aiop++) + aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40); + ctlp = sCtlNumToCtlPtr(i); + num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd); + for (aiop = 0; aiop < max_num_aiops; aiop++) + ctlp->AiopNumChan[aiop] = ports_per_aiop; + + dev_info(&dev->dev, "comtrol PCI controller #%d found at " + "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", + i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, + rocketModel[i].startingPortNumber, + rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); + + if (num_aiops <= 0) { + rcktpt_io_addr[i] = 0; + return (0); + } + is_PCI[i] = 1; + + /* Reset the AIOPIC, init the serial ports */ + for (aiop = 0; aiop < num_aiops; aiop++) { + sResetAiopByNum(ctlp, aiop); + num_chan = ports_per_aiop; + for (chan = 0; chan < num_chan; chan++) + init_r_port(i, aiop, chan, dev); + } + + /* Rocket modems must be reset */ + if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || + (rcktpt_type[i] == ROCKET_TYPE_MODEMII) || + (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) { + num_chan = ports_per_aiop; + for (chan = 0; chan < num_chan; chan++) + sPCIModemReset(ctlp, chan, 1); + msleep(500); + for (chan = 0; chan < num_chan; chan++) + sPCIModemReset(ctlp, chan, 0); + msleep(500); + rmSpeakerReset(ctlp, rocketModel[i].model); + } + return (1); +} + +/* + * Probes for PCI cards, inits them if found + * Input: board_found = number of ISA boards already found, or the + * starting board number + * Returns: Number of PCI boards found + */ +static int __init init_PCI(int boards_found) +{ + struct pci_dev *dev = NULL; + int count = 0; + + /* Work through the PCI device list, pulling out ours */ + while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) { + if (register_PCI(count + boards_found, dev)) + count++; + } + return (count); +} + +#endif /* CONFIG_PCI */ + +/* + * Probes for ISA cards + * Input: i = the board number to look for + * Returns: 1 if board found, 0 else + */ +static int __init init_ISA(int i) +{ + int num_aiops, num_chan = 0, total_num_chan = 0; + int aiop, chan; + unsigned int aiopio[MAX_AIOPS_PER_BOARD]; + CONTROLLER_t *ctlp; + char *type_string; + + /* If io_addr is zero, no board configured */ + if (rcktpt_io_addr[i] == 0) + return (0); + + /* Reserve the IO region */ + if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { + printk(KERN_ERR "Unable to reserve IO region for configured " + "ISA RocketPort at address 0x%lx, board not " + "installed...\n", rcktpt_io_addr[i]); + rcktpt_io_addr[i] = 0; + return (0); + } + + ctlp = sCtlNumToCtlPtr(i); + + ctlp->boardType = rcktpt_type[i]; + + switch (rcktpt_type[i]) { + case ROCKET_TYPE_PC104: + type_string = "(PC104)"; + break; + case ROCKET_TYPE_MODEM: + type_string = "(RocketModem)"; + break; + case ROCKET_TYPE_MODEMII: + type_string = "(RocketModem II)"; + break; + default: + type_string = ""; + break; + } + + /* + * If support_low_speed is set, use the slow clock prescale, + * which supports 50 bps + */ + if (support_low_speed) { + sClockPrescale = 0x19; /* mod 9 (divide by 10) prescale */ + rp_baud_base[i] = 230400; + } else { + sClockPrescale = 0x14; /* mod 4 (devide by 5) prescale */ + rp_baud_base[i] = 460800; + } + + for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++) + aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400); + + num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); + + if (ctlp->boardType == ROCKET_TYPE_PC104) { + sEnAiop(ctlp, 2); /* only one AIOPIC, but these */ + sEnAiop(ctlp, 3); /* CSels used for other stuff */ + } + + /* If something went wrong initing the AIOP's release the ISA IO memory */ + if (num_aiops <= 0) { + release_region(rcktpt_io_addr[i], 64); + rcktpt_io_addr[i] = 0; + return (0); + } + + rocketModel[i].startingPortNumber = nextLineNumber; + + for (aiop = 0; aiop < num_aiops; aiop++) { + sResetAiopByNum(ctlp, aiop); + sEnAiop(ctlp, aiop); + num_chan = sGetAiopNumChan(ctlp, aiop); + total_num_chan += num_chan; + for (chan = 0; chan < num_chan; chan++) + init_r_port(i, aiop, chan, NULL); + } + is_PCI[i] = 0; + if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) { + num_chan = sGetAiopNumChan(ctlp, 0); + total_num_chan = num_chan; + for (chan = 0; chan < num_chan; chan++) + sModemReset(ctlp, chan, 1); + msleep(500); + for (chan = 0; chan < num_chan; chan++) + sModemReset(ctlp, chan, 0); + msleep(500); + strcpy(rocketModel[i].modelString, "RocketModem ISA"); + } else { + strcpy(rocketModel[i].modelString, "RocketPort ISA"); + } + rocketModel[i].numPorts = total_num_chan; + rocketModel[i].model = MODEL_ISA; + + printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", + i, rcktpt_io_addr[i], num_aiops, type_string); + + printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", + rocketModel[i].modelString, + rocketModel[i].startingPortNumber, + rocketModel[i].startingPortNumber + + rocketModel[i].numPorts - 1); + + return (1); +} + +static const struct tty_operations rocket_ops = { + .open = rp_open, + .close = rp_close, + .write = rp_write, + .put_char = rp_put_char, + .write_room = rp_write_room, + .chars_in_buffer = rp_chars_in_buffer, + .flush_buffer = rp_flush_buffer, + .ioctl = rp_ioctl, + .throttle = rp_throttle, + .unthrottle = rp_unthrottle, + .set_termios = rp_set_termios, + .stop = rp_stop, + .start = rp_start, + .hangup = rp_hangup, + .break_ctl = rp_break, + .send_xchar = rp_send_xchar, + .wait_until_sent = rp_wait_until_sent, + .tiocmget = rp_tiocmget, + .tiocmset = rp_tiocmset, +}; + +static const struct tty_port_operations rocket_port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, +}; + +/* + * The module "startup" routine; it's run when the module is loaded. + */ +static int __init rp_init(void) +{ + int ret = -ENOMEM, pci_boards_found, isa_boards_found, i; + + printk(KERN_INFO "RocketPort device driver module, version %s, %s\n", + ROCKET_VERSION, ROCKET_DATE); + + rocket_driver = alloc_tty_driver(MAX_RP_PORTS); + if (!rocket_driver) + goto err; + + /* + * If board 1 is non-zero, there is at least one ISA configured. If controller is + * zero, use the default controller IO address of board1 + 0x40. + */ + if (board1) { + if (controller == 0) + controller = board1 + 0x40; + } else { + controller = 0; /* Used as a flag, meaning no ISA boards */ + } + + /* If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */ + if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) { + printk(KERN_ERR "Unable to reserve IO region for first " + "configured ISA RocketPort controller 0x%lx. " + "Driver exiting\n", controller); + ret = -EBUSY; + goto err_tty; + } + + /* Store ISA variable retrieved from command line or .conf file. */ + rcktpt_io_addr[0] = board1; + rcktpt_io_addr[1] = board2; + rcktpt_io_addr[2] = board3; + rcktpt_io_addr[3] = board4; + + rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0]; + rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1]; + rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2]; + rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3]; + + /* + * Set up the tty driver structure and then register this + * driver with the tty layer. + */ + + rocket_driver->owner = THIS_MODULE; + rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV; + rocket_driver->name = "ttyR"; + rocket_driver->driver_name = "Comtrol RocketPort"; + rocket_driver->major = TTY_ROCKET_MAJOR; + rocket_driver->minor_start = 0; + rocket_driver->type = TTY_DRIVER_TYPE_SERIAL; + rocket_driver->subtype = SERIAL_TYPE_NORMAL; + rocket_driver->init_termios = tty_std_termios; + rocket_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rocket_driver->init_termios.c_ispeed = 9600; + rocket_driver->init_termios.c_ospeed = 9600; +#ifdef ROCKET_SOFT_FLOW + rocket_driver->flags |= TTY_DRIVER_REAL_RAW; +#endif + tty_set_operations(rocket_driver, &rocket_ops); + + ret = tty_register_driver(rocket_driver); + if (ret < 0) { + printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); + goto err_controller; + } + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major); +#endif + + /* + * OK, let's probe each of the controllers looking for boards. Any boards found + * will be initialized here. + */ + isa_boards_found = 0; + pci_boards_found = 0; + + for (i = 0; i < NUM_BOARDS; i++) { + if (init_ISA(i)) + isa_boards_found++; + } + +#ifdef CONFIG_PCI + if (isa_boards_found < NUM_BOARDS) + pci_boards_found = init_PCI(isa_boards_found); +#endif + + max_board = pci_boards_found + isa_boards_found; + + if (max_board == 0) { + printk(KERN_ERR "No rocketport ports found; unloading driver\n"); + ret = -ENXIO; + goto err_ttyu; + } + + return 0; +err_ttyu: + tty_unregister_driver(rocket_driver); +err_controller: + if (controller) + release_region(controller, 4); +err_tty: + put_tty_driver(rocket_driver); +err: + return ret; +} + + +static void rp_cleanup_module(void) +{ + int retval; + int i; + + del_timer_sync(&rocket_timer); + + retval = tty_unregister_driver(rocket_driver); + if (retval) + printk(KERN_ERR "Error %d while trying to unregister " + "rocketport driver\n", -retval); + + for (i = 0; i < MAX_RP_PORTS; i++) + if (rp_table[i]) { + tty_unregister_device(rocket_driver, i); + kfree(rp_table[i]); + } + + put_tty_driver(rocket_driver); + + for (i = 0; i < NUM_BOARDS; i++) { + if (rcktpt_io_addr[i] <= 0 || is_PCI[i]) + continue; + release_region(rcktpt_io_addr[i], 64); + } + if (controller) + release_region(controller, 4); +} + +/*************************************************************************** +Function: sInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t MudbacIO; Mudbac base I/O address. + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, + ByteIO_t * AiopIOList, int AiopIOListSize, + int IRQNum, Byte_t Frequency, int PeriodicOnly) +{ + int i; + ByteIO_t io; + int done; + + CtlP->AiopIntrBits = aiop_intr_bits; + CtlP->AltChanRingIndicator = 0; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isISA; + CtlP->MBaseIO = MudbacIO; + CtlP->MReg1IO = MudbacIO + 1; + CtlP->MReg2IO = MudbacIO + 2; + CtlP->MReg3IO = MudbacIO + 3; +#if 1 + CtlP->MReg2 = 0; /* interrupt disable */ + CtlP->MReg3 = 0; /* no periodic interrupts */ +#else + if (sIRQMap[IRQNum] == 0) { /* interrupts globally disabled */ + CtlP->MReg2 = 0; /* interrupt disable */ + CtlP->MReg3 = 0; /* no periodic interrupts */ + } else { + CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ + CtlP->MReg3 = Frequency; /* set frequency */ + if (PeriodicOnly) { /* periodic interrupt only */ + CtlP->MReg3 |= PERIODIC_ONLY; + } + } +#endif + sOutB(CtlP->MReg2IO, CtlP->MReg2); + sOutB(CtlP->MReg3IO, CtlP->MReg3); + sControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = done = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */ + sOutB(MudbacIO, (Byte_t) (io >> 6)); /* set up AIOP I/O in MUDBAC */ + if (done) + continue; + sEnAiop(CtlP, i); /* enable the AIOP */ + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + done = 1; /* done looking for AIOPs */ + else { + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + sDisAiop(CtlP, i); /* disable AIOP */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} + +/*************************************************************************** +Function: sPCIInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd) +{ + int i; + ByteIO_t io; + + CtlP->AltChanRingIndicator = altChanRingIndicator; + CtlP->UPCIRingInd = UPCIRingInd; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isPCI; /* controller release 1 */ + + if (ConfigIO) { + CtlP->isUPCI = 1; + CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; + CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; + CtlP->AiopIntrBits = upci_aiop_intr_bits; + } else { + CtlP->isUPCI = 0; + CtlP->PCIIO = + (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); + CtlP->AiopIntrBits = aiop_intr_bits; + } + + sPCIControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + break; /* done looking for AIOPs */ + + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} + +/*************************************************************************** +Function: sReadAiopID +Purpose: Read the AIOP idenfication number directly from an AIOP. +Call: sReadAiopID(io) + ByteIO_t io: AIOP base I/O address +Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X + is replace by an identifying number. + Flag AIOPID_NULL if no valid AIOP is found +Warnings: No context switches are allowed while executing this function. + +*/ +static int sReadAiopID(ByteIO_t io) +{ + Byte_t AiopID; /* ID byte from AIOP */ + + sOutB(io + _CMD_REG, RESET_ALL); /* reset AIOP */ + sOutB(io + _CMD_REG, 0x0); + AiopID = sInW(io + _CHN_STAT0) & 0x07; + if (AiopID == 0x06) + return (1); + else /* AIOP does not exist */ + return (-1); +} + +/*************************************************************************** +Function: sReadAiopNumChan +Purpose: Read the number of channels available in an AIOP directly from + an AIOP. +Call: sReadAiopNumChan(io) + WordIO_t io: AIOP base I/O address +Return: int: The number of channels available +Comments: The number of channels is determined by write/reads from identical + offsets within the SRAM address spaces for channels 0 and 4. + If the channel 4 space is mirrored to channel 0 it is a 4 channel + AIOP, otherwise it is an 8 channel. +Warnings: No context switches are allowed while executing this function. +*/ +static int sReadAiopNumChan(WordIO_t io) +{ + Word_t x; + static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 }; + + /* write to chan 0 SRAM */ + out32((DWordIO_t) io + _INDX_ADDR, R); + sOutW(io + _INDX_ADDR, 0); /* read from SRAM, chan 0 */ + x = sInW(io + _INDX_DATA); + sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */ + if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ + return (8); + else + return (4); +} + +/*************************************************************************** +Function: sInitChan +Purpose: Initialization of a channel and channel structure +Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) + CONTROLLER_T *CtlP; Ptr to controller structure + CHANNEL_T *ChP; Ptr to channel structure + int AiopNum; AIOP number within controller + int ChanNum; Channel number within AIOP +Return: int: 1 if initialization succeeded, 0 if it fails because channel + number exceeds number of channels available in AIOP. +Comments: This function must be called before a channel can be used. +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. +*/ +static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, + int ChanNum) +{ + int i; + WordIO_t AiopIO; + WordIO_t ChIOOff; + Byte_t *ChR; + Word_t ChOff; + static Byte_t R[4]; + int brd9600; + + if (ChanNum >= CtlP->AiopNumChan[AiopNum]) + return 0; /* exceeds num chans in AIOP */ + + /* Channel, AIOP, and controller identifiers */ + ChP->CtlP = CtlP; + ChP->ChanID = CtlP->AiopID[AiopNum]; + ChP->AiopNum = AiopNum; + ChP->ChanNum = ChanNum; + + /* Global direct addresses */ + AiopIO = CtlP->AiopIO[AiopNum]; + ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG; + ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN; + ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK; + ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR; + ChP->IndexData = AiopIO + _INDX_DATA; + + /* Channel direct addresses */ + ChIOOff = AiopIO + ChP->ChanNum * 2; + ChP->TxRxData = ChIOOff + _TD0; + ChP->ChanStat = ChIOOff + _CHN_STAT0; + ChP->TxRxCount = ChIOOff + _FIFO_CNT0; + ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0; + + /* Initialize the channel from the RData array */ + for (i = 0; i < RDATASIZE; i += 4) { + R[0] = RData[i]; + R[1] = RData[i + 1] + 0x10 * ChanNum; + R[2] = RData[i + 2]; + R[3] = RData[i + 3]; + out32(ChP->IndexAddr, R); + } + + ChR = ChP->R; + for (i = 0; i < RREGDATASIZE; i += 4) { + ChR[i] = RRegData[i]; + ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum; + ChR[i + 2] = RRegData[i + 2]; + ChR[i + 3] = RRegData[i + 3]; + } + + /* Indexed registers */ + ChOff = (Word_t) ChanNum *0x1000; + + if (sClockPrescale == 0x14) + brd9600 = 47; + else + brd9600 = 23; + + ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD); + ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8); + ChP->BaudDiv[2] = (Byte_t) brd9600; + ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8); + out32(ChP->IndexAddr, ChP->BaudDiv); + + ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL); + ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8); + ChP->TxControl[2] = 0; + ChP->TxControl[3] = 0; + out32(ChP->IndexAddr, ChP->TxControl); + + ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL); + ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8); + ChP->RxControl[2] = 0; + ChP->RxControl[3] = 0; + out32(ChP->IndexAddr, ChP->RxControl); + + ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS); + ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8); + ChP->TxEnables[2] = 0; + ChP->TxEnables[3] = 0; + out32(ChP->IndexAddr, ChP->TxEnables); + + ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1); + ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8); + ChP->TxCompare[2] = 0; + ChP->TxCompare[3] = 0; + out32(ChP->IndexAddr, ChP->TxCompare); + + ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1); + ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8); + ChP->TxReplace1[2] = 0; + ChP->TxReplace1[3] = 0; + out32(ChP->IndexAddr, ChP->TxReplace1); + + ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2); + ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8); + ChP->TxReplace2[2] = 0; + ChP->TxReplace2[3] = 0; + out32(ChP->IndexAddr, ChP->TxReplace2); + + ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; + ChP->TxFIFO = ChOff + _TX_FIFO; + + sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ + sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Tx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ + sOutW(ChP->IndexData, 0); + ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; + ChP->RxFIFO = ChOff + _RX_FIFO; + + sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ + sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Rx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ + sOutW(ChP->IndexData, 0); + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ + sOutW(ChP->IndexData, 0); + ChP->TxPrioCnt = ChOff + _TXP_CNT; + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt); + sOutB(ChP->IndexData, 0); + ChP->TxPrioPtr = ChOff + _TXP_PNTR; + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr); + sOutB(ChP->IndexData, 0); + ChP->TxPrioBuf = ChOff + _TXP_BUF; + sEnRxProcessor(ChP); /* start the Rx processor */ + + return 1; +} + +/*************************************************************************** +Function: sStopRxProcessor +Purpose: Stop the receive processor from processing a channel. +Call: sStopRxProcessor(ChP) + CHANNEL_T *ChP; Ptr to channel structure + +Comments: The receive processor can be started again with sStartRxProcessor(). + This function causes the receive processor to skip over the + stopped channel. It does not stop it from processing other channels. + +Warnings: No context switches are allowed while executing this function. + + Do not leave the receive processor stopped for more than one + character time. + + After calling this function a delay of 4 uS is required to ensure + that the receive processor is no longer processing this channel. +*/ +static void sStopRxProcessor(CHANNEL_T * ChP) +{ + Byte_t R[4]; + + R[0] = ChP->R[0]; + R[1] = ChP->R[1]; + R[2] = 0x0a; + R[3] = ChP->R[3]; + out32(ChP->IndexAddr, R); +} + +/*************************************************************************** +Function: sFlushRxFIFO +Purpose: Flush the Rx FIFO +Call: sFlushRxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: void +Comments: To prevent data from being enqueued or dequeued in the Tx FIFO + while it is being flushed the receive processor is stopped + and the transmitter is disabled. After these operations a + 4 uS delay is done before clearing the pointers to allow + the receive processor to stop. These items are handled inside + this function. +Warnings: No context switches are allowed while executing this function. +*/ +static void sFlushRxFIFO(CHANNEL_T * ChP) +{ + int i; + Byte_t Ch; /* channel number within AIOP */ + int RxFIFOEnabled; /* 1 if Rx FIFO enabled */ + + if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ + return; /* don't need to flush */ + + RxFIFOEnabled = 0; + if (ChP->R[0x32] == 0x08) { /* Rx FIFO is enabled */ + RxFIFOEnabled = 1; + sDisRxFIFO(ChP); /* disable it */ + for (i = 0; i < 2000 / 200; i++) /* delay 2 uS to allow proc to disable FIFO */ + sInB(ChP->IntChan); /* depends on bus i/o timing */ + } + sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ + Ch = (Byte_t) sGetChanNum(ChP); + sOutB(ChP->Cmd, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ + sOutB(ChP->Cmd, Ch); /* remove reset Rx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ + sOutW(ChP->IndexData, 0); + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ + sOutW(ChP->IndexData, 0); + if (RxFIFOEnabled) + sEnRxFIFO(ChP); /* enable Rx FIFO */ +} + +/*************************************************************************** +Function: sFlushTxFIFO +Purpose: Flush the Tx FIFO +Call: sFlushTxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: void +Comments: To prevent data from being enqueued or dequeued in the Tx FIFO + while it is being flushed the receive processor is stopped + and the transmitter is disabled. After these operations a + 4 uS delay is done before clearing the pointers to allow + the receive processor to stop. These items are handled inside + this function. +Warnings: No context switches are allowed while executing this function. +*/ +static void sFlushTxFIFO(CHANNEL_T * ChP) +{ + int i; + Byte_t Ch; /* channel number within AIOP */ + int TxEnabled; /* 1 if transmitter enabled */ + + if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ + return; /* don't need to flush */ + + TxEnabled = 0; + if (ChP->TxControl[3] & TX_ENABLE) { + TxEnabled = 1; + sDisTransmit(ChP); /* disable transmitter */ + } + sStopRxProcessor(ChP); /* stop Rx processor */ + for (i = 0; i < 4000 / 200; i++) /* delay 4 uS to allow proc to stop */ + sInB(ChP->IntChan); /* depends on bus i/o timing */ + Ch = (Byte_t) sGetChanNum(ChP); + sOutB(ChP->Cmd, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ + sOutB(ChP->Cmd, Ch); /* remove reset Tx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ + sOutW(ChP->IndexData, 0); + if (TxEnabled) + sEnTransmit(ChP); /* enable transmitter */ + sStartRxProcessor(ChP); /* restart Rx processor */ +} + +/*************************************************************************** +Function: sWriteTxPrioByte +Purpose: Write a byte of priority transmit data to a channel +Call: sWriteTxPrioByte(ChP,Data) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Data; The transmit data byte + +Return: int: 1 if the bytes is successfully written, otherwise 0. + +Comments: The priority byte is transmitted before any data in the Tx FIFO. + +Warnings: No context switches are allowed while executing this function. +*/ +static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data) +{ + Byte_t DWBuf[4]; /* buffer for double word writes */ + Word_t *WordPtr; /* must be far because Win SS != DS */ + register DWordIO_t IndexAddr; + + if (sGetTxCnt(ChP) > 1) { /* write it to Tx priority buffer */ + IndexAddr = ChP->IndexAddr; + sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt); /* get priority buffer status */ + if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */ + return (0); /* nothing sent */ + + WordPtr = (Word_t *) (&DWBuf[0]); + *WordPtr = ChP->TxPrioBuf; /* data byte address */ + + DWBuf[2] = Data; /* data byte value */ + out32(IndexAddr, DWBuf); /* write it out */ + + *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ + + DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ + DWBuf[3] = 0; /* priority buffer pointer */ + out32(IndexAddr, DWBuf); /* write it out */ + } else { /* write it to Tx FIFO */ + + sWriteTxByte(sGetTxRxDataIO(ChP), Data); + } + return (1); /* 1 byte sent */ +} + +/*************************************************************************** +Function: sEnInterrupts +Purpose: Enable one or more interrupts for a channel +Call: sEnInterrupts(ChP,Flags) + CHANNEL_T *ChP; Ptr to channel structure + Word_t Flags: Interrupt enable flags, can be any combination + of the following flags: + TXINT_EN: Interrupt on Tx FIFO empty + RXINT_EN: Interrupt on Rx FIFO at trigger level (see + sSetRxTrigger()) + SRCINT_EN: Interrupt on SRC (Special Rx Condition) + MCINT_EN: Interrupt on modem input change + CHANINT_EN: Allow channel interrupt signal to the AIOP's + Interrupt Channel Register. +Return: void +Comments: If an interrupt enable flag is set in Flags, that interrupt will be + enabled. If an interrupt enable flag is not set in Flags, that + interrupt will not be changed. Interrupts can be disabled with + function sDisInterrupts(). + + This function sets the appropriate bit for the channel in the AIOP's + Interrupt Mask Register if the CHANINT_EN flag is set. This allows + this channel's bit to be set in the AIOP's Interrupt Channel Register. + + Interrupts must also be globally enabled before channel interrupts + will be passed on to the host. This is done with function + sEnGlobalInt(). + + In some cases it may be desirable to disable interrupts globally but + enable channel interrupts. This would allow the global interrupt + status register to be used to determine which AIOPs need service. +*/ +static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags) +{ + Byte_t Mask; /* Interrupt Mask Register */ + + ChP->RxControl[2] |= + ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + + out32(ChP->IndexAddr, ChP->RxControl); + + ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN); + + out32(ChP->IndexAddr, ChP->TxControl); + + if (Flags & CHANINT_EN) { + Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; + sOutB(ChP->IntMask, Mask); + } +} + +/*************************************************************************** +Function: sDisInterrupts +Purpose: Disable one or more interrupts for a channel +Call: sDisInterrupts(ChP,Flags) + CHANNEL_T *ChP; Ptr to channel structure + Word_t Flags: Interrupt flags, can be any combination + of the following flags: + TXINT_EN: Interrupt on Tx FIFO empty + RXINT_EN: Interrupt on Rx FIFO at trigger level (see + sSetRxTrigger()) + SRCINT_EN: Interrupt on SRC (Special Rx Condition) + MCINT_EN: Interrupt on modem input change + CHANINT_EN: Disable channel interrupt signal to the + AIOP's Interrupt Channel Register. +Return: void +Comments: If an interrupt flag is set in Flags, that interrupt will be + disabled. If an interrupt flag is not set in Flags, that + interrupt will not be changed. Interrupts can be enabled with + function sEnInterrupts(). + + This function clears the appropriate bit for the channel in the AIOP's + Interrupt Mask Register if the CHANINT_EN flag is set. This blocks + this channel's bit from being set in the AIOP's Interrupt Channel + Register. +*/ +static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags) +{ + Byte_t Mask; /* Interrupt Mask Register */ + + ChP->RxControl[2] &= + ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + out32(ChP->IndexAddr, ChP->RxControl); + ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN); + out32(ChP->IndexAddr, ChP->TxControl); + + if (Flags & CHANINT_EN) { + Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; + sOutB(ChP->IntMask, Mask); + } +} + +static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode) +{ + sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum); +} + +/* + * Not an official SSCI function, but how to reset RocketModems. + * ISA bus version + */ +static void sModemReset(CONTROLLER_T * CtlP, int chan, int on) +{ + ByteIO_t addr; + Byte_t val; + + addr = CtlP->AiopIO[0] + 0x400; + val = sInB(CtlP->MReg3IO); + /* if AIOP[1] is not enabled, enable it */ + if ((val & 2) == 0) { + val = sInB(CtlP->MReg2IO); + sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03)); + sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6)); + } + + sEnAiop(CtlP, 1); + if (!on) + addr += 8; + sOutB(addr + chan, 0); /* apply or remove reset */ + sDisAiop(CtlP, 1); +} + +/* + * Not an official SSCI function, but how to reset RocketModems. + * PCI bus version + */ +static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) +{ + ByteIO_t addr; + + addr = CtlP->AiopIO[0] + 0x40; /* 2nd AIOP */ + if (!on) + addr += 8; + sOutB(addr + chan, 0); /* apply or remove reset */ +} + +/* Resets the speaker controller on RocketModem II and III devices */ +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) +{ + ByteIO_t addr; + + /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ + if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { + addr = CtlP->AiopIO[0] + 0x4F; + sOutB(addr, 0); + } + + /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ + if ((model == MODEL_UPCI_RM3_8PORT) + || (model == MODEL_UPCI_RM3_4PORT)) { + addr = CtlP->AiopIO[0] + 0x88; + sOutB(addr, 0); + } +} + +/* Returns the line number given the controller (board), aiop and channel number */ +static unsigned char GetLineNumber(int ctrl, int aiop, int ch) +{ + return lineNumbers[(ctrl << 5) | (aiop << 3) | ch]; +} + +/* + * Stores the line number associated with a given controller (board), aiop + * and channel number. + * Returns: The line number assigned + */ +static unsigned char SetLineNumber(int ctrl, int aiop, int ch) +{ + lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++; + return (nextLineNumber - 1); +} diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h new file mode 100644 index 000000000000..ec863f35f1a9 --- /dev/null +++ b/drivers/tty/rocket.h @@ -0,0 +1,111 @@ +/* + * rocket.h --- the exported interface of the rocket driver to its configuration program. + * + * Written by Theodore Ts'o, Copyright 1997. + * Copyright 1997 Comtrol Corporation. + * + */ + +/* Model Information Struct */ +typedef struct { + unsigned long model; + char modelString[80]; + unsigned long numPorts; + int loadrm2; + int startingPortNumber; +} rocketModel_t; + +struct rocket_config { + int line; + int flags; + int closing_wait; + int close_delay; + int port; + int reserved[32]; +}; + +struct rocket_ports { + int tty_major; + int callout_major; + rocketModel_t rocketModel[8]; +}; + +struct rocket_version { + char rocket_version[32]; + char rocket_date[32]; + char reserved[64]; +}; + +/* + * Rocketport flags + */ +/*#define ROCKET_CALLOUT_NOHUP 0x00000001 */ +#define ROCKET_FORCE_CD 0x00000002 +#define ROCKET_HUP_NOTIFY 0x00000004 +#define ROCKET_SPLIT_TERMIOS 0x00000008 +#define ROCKET_SPD_MASK 0x00000070 +#define ROCKET_SPD_HI 0x00000010 /* Use 56000 instead of 38400 bps */ +#define ROCKET_SPD_VHI 0x00000020 /* Use 115200 instead of 38400 bps */ +#define ROCKET_SPD_SHI 0x00000030 /* Use 230400 instead of 38400 bps */ +#define ROCKET_SPD_WARP 0x00000040 /* Use 460800 instead of 38400 bps */ +#define ROCKET_SAK 0x00000080 +#define ROCKET_SESSION_LOCKOUT 0x00000100 +#define ROCKET_PGRP_LOCKOUT 0x00000200 +#define ROCKET_RTS_TOGGLE 0x00000400 +#define ROCKET_MODE_MASK 0x00003000 +#define ROCKET_MODE_RS232 0x00000000 +#define ROCKET_MODE_RS485 0x00001000 +#define ROCKET_MODE_RS422 0x00002000 +#define ROCKET_FLAGS 0x00003FFF + +#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged + * users can set or reset */ + +/* + * For closing_wait and closing_wait2 + */ +#define ROCKET_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE +#define ROCKET_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF + +/* + * Rocketport ioctls -- "RP" + */ +#define RCKP_GET_STRUCT 0x00525001 +#define RCKP_GET_CONFIG 0x00525002 +#define RCKP_SET_CONFIG 0x00525003 +#define RCKP_GET_PORTS 0x00525004 +#define RCKP_RESET_RM2 0x00525005 +#define RCKP_GET_VERSION 0x00525006 + +/* Rocketport Models */ +#define MODEL_RP32INTF 0x0001 /* RP 32 port w/external I/F */ +#define MODEL_RP8INTF 0x0002 /* RP 8 port w/external I/F */ +#define MODEL_RP16INTF 0x0003 /* RP 16 port w/external I/F */ +#define MODEL_RP8OCTA 0x0005 /* RP 8 port w/octa cable */ +#define MODEL_RP4QUAD 0x0004 /* RP 4 port w/quad cable */ +#define MODEL_RP8J 0x0006 /* RP 8 port w/RJ11 connectors */ +#define MODEL_RP4J 0x0007 /* RP 4 port w/RJ45 connectors */ +#define MODEL_RP8SNI 0x0008 /* RP 8 port w/ DB78 SNI connector */ +#define MODEL_RP16SNI 0x0009 /* RP 16 port w/ DB78 SNI connector */ +#define MODEL_RPP4 0x000A /* RP Plus 4 port */ +#define MODEL_RPP8 0x000B /* RP Plus 8 port */ +#define MODEL_RP2_232 0x000E /* RP Plus 2 port RS232 */ +#define MODEL_RP2_422 0x000F /* RP Plus 2 port RS232 */ + +/* Rocketmodem II Models */ +#define MODEL_RP6M 0x000C /* RM 6 port */ +#define MODEL_RP4M 0x000D /* RM 4 port */ + +/* Universal PCI boards */ +#define MODEL_UPCI_RP32INTF 0x0801 /* RP UPCI 32 port w/external I/F */ +#define MODEL_UPCI_RP8INTF 0x0802 /* RP UPCI 8 port w/external I/F */ +#define MODEL_UPCI_RP16INTF 0x0803 /* RP UPCI 16 port w/external I/F */ +#define MODEL_UPCI_RP8OCTA 0x0805 /* RP UPCI 8 port w/octa cable */ +#define MODEL_UPCI_RM3_8PORT 0x080C /* RP UPCI Rocketmodem III 8 port */ +#define MODEL_UPCI_RM3_4PORT 0x080C /* RP UPCI Rocketmodem III 4 port */ + +/* Compact PCI 16 port */ +#define MODEL_CPCI_RP16INTF 0x0903 /* RP Compact PCI 16 port w/external I/F */ + +/* All ISA boards */ +#define MODEL_ISA 0x1000 diff --git a/drivers/tty/rocket_int.h b/drivers/tty/rocket_int.h new file mode 100644 index 000000000000..67e0f1e778a2 --- /dev/null +++ b/drivers/tty/rocket_int.h @@ -0,0 +1,1214 @@ +/* + * rocket_int.h --- internal header file for rocket.c + * + * Written by Theodore Ts'o, Copyright 1997. + * Copyright 1997 Comtrol Corporation. + * + */ + +/* + * Definition of the types in rcktpt_type + */ +#define ROCKET_TYPE_NORMAL 0 +#define ROCKET_TYPE_MODEM 1 +#define ROCKET_TYPE_MODEMII 2 +#define ROCKET_TYPE_MODEMIII 3 +#define ROCKET_TYPE_PC104 4 + +#include + +#include +#include + +typedef unsigned char Byte_t; +typedef unsigned int ByteIO_t; + +typedef unsigned int Word_t; +typedef unsigned int WordIO_t; + +typedef unsigned int DWordIO_t; + +/* + * Note! Normally the Linux I/O macros already take care of + * byte-swapping the I/O instructions. However, all accesses using + * sOutDW aren't really 32-bit accesses, but should be handled in byte + * order. Hence the use of the cpu_to_le32() macro to byte-swap + * things to no-op the byte swapping done by the big-endian outl() + * instruction. + */ + +static inline void sOutB(unsigned short port, unsigned char value) +{ +#ifdef ROCKET_DEBUG_IO + printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value); +#endif + outb_p(value, port); +} + +static inline void sOutW(unsigned short port, unsigned short value) +{ +#ifdef ROCKET_DEBUG_IO + printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value); +#endif + outw_p(value, port); +} + +static inline void out32(unsigned short port, Byte_t *p) +{ + u32 value = get_unaligned_le32(p); +#ifdef ROCKET_DEBUG_IO + printk(KERN_DEBUG "out32(%x, %lx)...\n", port, value); +#endif + outl_p(value, port); +} + +static inline unsigned char sInB(unsigned short port) +{ + return inb_p(port); +} + +static inline unsigned short sInW(unsigned short port) +{ + return inw_p(port); +} + +/* This is used to move arrays of bytes so byte swapping isn't appropriate. */ +#define sOutStrW(port, addr, count) if (count) outsw(port, addr, count) +#define sInStrW(port, addr, count) if (count) insw(port, addr, count) + +#define CTL_SIZE 8 +#define AIOP_CTL_SIZE 4 +#define CHAN_AIOP_SIZE 8 +#define MAX_PORTS_PER_AIOP 8 +#define MAX_AIOPS_PER_BOARD 4 +#define MAX_PORTS_PER_BOARD 32 + +/* Bus type ID */ +#define isISA 0 +#define isPCI 1 +#define isMC 2 + +/* Controller ID numbers */ +#define CTLID_NULL -1 /* no controller exists */ +#define CTLID_0001 0x0001 /* controller release 1 */ + +/* AIOP ID numbers, identifies AIOP type implementing channel */ +#define AIOPID_NULL -1 /* no AIOP or channel exists */ +#define AIOPID_0001 0x0001 /* AIOP release 1 */ + +/************************************************************************ + Global Register Offsets - Direct Access - Fixed values +************************************************************************/ + +#define _CMD_REG 0x38 /* Command Register 8 Write */ +#define _INT_CHAN 0x39 /* Interrupt Channel Register 8 Read */ +#define _INT_MASK 0x3A /* Interrupt Mask Register 8 Read / Write */ +#define _UNUSED 0x3B /* Unused 8 */ +#define _INDX_ADDR 0x3C /* Index Register Address 16 Write */ +#define _INDX_DATA 0x3E /* Index Register Data 8/16 Read / Write */ + +/************************************************************************ + Channel Register Offsets for 1st channel in AIOP - Direct Access +************************************************************************/ +#define _TD0 0x00 /* Transmit Data 16 Write */ +#define _RD0 0x00 /* Receive Data 16 Read */ +#define _CHN_STAT0 0x20 /* Channel Status 8/16 Read / Write */ +#define _FIFO_CNT0 0x10 /* Transmit/Receive FIFO Count 16 Read */ +#define _INT_ID0 0x30 /* Interrupt Identification 8 Read */ + +/************************************************************************ + Tx Control Register Offsets - Indexed - External - Fixed +************************************************************************/ +#define _TX_ENBLS 0x980 /* Tx Processor Enables Register 8 Read / Write */ +#define _TXCMP1 0x988 /* Transmit Compare Value #1 8 Read / Write */ +#define _TXCMP2 0x989 /* Transmit Compare Value #2 8 Read / Write */ +#define _TXREP1B1 0x98A /* Tx Replace Value #1 - Byte 1 8 Read / Write */ +#define _TXREP1B2 0x98B /* Tx Replace Value #1 - Byte 2 8 Read / Write */ +#define _TXREP2 0x98C /* Transmit Replace Value #2 8 Read / Write */ + +/************************************************************************ +Memory Controller Register Offsets - Indexed - External - Fixed +************************************************************************/ +#define _RX_FIFO 0x000 /* Rx FIFO */ +#define _TX_FIFO 0x800 /* Tx FIFO */ +#define _RXF_OUTP 0x990 /* Rx FIFO OUT pointer 16 Read / Write */ +#define _RXF_INP 0x992 /* Rx FIFO IN pointer 16 Read / Write */ +#define _TXF_OUTP 0x994 /* Tx FIFO OUT pointer 8 Read / Write */ +#define _TXF_INP 0x995 /* Tx FIFO IN pointer 8 Read / Write */ +#define _TXP_CNT 0x996 /* Tx Priority Count 8 Read / Write */ +#define _TXP_PNTR 0x997 /* Tx Priority Pointer 8 Read / Write */ + +#define PRI_PEND 0x80 /* Priority data pending (bit7, Tx pri cnt) */ +#define TXFIFO_SIZE 255 /* size of Tx FIFO */ +#define RXFIFO_SIZE 1023 /* size of Rx FIFO */ + +/************************************************************************ +Tx Priority Buffer - Indexed - External - Fixed +************************************************************************/ +#define _TXP_BUF 0x9C0 /* Tx Priority Buffer 32 Bytes Read / Write */ +#define TXP_SIZE 0x20 /* 32 bytes */ + +/************************************************************************ +Channel Register Offsets - Indexed - Internal - Fixed +************************************************************************/ + +#define _TX_CTRL 0xFF0 /* Transmit Control 16 Write */ +#define _RX_CTRL 0xFF2 /* Receive Control 8 Write */ +#define _BAUD 0xFF4 /* Baud Rate 16 Write */ +#define _CLK_PRE 0xFF6 /* Clock Prescaler 8 Write */ + +#define STMBREAK 0x08 /* BREAK */ +#define STMFRAME 0x04 /* framing error */ +#define STMRCVROVR 0x02 /* receiver over run error */ +#define STMPARITY 0x01 /* parity error */ +#define STMERROR (STMBREAK | STMFRAME | STMPARITY) +#define STMBREAKH 0x800 /* BREAK */ +#define STMFRAMEH 0x400 /* framing error */ +#define STMRCVROVRH 0x200 /* receiver over run error */ +#define STMPARITYH 0x100 /* parity error */ +#define STMERRORH (STMBREAKH | STMFRAMEH | STMPARITYH) + +#define CTS_ACT 0x20 /* CTS input asserted */ +#define DSR_ACT 0x10 /* DSR input asserted */ +#define CD_ACT 0x08 /* CD input asserted */ +#define TXFIFOMT 0x04 /* Tx FIFO is empty */ +#define TXSHRMT 0x02 /* Tx shift register is empty */ +#define RDA 0x01 /* Rx data available */ +#define DRAINED (TXFIFOMT | TXSHRMT) /* indicates Tx is drained */ + +#define STATMODE 0x8000 /* status mode enable bit */ +#define RXFOVERFL 0x2000 /* receive FIFO overflow */ +#define RX2MATCH 0x1000 /* receive compare byte 2 match */ +#define RX1MATCH 0x0800 /* receive compare byte 1 match */ +#define RXBREAK 0x0400 /* received BREAK */ +#define RXFRAME 0x0200 /* received framing error */ +#define RXPARITY 0x0100 /* received parity error */ +#define STATERROR (RXBREAK | RXFRAME | RXPARITY) + +#define CTSFC_EN 0x80 /* CTS flow control enable bit */ +#define RTSTOG_EN 0x40 /* RTS toggle enable bit */ +#define TXINT_EN 0x10 /* transmit interrupt enable */ +#define STOP2 0x08 /* enable 2 stop bits (0 = 1 stop) */ +#define PARITY_EN 0x04 /* enable parity (0 = no parity) */ +#define EVEN_PAR 0x02 /* even parity (0 = odd parity) */ +#define DATA8BIT 0x01 /* 8 bit data (0 = 7 bit data) */ + +#define SETBREAK 0x10 /* send break condition (must clear) */ +#define LOCALLOOP 0x08 /* local loopback set for test */ +#define SET_DTR 0x04 /* assert DTR */ +#define SET_RTS 0x02 /* assert RTS */ +#define TX_ENABLE 0x01 /* enable transmitter */ + +#define RTSFC_EN 0x40 /* RTS flow control enable */ +#define RXPROC_EN 0x20 /* receive processor enable */ +#define TRIG_NO 0x00 /* Rx FIFO trigger level 0 (no trigger) */ +#define TRIG_1 0x08 /* trigger level 1 char */ +#define TRIG_1_2 0x10 /* trigger level 1/2 */ +#define TRIG_7_8 0x18 /* trigger level 7/8 */ +#define TRIG_MASK 0x18 /* trigger level mask */ +#define SRCINT_EN 0x04 /* special Rx condition interrupt enable */ +#define RXINT_EN 0x02 /* Rx interrupt enable */ +#define MCINT_EN 0x01 /* modem change interrupt enable */ + +#define RXF_TRIG 0x20 /* Rx FIFO trigger level interrupt */ +#define TXFIFO_MT 0x10 /* Tx FIFO empty interrupt */ +#define SRC_INT 0x08 /* special receive condition interrupt */ +#define DELTA_CD 0x04 /* CD change interrupt */ +#define DELTA_CTS 0x02 /* CTS change interrupt */ +#define DELTA_DSR 0x01 /* DSR change interrupt */ + +#define REP1W2_EN 0x10 /* replace byte 1 with 2 bytes enable */ +#define IGN2_EN 0x08 /* ignore byte 2 enable */ +#define IGN1_EN 0x04 /* ignore byte 1 enable */ +#define COMP2_EN 0x02 /* compare byte 2 enable */ +#define COMP1_EN 0x01 /* compare byte 1 enable */ + +#define RESET_ALL 0x80 /* reset AIOP (all channels) */ +#define TXOVERIDE 0x40 /* Transmit software off override */ +#define RESETUART 0x20 /* reset channel's UART */ +#define RESTXFCNT 0x10 /* reset channel's Tx FIFO count register */ +#define RESRXFCNT 0x08 /* reset channel's Rx FIFO count register */ + +#define INTSTAT0 0x01 /* AIOP 0 interrupt status */ +#define INTSTAT1 0x02 /* AIOP 1 interrupt status */ +#define INTSTAT2 0x04 /* AIOP 2 interrupt status */ +#define INTSTAT3 0x08 /* AIOP 3 interrupt status */ + +#define INTR_EN 0x08 /* allow interrupts to host */ +#define INT_STROB 0x04 /* strobe and clear interrupt line (EOI) */ + +/************************************************************************** + MUDBAC remapped for PCI +**************************************************************************/ + +#define _CFG_INT_PCI 0x40 +#define _PCI_INT_FUNC 0x3A + +#define PCI_STROB 0x2000 /* bit 13 of int aiop register */ +#define INTR_EN_PCI 0x0010 /* allow interrupts to host */ + +/* + * Definitions for Universal PCI board registers + */ +#define _PCI_9030_INT_CTRL 0x4c /* Offsets from BAR1 */ +#define _PCI_9030_GPIO_CTRL 0x54 +#define PCI_INT_CTRL_AIOP 0x0001 +#define PCI_GPIO_CTRL_8PORT 0x4000 +#define _PCI_9030_RING_IND 0xc0 /* Offsets from BAR1 */ + +#define CHAN3_EN 0x08 /* enable AIOP 3 */ +#define CHAN2_EN 0x04 /* enable AIOP 2 */ +#define CHAN1_EN 0x02 /* enable AIOP 1 */ +#define CHAN0_EN 0x01 /* enable AIOP 0 */ +#define FREQ_DIS 0x00 +#define FREQ_274HZ 0x60 +#define FREQ_137HZ 0x50 +#define FREQ_69HZ 0x40 +#define FREQ_34HZ 0x30 +#define FREQ_17HZ 0x20 +#define FREQ_9HZ 0x10 +#define PERIODIC_ONLY 0x80 /* only PERIODIC interrupt */ + +#define CHANINT_EN 0x0100 /* flags to enable/disable channel ints */ + +#define RDATASIZE 72 +#define RREGDATASIZE 52 + +/* + * AIOP interrupt bits for ISA/PCI boards and UPCI boards. + */ +#define AIOP_INTR_BIT_0 0x0001 +#define AIOP_INTR_BIT_1 0x0002 +#define AIOP_INTR_BIT_2 0x0004 +#define AIOP_INTR_BIT_3 0x0008 + +#define AIOP_INTR_BITS ( \ + AIOP_INTR_BIT_0 \ + | AIOP_INTR_BIT_1 \ + | AIOP_INTR_BIT_2 \ + | AIOP_INTR_BIT_3) + +#define UPCI_AIOP_INTR_BIT_0 0x0004 +#define UPCI_AIOP_INTR_BIT_1 0x0020 +#define UPCI_AIOP_INTR_BIT_2 0x0100 +#define UPCI_AIOP_INTR_BIT_3 0x0800 + +#define UPCI_AIOP_INTR_BITS ( \ + UPCI_AIOP_INTR_BIT_0 \ + | UPCI_AIOP_INTR_BIT_1 \ + | UPCI_AIOP_INTR_BIT_2 \ + | UPCI_AIOP_INTR_BIT_3) + +/* Controller level information structure */ +typedef struct { + int CtlID; + int CtlNum; + int BusType; + int boardType; + int isUPCI; + WordIO_t PCIIO; + WordIO_t PCIIO2; + ByteIO_t MBaseIO; + ByteIO_t MReg1IO; + ByteIO_t MReg2IO; + ByteIO_t MReg3IO; + Byte_t MReg2; + Byte_t MReg3; + int NumAiop; + int AltChanRingIndicator; + ByteIO_t UPCIRingInd; + WordIO_t AiopIO[AIOP_CTL_SIZE]; + ByteIO_t AiopIntChanIO[AIOP_CTL_SIZE]; + int AiopID[AIOP_CTL_SIZE]; + int AiopNumChan[AIOP_CTL_SIZE]; + Word_t *AiopIntrBits; +} CONTROLLER_T; + +typedef CONTROLLER_T CONTROLLER_t; + +/* Channel level information structure */ +typedef struct { + CONTROLLER_T *CtlP; + int AiopNum; + int ChanID; + int ChanNum; + int rtsToggle; + + ByteIO_t Cmd; + ByteIO_t IntChan; + ByteIO_t IntMask; + DWordIO_t IndexAddr; + WordIO_t IndexData; + + WordIO_t TxRxData; + WordIO_t ChanStat; + WordIO_t TxRxCount; + ByteIO_t IntID; + + Word_t TxFIFO; + Word_t TxFIFOPtrs; + Word_t RxFIFO; + Word_t RxFIFOPtrs; + Word_t TxPrioCnt; + Word_t TxPrioPtr; + Word_t TxPrioBuf; + + Byte_t R[RREGDATASIZE]; + + Byte_t BaudDiv[4]; + Byte_t TxControl[4]; + Byte_t RxControl[4]; + Byte_t TxEnables[4]; + Byte_t TxCompare[4]; + Byte_t TxReplace1[4]; + Byte_t TxReplace2[4]; +} CHANNEL_T; + +typedef CHANNEL_T CHANNEL_t; +typedef CHANNEL_T *CHANPTR_T; + +#define InterfaceModeRS232 0x00 +#define InterfaceModeRS422 0x08 +#define InterfaceModeRS485 0x10 +#define InterfaceModeRS232T 0x18 + +/*************************************************************************** +Function: sClrBreak +Purpose: Stop sending a transmit BREAK signal +Call: sClrBreak(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sClrBreak(ChP) \ +do { \ + (ChP)->TxControl[3] &= ~SETBREAK; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sClrDTR +Purpose: Clr the DTR output +Call: sClrDTR(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sClrDTR(ChP) \ +do { \ + (ChP)->TxControl[3] &= ~SET_DTR; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sClrRTS +Purpose: Clr the RTS output +Call: sClrRTS(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sClrRTS(ChP) \ +do { \ + if ((ChP)->rtsToggle) break; \ + (ChP)->TxControl[3] &= ~SET_RTS; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sClrTxXOFF +Purpose: Clear any existing transmit software flow control off condition +Call: sClrTxXOFF(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sClrTxXOFF(ChP) \ +do { \ + sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \ + sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \ +} while (0) + +/*************************************************************************** +Function: sCtlNumToCtlPtr +Purpose: Convert a controller number to controller structure pointer +Call: sCtlNumToCtlPtr(CtlNum) + int CtlNum; Controller number +Return: CONTROLLER_T *: Ptr to controller structure +*/ +#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM] + +/*************************************************************************** +Function: sControllerEOI +Purpose: Strobe the MUDBAC's End Of Interrupt bit. +Call: sControllerEOI(CtlP) + CONTROLLER_T *CtlP; Ptr to controller structure +*/ +#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB) + +/*************************************************************************** +Function: sPCIControllerEOI +Purpose: Strobe the PCI End Of Interrupt bit. + For the UPCI boards, toggle the AIOP interrupt enable bit + (this was taken from the Windows driver). +Call: sPCIControllerEOI(CtlP) + CONTROLLER_T *CtlP; Ptr to controller structure +*/ +#define sPCIControllerEOI(CTLP) \ +do { \ + if ((CTLP)->isUPCI) { \ + Word_t w = sInW((CTLP)->PCIIO); \ + sOutW((CTLP)->PCIIO, (w ^ PCI_INT_CTRL_AIOP)); \ + sOutW((CTLP)->PCIIO, w); \ + } \ + else { \ + sOutW((CTLP)->PCIIO, PCI_STROB); \ + } \ +} while (0) + +/*************************************************************************** +Function: sDisAiop +Purpose: Disable I/O access to an AIOP +Call: sDisAiop(CltP) + CONTROLLER_T *CtlP; Ptr to controller structure + int AiopNum; Number of AIOP on controller +*/ +#define sDisAiop(CTLP,AIOPNUM) \ +do { \ + (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \ + sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ +} while (0) + +/*************************************************************************** +Function: sDisCTSFlowCtl +Purpose: Disable output flow control using CTS +Call: sDisCTSFlowCtl(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisCTSFlowCtl(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~CTSFC_EN; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sDisIXANY +Purpose: Disable IXANY Software Flow Control +Call: sDisIXANY(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisIXANY(ChP) \ +do { \ + (ChP)->R[0x0e] = 0x86; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ +} while (0) + +/*************************************************************************** +Function: DisParity +Purpose: Disable parity +Call: sDisParity(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: Function sSetParity() can be used in place of functions sEnParity(), + sDisParity(), sSetOddParity(), and sSetEvenParity(). +*/ +#define sDisParity(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~PARITY_EN; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sDisRTSToggle +Purpose: Disable RTS toggle +Call: sDisRTSToggle(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisRTSToggle(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~RTSTOG_EN; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ + (ChP)->rtsToggle = 0; \ +} while (0) + +/*************************************************************************** +Function: sDisRxFIFO +Purpose: Disable Rx FIFO +Call: sDisRxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisRxFIFO(ChP) \ +do { \ + (ChP)->R[0x32] = 0x0a; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ +} while (0) + +/*************************************************************************** +Function: sDisRxStatusMode +Purpose: Disable the Rx status mode +Call: sDisRxStatusMode(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: This takes the channel out of the receive status mode. All + subsequent reads of receive data using sReadRxWord() will return + two data bytes. +*/ +#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0) + +/*************************************************************************** +Function: sDisTransmit +Purpose: Disable transmit +Call: sDisTransmit(ChP) + CHANNEL_T *ChP; Ptr to channel structure + This disables movement of Tx data from the Tx FIFO into the 1 byte + Tx buffer. Therefore there could be up to a 2 byte latency + between the time sDisTransmit() is called and the transmit buffer + and transmit shift register going completely empty. +*/ +#define sDisTransmit(ChP) \ +do { \ + (ChP)->TxControl[3] &= ~TX_ENABLE; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sDisTxSoftFlowCtl +Purpose: Disable Tx Software Flow Control +Call: sDisTxSoftFlowCtl(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisTxSoftFlowCtl(ChP) \ +do { \ + (ChP)->R[0x06] = 0x8a; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ +} while (0) + +/*************************************************************************** +Function: sEnAiop +Purpose: Enable I/O access to an AIOP +Call: sEnAiop(CltP) + CONTROLLER_T *CtlP; Ptr to controller structure + int AiopNum; Number of AIOP on controller +*/ +#define sEnAiop(CTLP,AIOPNUM) \ +do { \ + (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \ + sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \ +} while (0) + +/*************************************************************************** +Function: sEnCTSFlowCtl +Purpose: Enable output flow control using CTS +Call: sEnCTSFlowCtl(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnCTSFlowCtl(ChP) \ +do { \ + (ChP)->TxControl[2] |= CTSFC_EN; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sEnIXANY +Purpose: Enable IXANY Software Flow Control +Call: sEnIXANY(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnIXANY(ChP) \ +do { \ + (ChP)->R[0x0e] = 0x21; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \ +} while (0) + +/*************************************************************************** +Function: EnParity +Purpose: Enable parity +Call: sEnParity(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: Function sSetParity() can be used in place of functions sEnParity(), + sDisParity(), sSetOddParity(), and sSetEvenParity(). + +Warnings: Before enabling parity odd or even parity should be chosen using + functions sSetOddParity() or sSetEvenParity(). +*/ +#define sEnParity(ChP) \ +do { \ + (ChP)->TxControl[2] |= PARITY_EN; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sEnRTSToggle +Purpose: Enable RTS toggle +Call: sEnRTSToggle(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: This function will disable RTS flow control and clear the RTS + line to allow operation of RTS toggle. +*/ +#define sEnRTSToggle(ChP) \ +do { \ + (ChP)->RxControl[2] &= ~RTSFC_EN; \ + out32((ChP)->IndexAddr,(ChP)->RxControl); \ + (ChP)->TxControl[2] |= RTSTOG_EN; \ + (ChP)->TxControl[3] &= ~SET_RTS; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ + (ChP)->rtsToggle = 1; \ +} while (0) + +/*************************************************************************** +Function: sEnRxFIFO +Purpose: Enable Rx FIFO +Call: sEnRxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnRxFIFO(ChP) \ +do { \ + (ChP)->R[0x32] = 0x08; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \ +} while (0) + +/*************************************************************************** +Function: sEnRxProcessor +Purpose: Enable the receive processor +Call: sEnRxProcessor(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: This function is used to start the receive processor. When + the channel is in the reset state the receive processor is not + running. This is done to prevent the receive processor from + executing invalid microcode instructions prior to the + downloading of the microcode. + +Warnings: This function must be called after valid microcode has been + downloaded to the AIOP, and it must not be called before the + microcode has been downloaded. +*/ +#define sEnRxProcessor(ChP) \ +do { \ + (ChP)->RxControl[2] |= RXPROC_EN; \ + out32((ChP)->IndexAddr,(ChP)->RxControl); \ +} while (0) + +/*************************************************************************** +Function: sEnRxStatusMode +Purpose: Enable the Rx status mode +Call: sEnRxStatusMode(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: This places the channel in the receive status mode. All subsequent + reads of receive data using sReadRxWord() will return a data byte + in the low word and a status byte in the high word. + +*/ +#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE) + +/*************************************************************************** +Function: sEnTransmit +Purpose: Enable transmit +Call: sEnTransmit(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnTransmit(ChP) \ +do { \ + (ChP)->TxControl[3] |= TX_ENABLE; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sEnTxSoftFlowCtl +Purpose: Enable Tx Software Flow Control +Call: sEnTxSoftFlowCtl(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnTxSoftFlowCtl(ChP) \ +do { \ + (ChP)->R[0x06] = 0xc5; \ + out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ +} while (0) + +/*************************************************************************** +Function: sGetAiopIntStatus +Purpose: Get the AIOP interrupt status +Call: sGetAiopIntStatus(CtlP,AiopNum) + CONTROLLER_T *CtlP; Ptr to controller structure + int AiopNum; AIOP number +Return: Byte_t: The AIOP interrupt status. Bits 0 through 7 + represent channels 0 through 7 respectively. If a + bit is set that channel is interrupting. +*/ +#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM]) + +/*************************************************************************** +Function: sGetAiopNumChan +Purpose: Get the number of channels supported by an AIOP +Call: sGetAiopNumChan(CtlP,AiopNum) + CONTROLLER_T *CtlP; Ptr to controller structure + int AiopNum; AIOP number +Return: int: The number of channels supported by the AIOP +*/ +#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM] + +/*************************************************************************** +Function: sGetChanIntID +Purpose: Get a channel's interrupt identification byte +Call: sGetChanIntID(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: Byte_t: The channel interrupt ID. Can be any + combination of the following flags: + RXF_TRIG: Rx FIFO trigger level interrupt + TXFIFO_MT: Tx FIFO empty interrupt + SRC_INT: Special receive condition interrupt + DELTA_CD: CD change interrupt + DELTA_CTS: CTS change interrupt + DELTA_DSR: DSR change interrupt +*/ +#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR)) + +/*************************************************************************** +Function: sGetChanNum +Purpose: Get the number of a channel within an AIOP +Call: sGetChanNum(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: int: Channel number within AIOP, or NULLCHAN if channel does + not exist. +*/ +#define sGetChanNum(ChP) (ChP)->ChanNum + +/*************************************************************************** +Function: sGetChanStatus +Purpose: Get the channel status +Call: sGetChanStatus(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: Word_t: The channel status. Can be any combination of + the following flags: + LOW BYTE FLAGS + CTS_ACT: CTS input asserted + DSR_ACT: DSR input asserted + CD_ACT: CD input asserted + TXFIFOMT: Tx FIFO is empty + TXSHRMT: Tx shift register is empty + RDA: Rx data available + + HIGH BYTE FLAGS + STATMODE: status mode enable bit + RXFOVERFL: receive FIFO overflow + RX2MATCH: receive compare byte 2 match + RX1MATCH: receive compare byte 1 match + RXBREAK: received BREAK + RXFRAME: received framing error + RXPARITY: received parity error +Warnings: This function will clear the high byte flags in the Channel + Status Register. +*/ +#define sGetChanStatus(ChP) sInW((ChP)->ChanStat) + +/*************************************************************************** +Function: sGetChanStatusLo +Purpose: Get the low byte only of the channel status +Call: sGetChanStatusLo(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: Byte_t: The channel status low byte. Can be any combination + of the following flags: + CTS_ACT: CTS input asserted + DSR_ACT: DSR input asserted + CD_ACT: CD input asserted + TXFIFOMT: Tx FIFO is empty + TXSHRMT: Tx shift register is empty + RDA: Rx data available +*/ +#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat) + +/********************************************************************** + * Get RI status of channel + * Defined as a function in rocket.c -aes + */ +#if 0 +#define sGetChanRI(ChP) ((ChP)->CtlP->AltChanRingIndicator ? \ + (sInB((ByteIO_t)((ChP)->ChanStat+8)) & DSR_ACT) : \ + (((ChP)->CtlP->boardType == ROCKET_TYPE_PC104) ? \ + (!(sInB((ChP)->CtlP->AiopIO[3]) & sBitMapSetTbl[(ChP)->ChanNum])) : \ + 0)) +#endif + +/*************************************************************************** +Function: sGetControllerIntStatus +Purpose: Get the controller interrupt status +Call: sGetControllerIntStatus(CtlP) + CONTROLLER_T *CtlP; Ptr to controller structure +Return: Byte_t: The controller interrupt status in the lower 4 + bits. Bits 0 through 3 represent AIOP's 0 + through 3 respectively. If a bit is set that + AIOP is interrupting. Bits 4 through 7 will + always be cleared. +*/ +#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f) + +/*************************************************************************** +Function: sPCIGetControllerIntStatus +Purpose: Get the controller interrupt status +Call: sPCIGetControllerIntStatus(CtlP) + CONTROLLER_T *CtlP; Ptr to controller structure +Return: unsigned char: The controller interrupt status in the lower 4 + bits and bit 4. Bits 0 through 3 represent AIOP's 0 + through 3 respectively. Bit 4 is set if the int + was generated from periodic. If a bit is set the + AIOP is interrupting. +*/ +#define sPCIGetControllerIntStatus(CTLP) \ + ((CTLP)->isUPCI ? \ + (sInW((CTLP)->PCIIO2) & UPCI_AIOP_INTR_BITS) : \ + ((sInW((CTLP)->PCIIO) >> 8) & AIOP_INTR_BITS)) + +/*************************************************************************** + +Function: sGetRxCnt +Purpose: Get the number of data bytes in the Rx FIFO +Call: sGetRxCnt(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: int: The number of data bytes in the Rx FIFO. +Comments: Byte read of count register is required to obtain Rx count. + +*/ +#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount) + +/*************************************************************************** +Function: sGetTxCnt +Purpose: Get the number of data bytes in the Tx FIFO +Call: sGetTxCnt(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: Byte_t: The number of data bytes in the Tx FIFO. +Comments: Byte read of count register is required to obtain Tx count. + +*/ +#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount) + +/***************************************************************************** +Function: sGetTxRxDataIO +Purpose: Get the I/O address of a channel's TxRx Data register +Call: sGetTxRxDataIO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: WordIO_t: I/O address of a channel's TxRx Data register +*/ +#define sGetTxRxDataIO(ChP) (ChP)->TxRxData + +/*************************************************************************** +Function: sInitChanDefaults +Purpose: Initialize a channel structure to it's default state. +Call: sInitChanDefaults(ChP) + CHANNEL_T *ChP; Ptr to the channel structure +Comments: This function must be called once for every channel structure + that exists before any other SSCI calls can be made. + +*/ +#define sInitChanDefaults(ChP) \ +do { \ + (ChP)->CtlP = NULLCTLPTR; \ + (ChP)->AiopNum = NULLAIOP; \ + (ChP)->ChanID = AIOPID_NULL; \ + (ChP)->ChanNum = NULLCHAN; \ +} while (0) + +/*************************************************************************** +Function: sResetAiopByNum +Purpose: Reset the AIOP by number +Call: sResetAiopByNum(CTLP,AIOPNUM) + CONTROLLER_T CTLP; Ptr to controller structure + AIOPNUM; AIOP index +*/ +#define sResetAiopByNum(CTLP,AIOPNUM) \ +do { \ + sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \ + sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \ +} while (0) + +/*************************************************************************** +Function: sSendBreak +Purpose: Send a transmit BREAK signal +Call: sSendBreak(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSendBreak(ChP) \ +do { \ + (ChP)->TxControl[3] |= SETBREAK; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetBaud +Purpose: Set baud rate +Call: sSetBaud(ChP,Divisor) + CHANNEL_T *ChP; Ptr to channel structure + Word_t Divisor; 16 bit baud rate divisor for channel +*/ +#define sSetBaud(ChP,DIVISOR) \ +do { \ + (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \ + (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \ + out32((ChP)->IndexAddr,(ChP)->BaudDiv); \ +} while (0) + +/*************************************************************************** +Function: sSetData7 +Purpose: Set data bits to 7 +Call: sSetData7(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetData7(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~DATA8BIT; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetData8 +Purpose: Set data bits to 8 +Call: sSetData8(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetData8(ChP) \ +do { \ + (ChP)->TxControl[2] |= DATA8BIT; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetDTR +Purpose: Set the DTR output +Call: sSetDTR(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetDTR(ChP) \ +do { \ + (ChP)->TxControl[3] |= SET_DTR; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetEvenParity +Purpose: Set even parity +Call: sSetEvenParity(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: Function sSetParity() can be used in place of functions sEnParity(), + sDisParity(), sSetOddParity(), and sSetEvenParity(). + +Warnings: This function has no effect unless parity is enabled with function + sEnParity(). +*/ +#define sSetEvenParity(ChP) \ +do { \ + (ChP)->TxControl[2] |= EVEN_PAR; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetOddParity +Purpose: Set odd parity +Call: sSetOddParity(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: Function sSetParity() can be used in place of functions sEnParity(), + sDisParity(), sSetOddParity(), and sSetEvenParity(). + +Warnings: This function has no effect unless parity is enabled with function + sEnParity(). +*/ +#define sSetOddParity(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~EVEN_PAR; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetRTS +Purpose: Set the RTS output +Call: sSetRTS(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetRTS(ChP) \ +do { \ + if ((ChP)->rtsToggle) break; \ + (ChP)->TxControl[3] |= SET_RTS; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetRxTrigger +Purpose: Set the Rx FIFO trigger level +Call: sSetRxProcessor(ChP,Level) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Level; Number of characters in Rx FIFO at which the + interrupt will be generated. Can be any of the following flags: + + TRIG_NO: no trigger + TRIG_1: 1 character in FIFO + TRIG_1_2: FIFO 1/2 full + TRIG_7_8: FIFO 7/8 full +Comments: An interrupt will be generated when the trigger level is reached + only if function sEnInterrupt() has been called with flag + RXINT_EN set. The RXF_TRIG flag in the Interrupt Idenfification + register will be set whenever the trigger level is reached + regardless of the setting of RXINT_EN. + +*/ +#define sSetRxTrigger(ChP,LEVEL) \ +do { \ + (ChP)->RxControl[2] &= ~TRIG_MASK; \ + (ChP)->RxControl[2] |= LEVEL; \ + out32((ChP)->IndexAddr,(ChP)->RxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetStop1 +Purpose: Set stop bits to 1 +Call: sSetStop1(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetStop1(ChP) \ +do { \ + (ChP)->TxControl[2] &= ~STOP2; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetStop2 +Purpose: Set stop bits to 2 +Call: sSetStop2(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sSetStop2(ChP) \ +do { \ + (ChP)->TxControl[2] |= STOP2; \ + out32((ChP)->IndexAddr,(ChP)->TxControl); \ +} while (0) + +/*************************************************************************** +Function: sSetTxXOFFChar +Purpose: Set the Tx XOFF flow control character +Call: sSetTxXOFFChar(ChP,Ch) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Ch; The value to set the Tx XOFF character to +*/ +#define sSetTxXOFFChar(ChP,CH) \ +do { \ + (ChP)->R[0x07] = (CH); \ + out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \ +} while (0) + +/*************************************************************************** +Function: sSetTxXONChar +Purpose: Set the Tx XON flow control character +Call: sSetTxXONChar(ChP,Ch) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Ch; The value to set the Tx XON character to +*/ +#define sSetTxXONChar(ChP,CH) \ +do { \ + (ChP)->R[0x0b] = (CH); \ + out32((ChP)->IndexAddr,&(ChP)->R[0x08]); \ +} while (0) + +/*************************************************************************** +Function: sStartRxProcessor +Purpose: Start a channel's receive processor +Call: sStartRxProcessor(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Comments: This function is used to start a Rx processor after it was + stopped with sStopRxProcessor() or sStopSWInFlowCtl(). It + will restart both the Rx processor and software input flow control. + +*/ +#define sStartRxProcessor(ChP) out32((ChP)->IndexAddr,&(ChP)->R[0]) + +/*************************************************************************** +Function: sWriteTxByte +Purpose: Write a transmit data byte to a channel. + ByteIO_t io: Channel transmit register I/O address. This can + be obtained with sGetTxRxDataIO(). + Byte_t Data; The transmit data byte. +Warnings: This function writes the data byte without checking to see if + sMaxTxSize is exceeded in the Tx FIFO. +*/ +#define sWriteTxByte(IO,DATA) sOutB(IO,DATA) + +/* + * Begin Linux specific definitions for the Rocketport driver + * + * This code is Copyright Theodore Ts'o, 1995-1997 + */ + +struct r_port { + int magic; + struct tty_port port; + int line; + int flags; /* Don't yet match the ASY_ flags!! */ + unsigned int board:3; + unsigned int aiop:2; + unsigned int chan:3; + CONTROLLER_t *ctlp; + CHANNEL_t channel; + int intmask; + int xmit_fifo_room; /* room in xmit fifo */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + int cd_status; + int ignore_status_mask; + int read_status_mask; + int cps; + + struct completion close_wait; /* Not yet matching the core */ + spinlock_t slock; + struct mutex write_mtx; +}; + +#define RPORT_MAGIC 0x525001 + +#define NUM_BOARDS 8 +#define MAX_RP_PORTS (32*NUM_BOARDS) + +/* + * The size of the xmit buffer is 1 page, or 4096 bytes + */ +#define XMIT_BUF_SIZE 4096 + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * Assigned major numbers for the Comtrol Rocketport + */ +#define TTY_ROCKET_MAJOR 46 +#define CUA_ROCKET_MAJOR 47 + +#ifdef PCI_VENDOR_ID_RP +#undef PCI_VENDOR_ID_RP +#undef PCI_DEVICE_ID_RP8OCTA +#undef PCI_DEVICE_ID_RP8INTF +#undef PCI_DEVICE_ID_RP16INTF +#undef PCI_DEVICE_ID_RP32INTF +#undef PCI_DEVICE_ID_URP8OCTA +#undef PCI_DEVICE_ID_URP8INTF +#undef PCI_DEVICE_ID_URP16INTF +#undef PCI_DEVICE_ID_CRP16INTF +#undef PCI_DEVICE_ID_URP32INTF +#endif + +/* Comtrol PCI Vendor ID */ +#define PCI_VENDOR_ID_RP 0x11fe + +/* Comtrol Device ID's */ +#define PCI_DEVICE_ID_RP32INTF 0x0001 /* Rocketport 32 port w/external I/F */ +#define PCI_DEVICE_ID_RP8INTF 0x0002 /* Rocketport 8 port w/external I/F */ +#define PCI_DEVICE_ID_RP16INTF 0x0003 /* Rocketport 16 port w/external I/F */ +#define PCI_DEVICE_ID_RP4QUAD 0x0004 /* Rocketport 4 port w/quad cable */ +#define PCI_DEVICE_ID_RP8OCTA 0x0005 /* Rocketport 8 port w/octa cable */ +#define PCI_DEVICE_ID_RP8J 0x0006 /* Rocketport 8 port w/RJ11 connectors */ +#define PCI_DEVICE_ID_RP4J 0x0007 /* Rocketport 4 port w/RJ11 connectors */ +#define PCI_DEVICE_ID_RP8SNI 0x0008 /* Rocketport 8 port w/ DB78 SNI (Siemens) connector */ +#define PCI_DEVICE_ID_RP16SNI 0x0009 /* Rocketport 16 port w/ DB78 SNI (Siemens) connector */ +#define PCI_DEVICE_ID_RPP4 0x000A /* Rocketport Plus 4 port */ +#define PCI_DEVICE_ID_RPP8 0x000B /* Rocketport Plus 8 port */ +#define PCI_DEVICE_ID_RP6M 0x000C /* RocketModem 6 port */ +#define PCI_DEVICE_ID_RP4M 0x000D /* RocketModem 4 port */ +#define PCI_DEVICE_ID_RP2_232 0x000E /* Rocketport Plus 2 port RS232 */ +#define PCI_DEVICE_ID_RP2_422 0x000F /* Rocketport Plus 2 port RS422 */ + +/* Universal PCI boards */ +#define PCI_DEVICE_ID_URP32INTF 0x0801 /* Rocketport UPCI 32 port w/external I/F */ +#define PCI_DEVICE_ID_URP8INTF 0x0802 /* Rocketport UPCI 8 port w/external I/F */ +#define PCI_DEVICE_ID_URP16INTF 0x0803 /* Rocketport UPCI 16 port w/external I/F */ +#define PCI_DEVICE_ID_URP8OCTA 0x0805 /* Rocketport UPCI 8 port w/octa cable */ +#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C /* Rocketmodem III 8 port */ +#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D /* Rocketmodem III 4 port */ + +/* Compact PCI device */ +#define PCI_DEVICE_ID_CRP16INTF 0x0903 /* Rocketport Compact PCI 16 port w/external I/F */ + diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c new file mode 100644 index 000000000000..18888d005a0a --- /dev/null +++ b/drivers/tty/synclink.c @@ -0,0 +1,8119 @@ +/* + * linux/drivers/char/synclink.c + * + * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $ + * + * Device driver for Microgate SyncLink ISA and PCI + * high speed multiprotocol serial adapters. + * + * written by Paul Fulghum for Microgate Corporation + * paulkf@microgate.com + * + * Microgate and SyncLink are trademarks of Microgate Corporation + * + * Derived from serial.c written by Theodore Ts'o and Linus Torvalds + * + * Original release 01/11/99 + * + * This code is released under the GNU General Public License (GPL) + * + * This driver is primarily intended for use in synchronous + * HDLC mode. Asynchronous mode is also provided. + * + * When operating in synchronous mode, each call to mgsl_write() + * contains exactly one complete HDLC frame. Calling mgsl_put_char + * will start assembling an HDLC frame that will not be sent until + * mgsl_flush_chars or mgsl_write is called. + * + * Synchronous receive data is reported as complete frames. To accomplish + * this, the TTY flip buffer is bypassed (too small to hold largest + * frame and may fragment frames) and the line discipline + * receive entry point is called directly. + * + * This driver has been tested with a slightly modified ppp.c driver + * for synchronous PPP. + * + * 2000/02/16 + * Added interface for syncppp.c driver (an alternate synchronous PPP + * implementation that also supports Cisco HDLC). Each device instance + * registers as a tty device AND a network device (if dosyncppp option + * is set for the device). The functionality is determined by which + * device interface is opened. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(__i386__) +# define BREAKPOINT() asm(" int $3"); +#else +# define BREAKPOINT() { } +#endif + +#define MAX_ISA_DEVICES 10 +#define MAX_PCI_DEVICES 10 +#define MAX_TOTAL_DEVICES 20 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_MODULE)) +#define SYNCLINK_GENERIC_HDLC 1 +#else +#define SYNCLINK_GENERIC_HDLC 0 +#endif + +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#include + +#define RCLRVALUE 0xffff + +static MGSL_PARAMS default_params = { + MGSL_MODE_HDLC, /* unsigned long mode */ + 0, /* unsigned char loopback; */ + HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ + HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ + 0, /* unsigned long clock_speed; */ + 0xff, /* unsigned char addr_filter; */ + HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ + HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ + HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ + 9600, /* unsigned long data_rate; */ + 8, /* unsigned char data_bits; */ + 1, /* unsigned char stop_bits; */ + ASYNC_PARITY_NONE /* unsigned char parity; */ +}; + +#define SHARED_MEM_ADDRESS_SIZE 0x40000 +#define BUFFERLISTSIZE 4096 +#define DMABUFFERSIZE 4096 +#define MAXRXFRAMES 7 + +typedef struct _DMABUFFERENTRY +{ + u32 phys_addr; /* 32-bit flat physical address of data buffer */ + volatile u16 count; /* buffer size/data count */ + volatile u16 status; /* Control/status field */ + volatile u16 rcc; /* character count field */ + u16 reserved; /* padding required by 16C32 */ + u32 link; /* 32-bit flat link to next buffer entry */ + char *virt_addr; /* virtual address of data buffer */ + u32 phys_entry; /* physical address of this buffer entry */ + dma_addr_t dma_addr; +} DMABUFFERENTRY, *DMAPBUFFERENTRY; + +/* The queue of BH actions to be performed */ + +#define BH_RECEIVE 1 +#define BH_TRANSMIT 2 +#define BH_STATUS 4 + +#define IO_PIN_SHUTDOWN_LIMIT 100 + +struct _input_signal_events { + int ri_up; + int ri_down; + int dsr_up; + int dsr_down; + int dcd_up; + int dcd_down; + int cts_up; + int cts_down; +}; + +/* transmit holding buffer definitions*/ +#define MAX_TX_HOLDING_BUFFERS 5 +struct tx_holding_buffer { + int buffer_size; + unsigned char * buffer; +}; + + +/* + * Device instance data structure + */ + +struct mgsl_struct { + int magic; + struct tty_port port; + int line; + int hw_version; + + struct mgsl_icount icount; + + int timeout; + int x_char; /* xon/xoff character */ + u16 read_status_mask; + u16 ignore_status_mask; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + + wait_queue_head_t status_event_wait_q; + wait_queue_head_t event_wait_q; + struct timer_list tx_timer; /* HDLC transmit timeout timer */ + struct mgsl_struct *next_device; /* device list link */ + + spinlock_t irq_spinlock; /* spinlock for synchronizing with ISR */ + struct work_struct task; /* task structure for scheduling bh */ + + u32 EventMask; /* event trigger mask */ + u32 RecordedEvents; /* pending events */ + + u32 max_frame_size; /* as set by device config */ + + u32 pending_bh; + + bool bh_running; /* Protection from multiple */ + int isr_overflow; + bool bh_requested; + + int dcd_chkcount; /* check counts to prevent */ + int cts_chkcount; /* too many IRQs if a signal */ + int dsr_chkcount; /* is floating */ + int ri_chkcount; + + char *buffer_list; /* virtual address of Rx & Tx buffer lists */ + u32 buffer_list_phys; + dma_addr_t buffer_list_dma_addr; + + unsigned int rx_buffer_count; /* count of total allocated Rx buffers */ + DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */ + unsigned int current_rx_buffer; + + int num_tx_dma_buffers; /* number of tx dma frames required */ + int tx_dma_buffers_used; + unsigned int tx_buffer_count; /* count of total allocated Tx buffers */ + DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */ + int start_tx_dma_buffer; /* tx dma buffer to start tx dma operation */ + int current_tx_buffer; /* next tx dma buffer to be loaded */ + + unsigned char *intermediate_rxbuffer; + + int num_tx_holding_buffers; /* number of tx holding buffer allocated */ + int get_tx_holding_index; /* next tx holding buffer for adapter to load */ + int put_tx_holding_index; /* next tx holding buffer to store user request */ + int tx_holding_count; /* number of tx holding buffers waiting */ + struct tx_holding_buffer tx_holding_buffers[MAX_TX_HOLDING_BUFFERS]; + + bool rx_enabled; + bool rx_overflow; + bool rx_rcc_underrun; + + bool tx_enabled; + bool tx_active; + u32 idle_mode; + + u16 cmr_value; + u16 tcsr_value; + + char device_name[25]; /* device instance name */ + + unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ + unsigned char bus; /* expansion bus number (zero based) */ + unsigned char function; /* PCI device number */ + + unsigned int io_base; /* base I/O address of adapter */ + unsigned int io_addr_size; /* size of the I/O address range */ + bool io_addr_requested; /* true if I/O address requested */ + + unsigned int irq_level; /* interrupt level */ + unsigned long irq_flags; + bool irq_requested; /* true if IRQ requested */ + + unsigned int dma_level; /* DMA channel */ + bool dma_requested; /* true if dma channel requested */ + + u16 mbre_bit; + u16 loopback_bits; + u16 usc_idle_mode; + + MGSL_PARAMS params; /* communications parameters */ + + unsigned char serial_signals; /* current serial signal states */ + + bool irq_occurred; /* for diagnostics use */ + unsigned int init_error; /* Initialization startup error (DIAGS) */ + int fDiagnosticsmode; /* Driver in Diagnostic mode? (DIAGS) */ + + u32 last_mem_alloc; + unsigned char* memory_base; /* shared memory address (PCI only) */ + u32 phys_memory_base; + bool shared_mem_requested; + + unsigned char* lcr_base; /* local config registers (PCI only) */ + u32 phys_lcr_base; + u32 lcr_offset; + bool lcr_mem_requested; + + u32 misc_ctrl_value; + char flag_buf[MAX_ASYNC_BUFFER_SIZE]; + char char_buf[MAX_ASYNC_BUFFER_SIZE]; + bool drop_rts_on_tx_done; + + bool loopmode_insert_requested; + bool loopmode_send_done_requested; + + struct _input_signal_events input_signal_events; + + /* generic HDLC device parts */ + int netcount; + spinlock_t netlock; + +#if SYNCLINK_GENERIC_HDLC + struct net_device *netdev; +#endif +}; + +#define MGSL_MAGIC 0x5401 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#ifndef SERIAL_XMIT_SIZE +#define SERIAL_XMIT_SIZE 4096 +#endif + +/* + * These macros define the offsets used in calculating the + * I/O address of the specified USC registers. + */ + + +#define DCPIN 2 /* Bit 1 of I/O address */ +#define SDPIN 4 /* Bit 2 of I/O address */ + +#define DCAR 0 /* DMA command/address register */ +#define CCAR SDPIN /* channel command/address register */ +#define DATAREG DCPIN + SDPIN /* serial data register */ +#define MSBONLY 0x41 +#define LSBONLY 0x40 + +/* + * These macros define the register address (ordinal number) + * used for writing address/value pairs to the USC. + */ + +#define CMR 0x02 /* Channel mode Register */ +#define CCSR 0x04 /* Channel Command/status Register */ +#define CCR 0x06 /* Channel Control Register */ +#define PSR 0x08 /* Port status Register */ +#define PCR 0x0a /* Port Control Register */ +#define TMDR 0x0c /* Test mode Data Register */ +#define TMCR 0x0e /* Test mode Control Register */ +#define CMCR 0x10 /* Clock mode Control Register */ +#define HCR 0x12 /* Hardware Configuration Register */ +#define IVR 0x14 /* Interrupt Vector Register */ +#define IOCR 0x16 /* Input/Output Control Register */ +#define ICR 0x18 /* Interrupt Control Register */ +#define DCCR 0x1a /* Daisy Chain Control Register */ +#define MISR 0x1c /* Misc Interrupt status Register */ +#define SICR 0x1e /* status Interrupt Control Register */ +#define RDR 0x20 /* Receive Data Register */ +#define RMR 0x22 /* Receive mode Register */ +#define RCSR 0x24 /* Receive Command/status Register */ +#define RICR 0x26 /* Receive Interrupt Control Register */ +#define RSR 0x28 /* Receive Sync Register */ +#define RCLR 0x2a /* Receive count Limit Register */ +#define RCCR 0x2c /* Receive Character count Register */ +#define TC0R 0x2e /* Time Constant 0 Register */ +#define TDR 0x30 /* Transmit Data Register */ +#define TMR 0x32 /* Transmit mode Register */ +#define TCSR 0x34 /* Transmit Command/status Register */ +#define TICR 0x36 /* Transmit Interrupt Control Register */ +#define TSR 0x38 /* Transmit Sync Register */ +#define TCLR 0x3a /* Transmit count Limit Register */ +#define TCCR 0x3c /* Transmit Character count Register */ +#define TC1R 0x3e /* Time Constant 1 Register */ + + +/* + * MACRO DEFINITIONS FOR DMA REGISTERS + */ + +#define DCR 0x06 /* DMA Control Register (shared) */ +#define DACR 0x08 /* DMA Array count Register (shared) */ +#define BDCR 0x12 /* Burst/Dwell Control Register (shared) */ +#define DIVR 0x14 /* DMA Interrupt Vector Register (shared) */ +#define DICR 0x18 /* DMA Interrupt Control Register (shared) */ +#define CDIR 0x1a /* Clear DMA Interrupt Register (shared) */ +#define SDIR 0x1c /* Set DMA Interrupt Register (shared) */ + +#define TDMR 0x02 /* Transmit DMA mode Register */ +#define TDIAR 0x1e /* Transmit DMA Interrupt Arm Register */ +#define TBCR 0x2a /* Transmit Byte count Register */ +#define TARL 0x2c /* Transmit Address Register (low) */ +#define TARU 0x2e /* Transmit Address Register (high) */ +#define NTBCR 0x3a /* Next Transmit Byte count Register */ +#define NTARL 0x3c /* Next Transmit Address Register (low) */ +#define NTARU 0x3e /* Next Transmit Address Register (high) */ + +#define RDMR 0x82 /* Receive DMA mode Register (non-shared) */ +#define RDIAR 0x9e /* Receive DMA Interrupt Arm Register */ +#define RBCR 0xaa /* Receive Byte count Register */ +#define RARL 0xac /* Receive Address Register (low) */ +#define RARU 0xae /* Receive Address Register (high) */ +#define NRBCR 0xba /* Next Receive Byte count Register */ +#define NRARL 0xbc /* Next Receive Address Register (low) */ +#define NRARU 0xbe /* Next Receive Address Register (high) */ + + +/* + * MACRO DEFINITIONS FOR MODEM STATUS BITS + */ + +#define MODEMSTATUS_DTR 0x80 +#define MODEMSTATUS_DSR 0x40 +#define MODEMSTATUS_RTS 0x20 +#define MODEMSTATUS_CTS 0x10 +#define MODEMSTATUS_RI 0x04 +#define MODEMSTATUS_DCD 0x01 + + +/* + * Channel Command/Address Register (CCAR) Command Codes + */ + +#define RTCmd_Null 0x0000 +#define RTCmd_ResetHighestIus 0x1000 +#define RTCmd_TriggerChannelLoadDma 0x2000 +#define RTCmd_TriggerRxDma 0x2800 +#define RTCmd_TriggerTxDma 0x3000 +#define RTCmd_TriggerRxAndTxDma 0x3800 +#define RTCmd_PurgeRxFifo 0x4800 +#define RTCmd_PurgeTxFifo 0x5000 +#define RTCmd_PurgeRxAndTxFifo 0x5800 +#define RTCmd_LoadRcc 0x6800 +#define RTCmd_LoadTcc 0x7000 +#define RTCmd_LoadRccAndTcc 0x7800 +#define RTCmd_LoadTC0 0x8800 +#define RTCmd_LoadTC1 0x9000 +#define RTCmd_LoadTC0AndTC1 0x9800 +#define RTCmd_SerialDataLSBFirst 0xa000 +#define RTCmd_SerialDataMSBFirst 0xa800 +#define RTCmd_SelectBigEndian 0xb000 +#define RTCmd_SelectLittleEndian 0xb800 + + +/* + * DMA Command/Address Register (DCAR) Command Codes + */ + +#define DmaCmd_Null 0x0000 +#define DmaCmd_ResetTxChannel 0x1000 +#define DmaCmd_ResetRxChannel 0x1200 +#define DmaCmd_StartTxChannel 0x2000 +#define DmaCmd_StartRxChannel 0x2200 +#define DmaCmd_ContinueTxChannel 0x3000 +#define DmaCmd_ContinueRxChannel 0x3200 +#define DmaCmd_PauseTxChannel 0x4000 +#define DmaCmd_PauseRxChannel 0x4200 +#define DmaCmd_AbortTxChannel 0x5000 +#define DmaCmd_AbortRxChannel 0x5200 +#define DmaCmd_InitTxChannel 0x7000 +#define DmaCmd_InitRxChannel 0x7200 +#define DmaCmd_ResetHighestDmaIus 0x8000 +#define DmaCmd_ResetAllChannels 0x9000 +#define DmaCmd_StartAllChannels 0xa000 +#define DmaCmd_ContinueAllChannels 0xb000 +#define DmaCmd_PauseAllChannels 0xc000 +#define DmaCmd_AbortAllChannels 0xd000 +#define DmaCmd_InitAllChannels 0xf000 + +#define TCmd_Null 0x0000 +#define TCmd_ClearTxCRC 0x2000 +#define TCmd_SelectTicrTtsaData 0x4000 +#define TCmd_SelectTicrTxFifostatus 0x5000 +#define TCmd_SelectTicrIntLevel 0x6000 +#define TCmd_SelectTicrdma_level 0x7000 +#define TCmd_SendFrame 0x8000 +#define TCmd_SendAbort 0x9000 +#define TCmd_EnableDleInsertion 0xc000 +#define TCmd_DisableDleInsertion 0xd000 +#define TCmd_ClearEofEom 0xe000 +#define TCmd_SetEofEom 0xf000 + +#define RCmd_Null 0x0000 +#define RCmd_ClearRxCRC 0x2000 +#define RCmd_EnterHuntmode 0x3000 +#define RCmd_SelectRicrRtsaData 0x4000 +#define RCmd_SelectRicrRxFifostatus 0x5000 +#define RCmd_SelectRicrIntLevel 0x6000 +#define RCmd_SelectRicrdma_level 0x7000 + +/* + * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR) + */ + +#define RECEIVE_STATUS BIT5 +#define RECEIVE_DATA BIT4 +#define TRANSMIT_STATUS BIT3 +#define TRANSMIT_DATA BIT2 +#define IO_PIN BIT1 +#define MISC BIT0 + + +/* + * Receive status Bits in Receive Command/status Register RCSR + */ + +#define RXSTATUS_SHORT_FRAME BIT8 +#define RXSTATUS_CODE_VIOLATION BIT8 +#define RXSTATUS_EXITED_HUNT BIT7 +#define RXSTATUS_IDLE_RECEIVED BIT6 +#define RXSTATUS_BREAK_RECEIVED BIT5 +#define RXSTATUS_ABORT_RECEIVED BIT5 +#define RXSTATUS_RXBOUND BIT4 +#define RXSTATUS_CRC_ERROR BIT3 +#define RXSTATUS_FRAMING_ERROR BIT3 +#define RXSTATUS_ABORT BIT2 +#define RXSTATUS_PARITY_ERROR BIT2 +#define RXSTATUS_OVERRUN BIT1 +#define RXSTATUS_DATA_AVAILABLE BIT0 +#define RXSTATUS_ALL 0x01f6 +#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) ) + +/* + * Values for setting transmit idle mode in + * Transmit Control/status Register (TCSR) + */ +#define IDLEMODE_FLAGS 0x0000 +#define IDLEMODE_ALT_ONE_ZERO 0x0100 +#define IDLEMODE_ZERO 0x0200 +#define IDLEMODE_ONE 0x0300 +#define IDLEMODE_ALT_MARK_SPACE 0x0500 +#define IDLEMODE_SPACE 0x0600 +#define IDLEMODE_MARK 0x0700 +#define IDLEMODE_MASK 0x0700 + +/* + * IUSC revision identifiers + */ +#define IUSC_SL1660 0x4d44 +#define IUSC_PRE_SL1660 0x4553 + +/* + * Transmit status Bits in Transmit Command/status Register (TCSR) + */ + +#define TCSR_PRESERVE 0x0F00 + +#define TCSR_UNDERWAIT BIT11 +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF_SENT BIT4 +#define TXSTATUS_EOM_SENT BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 +#define TXSTATUS_ALL 0x00fa +#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->tcsr_value + ((b) & 0x00FF)) ) + + +#define MISCSTATUS_RXC_LATCHED BIT15 +#define MISCSTATUS_RXC BIT14 +#define MISCSTATUS_TXC_LATCHED BIT13 +#define MISCSTATUS_TXC BIT12 +#define MISCSTATUS_RI_LATCHED BIT11 +#define MISCSTATUS_RI BIT10 +#define MISCSTATUS_DSR_LATCHED BIT9 +#define MISCSTATUS_DSR BIT8 +#define MISCSTATUS_DCD_LATCHED BIT7 +#define MISCSTATUS_DCD BIT6 +#define MISCSTATUS_CTS_LATCHED BIT5 +#define MISCSTATUS_CTS BIT4 +#define MISCSTATUS_RCC_UNDERRUN BIT3 +#define MISCSTATUS_DPLL_NO_SYNC BIT2 +#define MISCSTATUS_BRG1_ZERO BIT1 +#define MISCSTATUS_BRG0_ZERO BIT0 + +#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0)) +#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f)) + +#define SICR_RXC_ACTIVE BIT15 +#define SICR_RXC_INACTIVE BIT14 +#define SICR_RXC (BIT15+BIT14) +#define SICR_TXC_ACTIVE BIT13 +#define SICR_TXC_INACTIVE BIT12 +#define SICR_TXC (BIT13+BIT12) +#define SICR_RI_ACTIVE BIT11 +#define SICR_RI_INACTIVE BIT10 +#define SICR_RI (BIT11+BIT10) +#define SICR_DSR_ACTIVE BIT9 +#define SICR_DSR_INACTIVE BIT8 +#define SICR_DSR (BIT9+BIT8) +#define SICR_DCD_ACTIVE BIT7 +#define SICR_DCD_INACTIVE BIT6 +#define SICR_DCD (BIT7+BIT6) +#define SICR_CTS_ACTIVE BIT5 +#define SICR_CTS_INACTIVE BIT4 +#define SICR_CTS (BIT5+BIT4) +#define SICR_RCC_UNDERFLOW BIT3 +#define SICR_DPLL_NO_SYNC BIT2 +#define SICR_BRG1_ZERO BIT1 +#define SICR_BRG0_ZERO BIT0 + +void usc_DisableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask ); + +#define usc_EnableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) ) + +#define usc_DisableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) ) + +#define usc_EnableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) ) + +#define usc_DisableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) ) + +#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) ) + +/* + * Transmit status Bits in Transmit Control status Register (TCSR) + * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) + */ + +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 + +#define DICR_MASTER BIT15 +#define DICR_TRANSMIT BIT0 +#define DICR_RECEIVE BIT1 + +#define usc_EnableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) ) + +#define usc_DisableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) ) + +#define usc_EnableStatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) ) + +#define usc_DisablestatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) ) + +/* Transmit status Bits in Transmit Control status Register (TCSR) */ +/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */ + + +#define DISABLE_UNCONDITIONAL 0 +#define DISABLE_END_OF_FRAME 1 +#define ENABLE_UNCONDITIONAL 2 +#define ENABLE_AUTO_CTS 3 +#define ENABLE_AUTO_DCD 3 +#define usc_EnableTransmitter(a,b) \ + usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) ) +#define usc_EnableReceiver(a,b) \ + usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) ) + +static u16 usc_InDmaReg( struct mgsl_struct *info, u16 Port ); +static void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value ); +static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ); + +static u16 usc_InReg( struct mgsl_struct *info, u16 Port ); +static void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value ); +static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_RCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_TCmd( struct mgsl_struct *info, u16 Cmd ); + +#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->tcsr_value + (b))) +#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b)) + +#define usc_SetTransmitSyncChars(a,s0,s1) usc_OutReg((a), TSR, (u16)(((u16)s0<<8)|(u16)s1)) + +static void usc_process_rxoverrun_sync( struct mgsl_struct *info ); +static void usc_start_receiver( struct mgsl_struct *info ); +static void usc_stop_receiver( struct mgsl_struct *info ); + +static void usc_start_transmitter( struct mgsl_struct *info ); +static void usc_stop_transmitter( struct mgsl_struct *info ); +static void usc_set_txidle( struct mgsl_struct *info ); +static void usc_load_txfifo( struct mgsl_struct *info ); + +static void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate ); +static void usc_enable_loopback( struct mgsl_struct *info, int enable ); + +static void usc_get_serial_signals( struct mgsl_struct *info ); +static void usc_set_serial_signals( struct mgsl_struct *info ); + +static void usc_reset( struct mgsl_struct *info ); + +static void usc_set_sync_mode( struct mgsl_struct *info ); +static void usc_set_sdlc_mode( struct mgsl_struct *info ); +static void usc_set_async_mode( struct mgsl_struct *info ); +static void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate ); + +static void usc_loopback_frame( struct mgsl_struct *info ); + +static void mgsl_tx_timeout(unsigned long context); + + +static void usc_loopmode_cancel_transmit( struct mgsl_struct * info ); +static void usc_loopmode_insert_request( struct mgsl_struct * info ); +static int usc_loopmode_active( struct mgsl_struct * info); +static void usc_loopmode_send_done( struct mgsl_struct * info ); + +static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg); + +#if SYNCLINK_GENERIC_HDLC +#define dev_to_port(D) (dev_to_hdlc(D)->priv) +static void hdlcdev_tx_done(struct mgsl_struct *info); +static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size); +static int hdlcdev_init(struct mgsl_struct *info); +static void hdlcdev_exit(struct mgsl_struct *info); +#endif + +/* + * Defines a BUS descriptor value for the PCI adapter + * local bus address ranges. + */ + +#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \ +(0x00400020 + \ +((WrHold) << 30) + \ +((WrDly) << 28) + \ +((RdDly) << 26) + \ +((Nwdd) << 20) + \ +((Nwad) << 15) + \ +((Nxda) << 13) + \ +((Nrdd) << 11) + \ +((Nrad) << 6) ) + +static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit); + +/* + * Adapter diagnostic routines + */ +static bool mgsl_register_test( struct mgsl_struct *info ); +static bool mgsl_irq_test( struct mgsl_struct *info ); +static bool mgsl_dma_test( struct mgsl_struct *info ); +static bool mgsl_memory_test( struct mgsl_struct *info ); +static int mgsl_adapter_test( struct mgsl_struct *info ); + +/* + * device and resource management routines + */ +static int mgsl_claim_resources(struct mgsl_struct *info); +static void mgsl_release_resources(struct mgsl_struct *info); +static void mgsl_add_device(struct mgsl_struct *info); +static struct mgsl_struct* mgsl_allocate_device(void); + +/* + * DMA buffer manupulation functions. + */ +static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ); +static bool mgsl_get_rx_frame( struct mgsl_struct *info ); +static bool mgsl_get_raw_rx_frame( struct mgsl_struct *info ); +static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ); +static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info ); +static int num_free_tx_dma_buffers(struct mgsl_struct *info); +static void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize); +static void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count); + +/* + * DMA and Shared Memory buffer allocation and formatting + */ +static int mgsl_allocate_dma_buffers(struct mgsl_struct *info); +static void mgsl_free_dma_buffers(struct mgsl_struct *info); +static int mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +static int mgsl_alloc_buffer_list_memory(struct mgsl_struct *info); +static void mgsl_free_buffer_list_memory(struct mgsl_struct *info); +static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info); +static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info); +static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info); +static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info); +static bool load_next_tx_holding_buffer(struct mgsl_struct *info); +static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize); + +/* + * Bottom half interrupt handlers + */ +static void mgsl_bh_handler(struct work_struct *work); +static void mgsl_bh_receive(struct mgsl_struct *info); +static void mgsl_bh_transmit(struct mgsl_struct *info); +static void mgsl_bh_status(struct mgsl_struct *info); + +/* + * Interrupt handler routines and dispatch table. + */ +static void mgsl_isr_null( struct mgsl_struct *info ); +static void mgsl_isr_transmit_data( struct mgsl_struct *info ); +static void mgsl_isr_receive_data( struct mgsl_struct *info ); +static void mgsl_isr_receive_status( struct mgsl_struct *info ); +static void mgsl_isr_transmit_status( struct mgsl_struct *info ); +static void mgsl_isr_io_pin( struct mgsl_struct *info ); +static void mgsl_isr_misc( struct mgsl_struct *info ); +static void mgsl_isr_receive_dma( struct mgsl_struct *info ); +static void mgsl_isr_transmit_dma( struct mgsl_struct *info ); + +typedef void (*isr_dispatch_func)(struct mgsl_struct *); + +static isr_dispatch_func UscIsrTable[7] = +{ + mgsl_isr_null, + mgsl_isr_misc, + mgsl_isr_io_pin, + mgsl_isr_transmit_data, + mgsl_isr_transmit_status, + mgsl_isr_receive_data, + mgsl_isr_receive_status +}; + +/* + * ioctl call handlers + */ +static int tiocmget(struct tty_struct *tty); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount + __user *user_icount); +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params); +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params); +static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode); +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode); +static int mgsl_txenable(struct mgsl_struct * info, int enable); +static int mgsl_txabort(struct mgsl_struct * info); +static int mgsl_rxenable(struct mgsl_struct * info, int enable); +static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask); +static int mgsl_loopmode_send_done( struct mgsl_struct * info ); + +/* set non-zero on successful registration with PCI subsystem */ +static bool pci_registered; + +/* + * Global linked list of SyncLink devices + */ +static struct mgsl_struct *mgsl_device_list; +static int mgsl_device_count; + +/* + * Set this param to non-zero to load eax with the + * .text section address and breakpoint on module load. + * This is useful for use with gdb and add-symbol-file command. + */ +static int break_on_load; + +/* + * Driver major number, defaults to zero to get auto + * assigned major number. May be forced as module parameter. + */ +static int ttymajor; + +/* + * Array of user specified options for ISA adapters. + */ +static int io[MAX_ISA_DEVICES]; +static int irq[MAX_ISA_DEVICES]; +static int dma[MAX_ISA_DEVICES]; +static int debug_level; +static int maxframe[MAX_TOTAL_DEVICES]; +static int txdmabufs[MAX_TOTAL_DEVICES]; +static int txholdbufs[MAX_TOTAL_DEVICES]; + +module_param(break_on_load, bool, 0); +module_param(ttymajor, int, 0); +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(dma, int, NULL, 0); +module_param(debug_level, int, 0); +module_param_array(maxframe, int, NULL, 0); +module_param_array(txdmabufs, int, NULL, 0); +module_param_array(txholdbufs, int, NULL, 0); + +static char *driver_name = "SyncLink serial driver"; +static char *driver_version = "$Revision: 4.38 $"; + +static int synclink_init_one (struct pci_dev *dev, + const struct pci_device_id *ent); +static void synclink_remove_one (struct pci_dev *dev); + +static struct pci_device_id synclink_pci_tbl[] = { + { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_USC, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_MICROGATE, 0x0210, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, synclink_pci_tbl); + +MODULE_LICENSE("GPL"); + +static struct pci_driver synclink_pci_driver = { + .name = "synclink", + .id_table = synclink_pci_tbl, + .probe = synclink_init_one, + .remove = __devexit_p(synclink_remove_one), +}; + +static struct tty_driver *serial_driver; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + + +static void mgsl_change_params(struct mgsl_struct *info); +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * 1st function defined in .text section. Calling this function in + * init_module() followed by a breakpoint allows a remote debugger + * (gdb) to get the .text address for the add-symbol-file command. + * This allows remote debugging of dynamically loadable modules. + */ +static void* mgsl_get_text_ptr(void) +{ + return mgsl_get_text_ptr; +} + +static inline int mgsl_paranoia_check(struct mgsl_struct *info, + char *name, const char *routine) +{ +#ifdef MGSL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for mgsl struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null mgsl_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, name, routine); + return 1; + } + if (info->magic != MGSL_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#else + if (!info) + return 1; +#endif + return 0; +} + +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + +/* mgsl_stop() throttle (stop) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_stop(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->name, "mgsl_stop")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_stop(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->tx_enabled) + usc_stop_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_stop() */ + +/* mgsl_start() release (start) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_start(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->name, "mgsl_start")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_start(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_start() */ + +/* + * Bottom half work queue access functions + */ + +/* mgsl_bh_action() Return next bottom half action to perform. + * Return Value: BH action code or 0 if nothing to do. + */ +static int mgsl_bh_action(struct mgsl_struct *info) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if (info->pending_bh & BH_RECEIVE) { + info->pending_bh &= ~BH_RECEIVE; + rc = BH_RECEIVE; + } else if (info->pending_bh & BH_TRANSMIT) { + info->pending_bh &= ~BH_TRANSMIT; + rc = BH_TRANSMIT; + } else if (info->pending_bh & BH_STATUS) { + info->pending_bh &= ~BH_STATUS; + rc = BH_STATUS; + } + + if (!rc) { + /* Mark BH routine as complete */ + info->bh_running = false; + info->bh_requested = false; + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return rc; +} + +/* + * Perform bottom half processing of work items queued by ISR. + */ +static void mgsl_bh_handler(struct work_struct *work) +{ + struct mgsl_struct *info = + container_of(work, struct mgsl_struct, task); + int action; + + if (!info) + return; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) entry\n", + __FILE__,__LINE__,info->device_name); + + info->bh_running = true; + + while((action = mgsl_bh_action(info)) != 0) { + + /* Process work item */ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler() work item action=%d\n", + __FILE__,__LINE__,action); + + switch (action) { + + case BH_RECEIVE: + mgsl_bh_receive(info); + break; + case BH_TRANSMIT: + mgsl_bh_transmit(info); + break; + case BH_STATUS: + mgsl_bh_status(info); + break; + default: + /* unknown work item ID */ + printk("Unknown work item ID=%08X!\n", action); + break; + } + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) exit\n", + __FILE__,__LINE__,info->device_name); +} + +static void mgsl_bh_receive(struct mgsl_struct *info) +{ + bool (*get_rx_frame)(struct mgsl_struct *info) = + (info->params.mode == MGSL_MODE_HDLC ? mgsl_get_rx_frame : mgsl_get_raw_rx_frame); + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_receive(%s)\n", + __FILE__,__LINE__,info->device_name); + + do + { + if (info->rx_rcc_underrun) { + unsigned long flags; + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return; + } + } while(get_rx_frame(info)); +} + +static void mgsl_bh_transmit(struct mgsl_struct *info) +{ + struct tty_struct *tty = info->port.tty; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_transmit() entry on %s\n", + __FILE__,__LINE__,info->device_name); + + if (tty) + tty_wakeup(tty); + + /* if transmitter idle and loopmode_send_done_requested + * then start echoing RxD to TxD + */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( !info->tx_active && info->loopmode_send_done_requested ) + usc_loopmode_send_done( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); +} + +static void mgsl_bh_status(struct mgsl_struct *info) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_status() entry on %s\n", + __FILE__,__LINE__,info->device_name); + + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + info->dcd_chkcount = 0; + info->cts_chkcount = 0; +} + +/* mgsl_isr_receive_status() + * + * Service a receive status interrupt. The type of status + * interrupt is indicated by the state of the RCSR. + * This is only used for HDLC mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_receive_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, RCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_status status=%04X\n", + __FILE__,__LINE__,status); + + if ( (status & RXSTATUS_ABORT_RECEIVED) && + info->loopmode_insert_requested && + usc_loopmode_active(info) ) + { + ++info->icount.rxabort; + info->loopmode_insert_requested = false; + + /* clear CMR:13 to start echoing RxD to TxD */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); + + /* disable received abort irq (no longer required) */ + usc_OutReg(info, RICR, + (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); + } + + if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { + if (status & RXSTATUS_EXITED_HUNT) + info->icount.exithunt++; + if (status & RXSTATUS_IDLE_RECEIVED) + info->icount.rxidle++; + wake_up_interruptible(&info->event_wait_q); + } + + if (status & RXSTATUS_OVERRUN){ + info->icount.rxover++; + usc_process_rxoverrun_sync( info ); + } + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + usc_UnlatchRxstatusBits( info, status ); + +} /* end of mgsl_isr_receive_status() */ + +/* mgsl_isr_transmit_status() + * + * Service a transmit status interrupt + * HDLC mode :end of transmit frame + * Async mode:all data is sent + * transmit status is indicated by bits in the TCSR. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_transmit_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, TCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_status status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_UnlatchTxstatusBits( info, status ); + + if ( status & (TXSTATUS_UNDERRUN | TXSTATUS_ABORT_SENT) ) + { + /* finished sending HDLC abort. This may leave */ + /* the TxFifo with data from the aborted frame */ + /* so purge the TxFifo. Also shutdown the DMA */ + /* channel in case there is data remaining in */ + /* the DMA buffer */ + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + } + + if ( status & TXSTATUS_EOF_SENT ) + info->icount.txok++; + else if ( status & TXSTATUS_UNDERRUN ) + info->icount.txunder++; + else if ( status & TXSTATUS_ABORT_SENT ) + info->icount.txabort++; + else + info->icount.txunder++; + + info->tx_active = false; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + + if ( info->drop_rts_on_tx_done ) { + usc_get_serial_signals( info ); + if ( info->serial_signals & SerialSignal_RTS ) { + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals( info ); + } + info->drop_rts_on_tx_done = false; + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + { + if (info->port.tty->stopped || info->port.tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + info->pending_bh |= BH_TRANSMIT; + } + +} /* end of mgsl_isr_transmit_status() */ + +/* mgsl_isr_io_pin() + * + * Service an Input/Output pin interrupt. The type of + * interrupt is indicated by bits in the MISR + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_io_pin( struct mgsl_struct *info ) +{ + struct mgsl_icount *icount; + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_io_pin status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, IO_PIN ); + usc_UnlatchIostatusBits( info, status ); + + if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | + MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { + icount = &info->icount; + /* update input line counters */ + if (status & MISCSTATUS_RI_LATCHED) { + if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_RI); + icount->rng++; + if ( status & MISCSTATUS_RI ) + info->input_signal_events.ri_up++; + else + info->input_signal_events.ri_down++; + } + if (status & MISCSTATUS_DSR_LATCHED) { + if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DSR); + icount->dsr++; + if ( status & MISCSTATUS_DSR ) + info->input_signal_events.dsr_up++; + else + info->input_signal_events.dsr_down++; + } + if (status & MISCSTATUS_DCD_LATCHED) { + if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DCD); + icount->dcd++; + if (status & MISCSTATUS_DCD) { + info->input_signal_events.dcd_up++; + } else + info->input_signal_events.dcd_down++; +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) { + if (status & MISCSTATUS_DCD) + netif_carrier_on(info->netdev); + else + netif_carrier_off(info->netdev); + } +#endif + } + if (status & MISCSTATUS_CTS_LATCHED) + { + if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_CTS); + icount->cts++; + if ( status & MISCSTATUS_CTS ) + info->input_signal_events.cts_up++; + else + info->input_signal_events.cts_down++; + } + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if ( (info->port.flags & ASYNC_CHECK_CD) && + (status & MISCSTATUS_DCD_LATCHED) ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s CD now %s...", info->device_name, + (status & MISCSTATUS_DCD) ? "on" : "off"); + if (status & MISCSTATUS_DCD) + wake_up_interruptible(&info->port.open_wait); + else { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("doing serial hangup..."); + if (info->port.tty) + tty_hangup(info->port.tty); + } + } + + if ( (info->port.flags & ASYNC_CTS_FLOW) && + (status & MISCSTATUS_CTS_LATCHED) ) { + if (info->port.tty->hw_stopped) { + if (status & MISCSTATUS_CTS) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx start..."); + if (info->port.tty) + info->port.tty->hw_stopped = 0; + usc_start_transmitter(info); + info->pending_bh |= BH_TRANSMIT; + return; + } + } else { + if (!(status & MISCSTATUS_CTS)) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx stop..."); + if (info->port.tty) + info->port.tty->hw_stopped = 1; + usc_stop_transmitter(info); + } + } + } + } + + info->pending_bh |= BH_STATUS; + + /* for diagnostics set IRQ flag */ + if ( status & MISCSTATUS_TXC_LATCHED ){ + usc_OutReg( info, SICR, + (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) ); + usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED ); + info->irq_occurred = true; + } + +} /* end of mgsl_isr_io_pin() */ + +/* mgsl_isr_transmit_data() + * + * Service a transmit data interrupt (async mode only). + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_transmit_data( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n", + __FILE__,__LINE__,info->xmit_cnt); + + usc_ClearIrqPendingBits( info, TRANSMIT_DATA ); + + if (info->port.tty->stopped || info->port.tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + + if ( info->xmit_cnt ) + usc_load_txfifo( info ); + else + info->tx_active = false; + + if (info->xmit_cnt < WAKEUP_CHARS) + info->pending_bh |= BH_TRANSMIT; + +} /* end of mgsl_isr_transmit_data() */ + +/* mgsl_isr_receive_data() + * + * Service a receive data interrupt. This occurs + * when operating in asynchronous interrupt transfer mode. + * The receive data FIFO is flushed to the receive data buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_receive_data( struct mgsl_struct *info ) +{ + int Fifocount; + u16 status; + int work = 0; + unsigned char DataByte; + struct tty_struct *tty = info->port.tty; + struct mgsl_icount *icount = &info->icount; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_data\n", + __FILE__,__LINE__); + + usc_ClearIrqPendingBits( info, RECEIVE_DATA ); + + /* select FIFO status for RICR readback */ + usc_RCmd( info, RCmd_SelectRicrRxFifostatus ); + + /* clear the Wordstatus bit so that status readback */ + /* only reflects the status of this byte */ + usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 )); + + /* flush the receive FIFO */ + + while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) { + int flag; + + /* read one byte from RxFIFO */ + outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY), + info->io_base + CCAR ); + DataByte = inb( info->io_base + CCAR ); + + /* get the status of the received byte */ + status = usc_InReg(info, RCSR); + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) + usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); + + icount->rx++; + + flag = 0; + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { + printk("rxerr=%04X\n",status); + /* update error statistics */ + if ( status & RXSTATUS_BREAK_RECEIVED ) { + status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); + icount->brk++; + } else if (status & RXSTATUS_PARITY_ERROR) + icount->parity++; + else if (status & RXSTATUS_FRAMING_ERROR) + icount->frame++; + else if (status & RXSTATUS_OVERRUN) { + /* must issue purge fifo cmd before */ + /* 16C32 accepts more receive chars */ + usc_RTCmd(info,RTCmd_PurgeRxFifo); + icount->overrun++; + } + + /* discard char if tty control flags say so */ + if (status & info->ignore_status_mask) + continue; + + status &= info->read_status_mask; + + if (status & RXSTATUS_BREAK_RECEIVED) { + flag = TTY_BREAK; + if (info->port.flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & RXSTATUS_PARITY_ERROR) + flag = TTY_PARITY; + else if (status & RXSTATUS_FRAMING_ERROR) + flag = TTY_FRAME; + } /* end of if (error) */ + tty_insert_flip_char(tty, DataByte, flag); + if (status & RXSTATUS_OVERRUN) { + /* Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + work += tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + } + + if ( debug_level >= DEBUG_LEVEL_ISR ) { + printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n", + __FILE__,__LINE__,icount->rx,icount->brk, + icount->parity,icount->frame,icount->overrun); + } + + if(work) + tty_flip_buffer_push(tty); +} + +/* mgsl_isr_misc() + * + * Service a miscellaneous interrupt source. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +static void mgsl_isr_misc( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_misc status=%04X\n", + __FILE__,__LINE__,status); + + if ((status & MISCSTATUS_RCC_UNDERRUN) && + (info->params.mode == MGSL_MODE_HDLC)) { + + /* turn off receiver and rx DMA */ + usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); + usc_DmaCmd(info, DmaCmd_ResetRxChannel); + usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); + usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS); + + /* schedule BH handler to restart receiver */ + info->pending_bh |= BH_RECEIVE; + info->rx_rcc_underrun = true; + } + + usc_ClearIrqPendingBits( info, MISC ); + usc_UnlatchMiscstatusBits( info, status ); + +} /* end of mgsl_isr_misc() */ + +/* mgsl_isr_null() + * + * Services undefined interrupt vectors from the + * USC. (hence this function SHOULD never be called) + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +static void mgsl_isr_null( struct mgsl_struct *info ) +{ + +} /* end of mgsl_isr_null() */ + +/* mgsl_isr_receive_dma() + * + * Service a receive DMA channel interrupt. + * For this driver there are two sources of receive DMA interrupts + * as identified in the Receive DMA mode Register (RDMR): + * + * BIT3 EOA/EOL End of List, all receive buffers in receive + * buffer list have been filled (no more free buffers + * available). The DMA controller has shut down. + * + * BIT2 EOB End of Buffer. This interrupt occurs when a receive + * DMA buffer is terminated in response to completion + * of a good frame or a frame with errors. The status + * of the frame is stored in the buffer entry in the + * list of receive buffer entries. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_receive_dma( struct mgsl_struct *info ) +{ + u16 status; + + /* clear interrupt pending and IUS bit for Rx DMA IRQ */ + usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); + + /* Read the receive DMA status to identify interrupt type. */ + /* This also clears the status bits. */ + status = usc_InDmaReg( info, RDMR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n", + __FILE__,__LINE__,info->device_name,status); + + info->pending_bh |= BH_RECEIVE; + + if ( status & BIT3 ) { + info->rx_overflow = true; + info->icount.buf_overrun++; + } + +} /* end of mgsl_isr_receive_dma() */ + +/* mgsl_isr_transmit_dma() + * + * This function services a transmit DMA channel interrupt. + * + * For this driver there is one source of transmit DMA interrupts + * as identified in the Transmit DMA Mode Register (TDMR): + * + * BIT2 EOB End of Buffer. This interrupt occurs when a + * transmit DMA buffer has been emptied. + * + * The driver maintains enough transmit DMA buffers to hold at least + * one max frame size transmit frame. When operating in a buffered + * transmit mode, there may be enough transmit DMA buffers to hold at + * least two or more max frame size frames. On an EOB condition, + * determine if there are any queued transmit buffers and copy into + * transmit DMA buffers if we have room. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_isr_transmit_dma( struct mgsl_struct *info ) +{ + u16 status; + + /* clear interrupt pending and IUS bit for Tx DMA IRQ */ + usc_OutDmaReg(info, CDIR, BIT8+BIT0 ); + + /* Read the transmit DMA status to identify interrupt type. */ + /* This also clears the status bits. */ + + status = usc_InDmaReg( info, TDMR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_dma(%s) status=%04X\n", + __FILE__,__LINE__,info->device_name,status); + + if ( status & BIT2 ) { + --info->tx_dma_buffers_used; + + /* if there are transmit frames queued, + * try to load the next one + */ + if ( load_next_tx_holding_buffer(info) ) { + /* if call returns non-zero value, we have + * at least one free tx holding buffer + */ + info->pending_bh |= BH_TRANSMIT; + } + } + +} /* end of mgsl_isr_transmit_dma() */ + +/* mgsl_interrupt() + * + * Interrupt service routine entry point. + * + * Arguments: + * + * irq interrupt number that caused interrupt + * dev_id device ID supplied during interrupt registration + * + * Return Value: None + */ +static irqreturn_t mgsl_interrupt(int dummy, void *dev_id) +{ + struct mgsl_struct *info = dev_id; + u16 UscVector; + u16 DmaVector; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)entry.\n", + __FILE__, __LINE__, info->irq_level); + + spin_lock(&info->irq_spinlock); + + for(;;) { + /* Read the interrupt vectors from hardware. */ + UscVector = usc_InReg(info, IVR) >> 9; + DmaVector = usc_InDmaReg(info, DIVR); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n", + __FILE__,__LINE__,info->device_name,UscVector,DmaVector); + + if ( !UscVector && !DmaVector ) + break; + + /* Dispatch interrupt vector */ + if ( UscVector ) + (*UscIsrTable[UscVector])(info); + else if ( (DmaVector&(BIT10|BIT9)) == BIT10) + mgsl_isr_transmit_dma(info); + else + mgsl_isr_receive_dma(info); + + if ( info->isr_overflow ) { + printk(KERN_ERR "%s(%d):%s isr overflow irq=%d\n", + __FILE__, __LINE__, info->device_name, info->irq_level); + usc_DisableMasterIrqBit(info); + usc_DisableDmaInterrupts(info,DICR_MASTER); + break; + } + } + + /* Request bottom half processing if there's something + * for it to do and the bh is not already running + */ + + if ( info->pending_bh && !info->bh_running && !info->bh_requested ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s queueing bh task.\n", + __FILE__,__LINE__,info->device_name); + schedule_work(&info->task); + info->bh_requested = true; + } + + spin_unlock(&info->irq_spinlock); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)exit.\n", + __FILE__, __LINE__, info->irq_level); + + return IRQ_HANDLED; +} /* end of mgsl_interrupt() */ + +/* startup() + * + * Initialize and start device. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int startup(struct mgsl_struct * info) +{ + int retval = 0; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name); + + if (info->port.flags & ASYNC_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + /* allocate a page of memory for a transmit buffer */ + info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + if (!info->xmit_buf) { + printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", + __FILE__,__LINE__,info->device_name); + return -ENOMEM; + } + } + + info->pending_bh = 0; + + memset(&info->icount, 0, sizeof(info->icount)); + + setup_timer(&info->tx_timer, mgsl_tx_timeout, (unsigned long)info); + + /* Allocate and claim adapter resources */ + retval = mgsl_claim_resources(info); + + /* perform existence check and diagnostics */ + if ( !retval ) + retval = mgsl_adapter_test(info); + + if ( retval ) { + if (capable(CAP_SYS_ADMIN) && info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + mgsl_release_resources(info); + return retval; + } + + /* program hardware for current parameters */ + mgsl_change_params(info); + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags |= ASYNC_INITIALIZED; + + return 0; + +} /* end of startup() */ + +/* shutdown() + * + * Called by mgsl_close() and mgsl_hangup() to shutdown hardware + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void shutdown(struct mgsl_struct * info) +{ + unsigned long flags; + + if (!(info->port.flags & ASYNC_INITIALIZED)) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_shutdown(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* clear status wait queue because status changes */ + /* can't happen after shutting down the hardware */ + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + del_timer_sync(&info->tx_timer); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = NULL; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_DisableMasterIrqBit(info); + usc_stop_receiver(info); + usc_stop_transmitter(info); + usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + + TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); + usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); + + /* Disable DMAEN (Port 7, Bit 14) */ + /* This disconnects the DMA request signal from the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14)); + + /* Disable INTEN (Port 6, Bit12) */ + /* This disconnects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); + + if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + usc_set_serial_signals(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_release_resources(info); + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags &= ~ASYNC_INITIALIZED; + +} /* end of shutdown() */ + +static void mgsl_program_hw(struct mgsl_struct *info) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + usc_stop_receiver(info); + usc_stop_transmitter(info); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if (info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW || + info->netcount) + usc_set_sync_mode(info); + else + usc_set_async_mode(info); + + usc_set_serial_signals(info); + + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + + usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); + usc_EnableInterrupts(info, IO_PIN); + usc_get_serial_signals(info); + + if (info->netcount || info->port.tty->termios->c_cflag & CREAD) + usc_start_receiver(info); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); +} + +/* Reconfigure adapter based on new parameters + */ +static void mgsl_change_params(struct mgsl_struct *info) +{ + unsigned cflag; + int bits_per_char; + + if (!info->port.tty || !info->port.tty->termios) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_change_params(%s)\n", + __FILE__,__LINE__, info->device_name ); + + cflag = info->port.tty->termios->c_cflag; + + /* if B0 rate (hangup) specified then negate DTR and RTS */ + /* otherwise assert DTR and RTS */ + if (cflag & CBAUD) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + + /* byte size and parity */ + + switch (cflag & CSIZE) { + case CS5: info->params.data_bits = 5; break; + case CS6: info->params.data_bits = 6; break; + case CS7: info->params.data_bits = 7; break; + case CS8: info->params.data_bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: info->params.data_bits = 7; break; + } + + if (cflag & CSTOPB) + info->params.stop_bits = 2; + else + info->params.stop_bits = 1; + + info->params.parity = ASYNC_PARITY_NONE; + if (cflag & PARENB) { + if (cflag & PARODD) + info->params.parity = ASYNC_PARITY_ODD; + else + info->params.parity = ASYNC_PARITY_EVEN; +#ifdef CMSPAR + if (cflag & CMSPAR) + info->params.parity = ASYNC_PARITY_SPACE; +#endif + } + + /* calculate number of jiffies to transmit a full + * FIFO (32 bytes) at specified data rate + */ + bits_per_char = info->params.data_bits + + info->params.stop_bits + 1; + + /* if port data rate is set to 460800 or less then + * allow tty settings to override, otherwise keep the + * current data rate. + */ + if (info->params.data_rate <= 460800) + info->params.data_rate = tty_get_baud_rate(info->port.tty); + + if ( info->params.data_rate ) { + info->timeout = (32*HZ*bits_per_char) / + info->params.data_rate; + } + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->port.flags |= ASYNC_CTS_FLOW; + else + info->port.flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->port.flags &= ~ASYNC_CHECK_CD; + else + info->port.flags |= ASYNC_CHECK_CD; + + /* process tty input control flags */ + + info->read_status_mask = RXSTATUS_OVERRUN; + if (I_INPCK(info->port.tty)) + info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask |= RXSTATUS_BREAK_RECEIVED; + + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_IGNBRK(info->port.tty)) { + info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED; + /* If ignoring parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= RXSTATUS_OVERRUN; + } + + mgsl_program_hw(info); + +} /* end of mgsl_change_params() */ + +/* mgsl_put_char() + * + * Add a character to the transmit buffer. + * + * Arguments: tty pointer to tty information structure + * ch character to add to transmit buffer + * + * Return Value: None + */ +static int mgsl_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + int ret = 0; + + if (debug_level >= DEBUG_LEVEL_INFO) { + printk(KERN_DEBUG "%s(%d):mgsl_put_char(%d) on %s\n", + __FILE__, __LINE__, ch, info->device_name); + } + + if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char")) + return 0; + + if (!info->xmit_buf) + return 0; + + spin_lock_irqsave(&info->irq_spinlock, flags); + + if ((info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active) { + if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) { + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE-1; + info->xmit_cnt++; + ret = 1; + } + } + spin_unlock_irqrestore(&info->irq_spinlock, flags); + return ret; + +} /* end of mgsl_put_char() */ + +/* mgsl_flush_chars() + * + * Enable transmitter so remaining characters in the + * transmit buffer are sent. + * + * Arguments: tty pointer to tty information structure + * Return Value: None + */ +static void mgsl_flush_chars(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n", + __FILE__,__LINE__,info->device_name,info->xmit_cnt); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n", + __FILE__,__LINE__,info->device_name ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if (!info->tx_active) { + if ( (info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW) && info->xmit_cnt ) { + /* operating in synchronous (frame oriented) mode */ + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + } + usc_start_transmitter(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_flush_chars() */ + +/* mgsl_write() + * + * Send a block of data + * + * Arguments: + * + * tty pointer to tty information structure + * buf pointer to buffer containing send data + * count size of send data in bytes + * + * Return Value: number of characters written + */ +static int mgsl_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) count=%d\n", + __FILE__,__LINE__,info->device_name,count); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_write")) + goto cleanup; + + if (!info->xmit_buf) + goto cleanup; + + if ( info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + /* operating in synchronous (frame oriented) mode */ + /* operating in synchronous (frame oriented) mode */ + if (info->tx_active) { + + if ( info->params.mode == MGSL_MODE_HDLC ) { + ret = 0; + goto cleanup; + } + /* transmitter is actively sending data - + * if we have multiple transmit dma and + * holding buffers, attempt to queue this + * frame for transmission at a later time. + */ + if (info->tx_holding_count >= info->num_tx_holding_buffers ) { + /* no tx holding buffers available */ + ret = 0; + goto cleanup; + } + + /* queue transmit frame request */ + ret = count; + save_tx_buffer_request(info,buf,count); + + /* if we have sufficient tx dma buffers, + * load the next buffered tx request + */ + spin_lock_irqsave(&info->irq_spinlock,flags); + load_next_tx_holding_buffer(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + goto cleanup; + } + + /* if operating in HDLC LoopMode and the adapter */ + /* has yet to be inserted into the loop, we can't */ + /* transmit */ + + if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) && + !usc_loopmode_active(info) ) + { + ret = 0; + goto cleanup; + } + + if ( info->xmit_cnt ) { + /* Send accumulated from send_char() calls */ + /* as frame and wait before accepting more data. */ + ret = 0; + + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n", + __FILE__,__LINE__,info->device_name); + } else { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n", + __FILE__,__LINE__,info->device_name); + ret = count; + info->xmit_cnt = count; + mgsl_load_tx_dma_buffer(info,buf,count); + } + } else { + while (1) { + spin_lock_irqsave(&info->irq_spinlock,flags); + c = min_t(int, count, + min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + spin_unlock_irqrestore(&info->irq_spinlock,flags); + break; + } + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + buf += c; + count -= c; + ret += c; + } + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_active) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +cleanup: + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) returning=%d\n", + __FILE__,__LINE__,info->device_name,ret); + + return ret; + +} /* end of mgsl_write() */ + +/* mgsl_write_room() + * + * Return the count of free bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_write_room(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + int ret; + + if (mgsl_paranoia_check(info, tty->name, "mgsl_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_write_room(%s)=%d\n", + __FILE__,__LINE__, info->device_name,ret ); + + if ( info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return 0; + else + return HDLC_MAX_FRAME_SIZE; + } + + return ret; + +} /* end of mgsl_write_room() */ + +/* mgsl_chars_in_buffer() + * + * Return the count of bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_chars_in_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_chars_in_buffer")) + return 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n", + __FILE__,__LINE__, info->device_name,info->xmit_cnt ); + + if ( info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return info->max_frame_size; + else + return 0; + } + + return info->xmit_cnt; +} /* end of mgsl_chars_in_buffer() */ + +/* mgsl_flush_buffer() + * + * Discard all data in the send buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_flush_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_flush_buffer(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_buffer")) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + tty_wakeup(tty); +} + +/* mgsl_send_xchar() + * + * Send a high-priority XON/XOFF character + * + * Arguments: tty pointer to tty info structure + * ch character to send + * Return Value: None + */ +static void mgsl_send_xchar(struct tty_struct *tty, char ch) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_send_xchar(%s,%d)\n", + __FILE__,__LINE__, info->device_name, ch ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_send_xchar")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_send_xchar() */ + +/* mgsl_throttle() + * + * Signal remote device to throttle send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_throttle(struct tty_struct * tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_throttle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_throttle")) + return; + + if (I_IXOFF(tty)) + mgsl_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_throttle() */ + +/* mgsl_unthrottle() + * + * Signal remote device to stop throttling send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_unthrottle(struct tty_struct * tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_unthrottle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + mgsl_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + +} /* end of mgsl_unthrottle() */ + +/* mgsl_get_stats() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_icount pointer to buffer to hold returned stats + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *user_icount) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + if (!user_icount) { + memset(&info->icount, 0, sizeof(info->icount)); + } else { + mutex_lock(&info->port.mutex); + COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount)); + mutex_unlock(&info->port.mutex); + if (err) + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_stats() */ + +/* mgsl_get_params() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_params pointer to buffer to hold returned params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params) +{ + int err; + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + mutex_lock(&info->port.mutex); + COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); + mutex_unlock(&info->port.mutex); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_params() */ + +/* mgsl_set_params() + * + * set the serial parameters + * + * Arguments: + * + * info pointer to device instance data + * new_params user buffer containing new serial params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params) +{ + unsigned long flags; + MGSL_PARAMS tmp_params; + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__, + info->device_name ); + COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + mutex_lock(&info->port.mutex); + spin_lock_irqsave(&info->irq_spinlock,flags); + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_change_params(info); + mutex_unlock(&info->port.mutex); + + return 0; + +} /* end of mgsl_set_params() */ + +/* mgsl_get_txidle() + * + * get the current transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode pointer to buffer to hold returned idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_txidle(%s)=%d\n", + __FILE__,__LINE__, info->device_name, info->idle_mode); + + COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_txidle() */ + +/* mgsl_set_txidle() service ioctl to set transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode new idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__, + info->device_name, idle_mode ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->idle_mode = idle_mode; + usc_set_txidle( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_set_txidle() */ + +/* mgsl_txenable() + * + * enable or disable the transmitter + * + * Arguments: + * + * info pointer to device instance data + * enable 1 = enable, 0 = disable + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->tx_enabled ) { + + usc_start_transmitter(info); + /*-------------------------------------------------- + * if HDLC/SDLC Loop mode, attempt to insert the + * station in the 'loop' by setting CMR:13. Upon + * receipt of the next GoAhead (RxAbort) sequence, + * the OnLoop indicator (CCSR:7) should go active + * to indicate that we are on the loop + *--------------------------------------------------*/ + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_insert_request( info ); + } + } else { + if ( info->tx_enabled ) + usc_stop_transmitter(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txenable() */ + +/* mgsl_txabort() abort send HDLC frame + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txabort(struct mgsl_struct * info) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__, + info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) + { + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + else + usc_TCmd(info,TCmd_SendAbort); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txabort() */ + +/* mgsl_rxenable() enable or disable the receiver + * + * Arguments: info pointer to device instance data + * enable 1 = enable, 0 = disable + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_rxenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->rx_enabled ) + usc_start_receiver(info); + } else { + if ( info->rx_enabled ) + usc_stop_receiver(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_rxenable() */ + +/* mgsl_wait_event() wait for specified event to occur + * + * Arguments: info pointer to device instance data + * mask pointer to bitmask of events to wait for + * Return Value: 0 if successful and bit mask updated with + * of events triggerred, + * otherwise error code + */ +static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr) +{ + unsigned long flags; + int s; + int rc=0; + struct mgsl_icount cprev, cnow; + int events; + int mask; + struct _input_signal_events oldsigs, newsigs; + DECLARE_WAITQUEUE(wait, current); + + COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); + if (rc) { + return -EFAULT; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, + info->device_name, mask); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* return immediately if state matches requested events */ + usc_get_serial_signals(info); + s = info->serial_signals; + events = mask & + ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + + ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + + ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + + ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); + if (events) { + spin_unlock_irqrestore(&info->irq_spinlock,flags); + goto exit; + } + + /* save current irq counts */ + cprev = info->icount; + oldsigs = info->input_signal_events; + + /* enable hunt and idle irqs if needed */ + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + u16 oldreg = usc_InReg(info,RICR); + u16 newreg = oldreg + + (mask & MgslEvent_ExitHuntMode ? RXSTATUS_EXITED_HUNT:0) + + (mask & MgslEvent_IdleReceived ? RXSTATUS_IDLE_RECEIVED:0); + if (oldreg != newreg) + usc_OutReg(info, RICR, newreg); + } + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&info->event_wait_q, &wait); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get current irq counts */ + spin_lock_irqsave(&info->irq_spinlock,flags); + cnow = info->icount; + newsigs = info->input_signal_events; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* if no change, wait aborted for some reason */ + if (newsigs.dsr_up == oldsigs.dsr_up && + newsigs.dsr_down == oldsigs.dsr_down && + newsigs.dcd_up == oldsigs.dcd_up && + newsigs.dcd_down == oldsigs.dcd_down && + newsigs.cts_up == oldsigs.cts_up && + newsigs.cts_down == oldsigs.cts_down && + newsigs.ri_up == oldsigs.ri_up && + newsigs.ri_down == oldsigs.ri_down && + cnow.exithunt == cprev.exithunt && + cnow.rxidle == cprev.rxidle) { + rc = -EIO; + break; + } + + events = mask & + ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + + (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + + (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + + (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + + (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + + (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + + (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + + (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + + (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + + (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); + if (events) + break; + + cprev = cnow; + oldsigs = newsigs; + } + + remove_wait_queue(&info->event_wait_q, &wait); + set_current_state(TASK_RUNNING); + + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!waitqueue_active(&info->event_wait_q)) { + /* disable enable exit hunt mode/idle rcvd IRQs */ + usc_OutReg(info, RICR, usc_InReg(info,RICR) & + ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +exit: + if ( rc == 0 ) + PUT_USER(rc, events, mask_ptr); + + return rc; + +} /* end of mgsl_wait_event() */ + +static int modem_input_wait(struct mgsl_struct *info,int arg) +{ + unsigned long flags; + int rc; + struct mgsl_icount cprev, cnow; + DECLARE_WAITQUEUE(wait, current); + + /* save current irq counts */ + spin_lock_irqsave(&info->irq_spinlock,flags); + cprev = info->icount; + add_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get new irq counts */ + spin_lock_irqsave(&info->irq_spinlock,flags); + cnow = info->icount; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* if no change, wait aborted for some reason */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; + break; + } + + /* check for change in caller specified modem input */ + if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || + (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || + (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || + (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { + rc = 0; + break; + } + + cprev = cnow; + } + remove_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_RUNNING); + return rc; +} + +/* return the state of the serial control and status signals + */ +static int tiocmget(struct tty_struct *tty) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned int result; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + + ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + + ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + + ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + + ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + + ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tiocmget() value=%08X\n", + __FILE__,__LINE__, info->device_name, result ); + return result; +} + +/* set modem control signals (DTR/RTS) + */ +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tiocmset(%x,%x)\n", + __FILE__,__LINE__,info->device_name, set, clear); + + if (set & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + if (set & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + if (clear & TIOCM_RTS) + info->serial_signals &= ~SerialSignal_RTS; + if (clear & TIOCM_DTR) + info->serial_signals &= ~SerialSignal_DTR; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; +} + +/* mgsl_break() Set or clear transmit break condition + * + * Arguments: tty pointer to tty instance data + * break_state -1=set break condition, 0=clear + * Return Value: error code + */ +static int mgsl_break(struct tty_struct *tty, int break_state) +{ + struct mgsl_struct * info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_break(%s,%d)\n", + __FILE__,__LINE__, info->device_name, break_state); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_break")) + return -EINVAL; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (break_state == -1) + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7)); + else + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_break() */ + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int msgl_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) + +{ + struct mgsl_struct * info = tty->driver_data; + struct mgsl_icount cnow; /* kernel counter temps */ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + return 0; +} + +/* mgsl_ioctl() Service an IOCTL request + * + * Arguments: + * + * tty pointer to tty instance data + * cmd IOCTL command code + * arg command argument/context + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct mgsl_struct * info = tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, + info->device_name, cmd ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + return mgsl_ioctl_common(info, cmd, arg); +} + +static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + + switch (cmd) { + case MGSL_IOCGPARAMS: + return mgsl_get_params(info, argp); + case MGSL_IOCSPARAMS: + return mgsl_set_params(info, argp); + case MGSL_IOCGTXIDLE: + return mgsl_get_txidle(info, argp); + case MGSL_IOCSTXIDLE: + return mgsl_set_txidle(info,(int)arg); + case MGSL_IOCTXENABLE: + return mgsl_txenable(info,(int)arg); + case MGSL_IOCRXENABLE: + return mgsl_rxenable(info,(int)arg); + case MGSL_IOCTXABORT: + return mgsl_txabort(info); + case MGSL_IOCGSTATS: + return mgsl_get_stats(info, argp); + case MGSL_IOCWAITEVENT: + return mgsl_wait_event(info, argp); + case MGSL_IOCLOOPTXDONE: + return mgsl_loopmode_send_done(info); + /* Wait for modem input (DCD,RI,DSR,CTS) change + * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS) + */ + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* mgsl_set_termios() + * + * Set new termios settings + * + * Arguments: + * + * tty pointer to tty structure + * termios pointer to buffer to hold returned old termios + * + * Return Value: None + */ +static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct mgsl_struct *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__, + tty->driver->name ); + + mgsl_change_params(info); + + /* Handle transition to B0 status */ + if (old_termios->c_cflag & CBAUD && + !(tty->termios->c_cflag & CBAUD)) { + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + tty->termios->c_cflag & CBAUD) { + info->serial_signals |= SerialSignal_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->serial_signals |= SerialSignal_RTS; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle turning off CRTSCTS */ + if (old_termios->c_cflag & CRTSCTS && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mgsl_start(tty); + } + +} /* end of mgsl_set_termios() */ + +/* mgsl_close() + * + * Called when port is closed. Wait for remaining data to be + * sent. Disable port and free resources. + * + * Arguments: + * + * tty pointer to open tty structure + * filp pointer to open file object + * + * Return Value: None + */ +static void mgsl_close(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct * info = tty->driver_data; + + if (mgsl_paranoia_check(info, tty->name, "mgsl_close")) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) entry, count=%d\n", + __FILE__,__LINE__, info->device_name, info->port.count); + + if (tty_port_close_start(&info->port, tty, filp) == 0) + goto cleanup; + + mutex_lock(&info->port.mutex); + if (info->port.flags & ASYNC_INITIALIZED) + mgsl_wait_until_sent(tty, info->timeout); + mgsl_flush_buffer(tty); + tty_ldisc_flush(tty); + shutdown(info); + mutex_unlock(&info->port.mutex); + + tty_port_close_end(&info->port, tty); + info->port.tty = NULL; +cleanup: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__, + tty->driver->name, info->port.count); + +} /* end of mgsl_close() */ + +/* mgsl_wait_until_sent() + * + * Wait until the transmitter is empty. + * + * Arguments: + * + * tty pointer to tty info structure + * timeout time to wait for send completion + * + * Return Value: None + */ +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct mgsl_struct * info = tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (!info ) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_wait_until_sent")) + return; + + if (!(info->port.flags & ASYNC_INITIALIZED)) + goto exit; + + orig_jiffies = jiffies; + + /* Set check interval to 1/5 of estimated time to + * send a character, and make it at least 1. The check + * interval should also be less than the timeout. + * Note: use tight timings here to satisfy the NIST-PCTS. + */ + + if ( info->params.data_rate ) { + char_time = info->timeout/(32 * 5); + if (!char_time) + char_time++; + } else + char_time = 1; + + if (timeout) + char_time = min_t(unsigned long, char_time, timeout); + + if ( info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + while (info->tx_active) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + } else { + while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && + info->tx_enabled) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + } + +exit: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) exit\n", + __FILE__,__LINE__, info->device_name ); + +} /* end of mgsl_wait_until_sent() */ + +/* mgsl_hangup() + * + * Called by tty_hangup() when a hangup is signaled. + * This is the same as to closing all open files for the port. + * + * Arguments: tty pointer to associated tty object + * Return Value: None + */ +static void mgsl_hangup(struct tty_struct *tty) +{ + struct mgsl_struct * info = tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_hangup(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->name, "mgsl_hangup")) + return; + + mgsl_flush_buffer(tty); + shutdown(info); + + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + + wake_up_interruptible(&info->port.open_wait); + +} /* end of mgsl_hangup() */ + +/* + * carrier_raised() + * + * Return true if carrier is raised + */ + +static int carrier_raised(struct tty_port *port) +{ + unsigned long flags; + struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); + + spin_lock_irqsave(&info->irq_spinlock, flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock, flags); + return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; +} + +static void dtr_rts(struct tty_port *port, int on) +{ + struct mgsl_struct *info = container_of(port, struct mgsl_struct, port); + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (on) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); +} + + +/* block_til_ready() + * + * Block the current process until the specified port + * is ready to be opened. + * + * Arguments: + * + * tty pointer to tty info structure + * filp pointer to open file object + * info pointer to device instance data + * + * Return Value: 0 if success, otherwise error code + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct mgsl_struct *info) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + bool do_clocal = false; + bool extra_count = false; + unsigned long flags; + int dcd; + struct tty_port *port = &info->port; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready on %s\n", + __FILE__,__LINE__, tty->driver->name ); + + if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + /* nonblock mode is set or port is not enabled */ + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = true; + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * mgsl_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready before block on %s count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + spin_lock_irqsave(&info->irq_spinlock, flags); + if (!tty_hung_up_p(filp)) { + extra_count = true; + port->count--; + } + spin_unlock_irqrestore(&info->irq_spinlock, flags); + port->blocked_open++; + + while (1) { + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); + + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + dcd = tty_port_carrier_raised(&info->port); + + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd)) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + tty_unlock(); + schedule(); + tty_lock(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + + /* FIXME: Racy on hangup during close wait */ + if (extra_count) + port->count++; + port->blocked_open--; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready after blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + if (!retval) + port->flags |= ASYNC_NORMAL_ACTIVE; + + return retval; + +} /* end of block_til_ready() */ + +/* mgsl_open() + * + * Called when a port is opened. Init and enable port. + * Perform serial-specific initialization for the tty structure. + * + * Arguments: tty pointer to tty info structure + * filp associated file pointer + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_open(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct *info; + int retval, line; + unsigned long flags; + + /* verify range of specified line number */ + line = tty->index; + if ((line < 0) || (line >= mgsl_device_count)) { + printk("%s(%d):mgsl_open with invalid line #%d.\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + /* find the info structure for the specified line */ + info = mgsl_device_list; + while(info && info->line != line) + info = info->next_device; + if (mgsl_paranoia_check(info, tty->name, "mgsl_open")) + return -ENODEV; + + tty->driver_data = info; + info->port.tty = tty; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s), old ref count = %d\n", + __FILE__,__LINE__,tty->driver->name, info->port.count); + + /* If port is closing, signal caller to try again */ + if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ + if (info->port.flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->port.close_wait); + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); + goto cleanup; + } + + info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + spin_lock_irqsave(&info->netlock, flags); + if (info->netcount) { + retval = -EBUSY; + spin_unlock_irqrestore(&info->netlock, flags); + goto cleanup; + } + info->port.count++; + spin_unlock_irqrestore(&info->netlock, flags); + + if (info->port.count == 1) { + /* 1st open on this device, init hardware */ + retval = startup(info); + if (retval < 0) + goto cleanup; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready(%s) returned %d\n", + __FILE__,__LINE__, info->device_name, retval); + goto cleanup; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s) success\n", + __FILE__,__LINE__, info->device_name); + retval = 0; + +cleanup: + if (retval) { + if (tty->count == 1) + info->port.tty = NULL; /* tty layer will release tty struct */ + if(info->port.count) + info->port.count--; + } + + return retval; + +} /* end of mgsl_open() */ + +/* + * /proc fs routines.... + */ + +static inline void line_info(struct seq_file *m, struct mgsl_struct *info) +{ + char stat_buf[30]; + unsigned long flags; + + if (info->bus_type == MGSL_BUS_TYPE_PCI) { + seq_printf(m, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", + info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base); + } else { + seq_printf(m, "%s:(E)ISA io:%04X irq:%d dma:%d", + info->device_name, info->io_base, + info->irq_level, info->dma_level); + } + + /* output current serial signal states */ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (info->serial_signals & SerialSignal_RTS) + strcat(stat_buf, "|RTS"); + if (info->serial_signals & SerialSignal_CTS) + strcat(stat_buf, "|CTS"); + if (info->serial_signals & SerialSignal_DTR) + strcat(stat_buf, "|DTR"); + if (info->serial_signals & SerialSignal_DSR) + strcat(stat_buf, "|DSR"); + if (info->serial_signals & SerialSignal_DCD) + strcat(stat_buf, "|CD"); + if (info->serial_signals & SerialSignal_RI) + strcat(stat_buf, "|RI"); + + if (info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + seq_printf(m, " HDLC txok:%d rxok:%d", + info->icount.txok, info->icount.rxok); + if (info->icount.txunder) + seq_printf(m, " txunder:%d", info->icount.txunder); + if (info->icount.txabort) + seq_printf(m, " txabort:%d", info->icount.txabort); + if (info->icount.rxshort) + seq_printf(m, " rxshort:%d", info->icount.rxshort); + if (info->icount.rxlong) + seq_printf(m, " rxlong:%d", info->icount.rxlong); + if (info->icount.rxover) + seq_printf(m, " rxover:%d", info->icount.rxover); + if (info->icount.rxcrc) + seq_printf(m, " rxcrc:%d", info->icount.rxcrc); + } else { + seq_printf(m, " ASYNC tx:%d rx:%d", + info->icount.tx, info->icount.rx); + if (info->icount.frame) + seq_printf(m, " fe:%d", info->icount.frame); + if (info->icount.parity) + seq_printf(m, " pe:%d", info->icount.parity); + if (info->icount.brk) + seq_printf(m, " brk:%d", info->icount.brk); + if (info->icount.overrun) + seq_printf(m, " oe:%d", info->icount.overrun); + } + + /* Append serial signal status to end */ + seq_printf(m, " %s\n", stat_buf+1); + + seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + info->tx_active,info->bh_requested,info->bh_running, + info->pending_bh); + + spin_lock_irqsave(&info->irq_spinlock,flags); + { + u16 Tcsr = usc_InReg( info, TCSR ); + u16 Tdmr = usc_InDmaReg( info, TDMR ); + u16 Ticr = usc_InReg( info, TICR ); + u16 Rscr = usc_InReg( info, RCSR ); + u16 Rdmr = usc_InDmaReg( info, RDMR ); + u16 Ricr = usc_InReg( info, RICR ); + u16 Icr = usc_InReg( info, ICR ); + u16 Dccr = usc_InReg( info, DCCR ); + u16 Tmr = usc_InReg( info, TMR ); + u16 Tccr = usc_InReg( info, TCCR ); + u16 Ccar = inw( info->io_base + CCAR ); + seq_printf(m, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" + "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n", + Tcsr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar ); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); +} + +/* Called to print information about devices */ +static int mgsl_proc_show(struct seq_file *m, void *v) +{ + struct mgsl_struct *info; + + seq_printf(m, "synclink driver:%s\n", driver_version); + + info = mgsl_device_list; + while( info ) { + line_info(m, info); + info = info->next_device; + } + return 0; +} + +static int mgsl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mgsl_proc_show, NULL); +} + +static const struct file_operations mgsl_proc_fops = { + .owner = THIS_MODULE, + .open = mgsl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* mgsl_allocate_dma_buffers() + * + * Allocate and format DMA buffers (ISA adapter) + * or format shared memory buffers (PCI adapter). + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +static int mgsl_allocate_dma_buffers(struct mgsl_struct *info) +{ + unsigned short BuffersPerFrame; + + info->last_mem_alloc = 0; + + /* Calculate the number of DMA buffers necessary to hold the */ + /* largest allowable frame size. Note: If the max frame size is */ + /* not an even multiple of the DMA buffer size then we need to */ + /* round the buffer count per frame up one. */ + + BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE); + if ( info->max_frame_size % DMABUFFERSIZE ) + BuffersPerFrame++; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* + * The PCI adapter has 256KBytes of shared memory to use. + * This is 64 PAGE_SIZE buffers. + * + * The first page is used for padding at this time so the + * buffer list does not begin at offset 0 of the PCI + * adapter's shared memory. + * + * The 2nd page is used for the buffer list. A 4K buffer + * list can hold 128 DMA_BUFFER structures at 32 bytes + * each. + * + * This leaves 62 4K pages. + * + * The next N pages are used for transmit frame(s). We + * reserve enough 4K page blocks to hold the required + * number of transmit dma buffers (num_tx_dma_buffers), + * each of MaxFrameSize size. + * + * Of the remaining pages (62-N), determine how many can + * be used to receive full MaxFrameSize inbound frames + */ + info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame; + info->rx_buffer_count = 62 - info->tx_buffer_count; + } else { + /* Calculate the number of PAGE_SIZE buffers needed for */ + /* receive and transmit DMA buffers. */ + + + /* Calculate the number of DMA buffers necessary to */ + /* hold 7 max size receive frames and one max size transmit frame. */ + /* The receive buffer count is bumped by one so we avoid an */ + /* End of List condition if all receive buffers are used when */ + /* using linked list DMA buffers. */ + + info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame; + info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6; + + /* + * limit total TxBuffers & RxBuffers to 62 4K total + * (ala PCI Allocation) + */ + + if ( (info->tx_buffer_count + info->rx_buffer_count) > 62 ) + info->rx_buffer_count = 62 - info->tx_buffer_count; + + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n", + __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count); + + if ( mgsl_alloc_buffer_list_memory( info ) < 0 || + mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || + mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0 || + mgsl_alloc_intermediate_rxbuffer_memory(info) < 0 || + mgsl_alloc_intermediate_txbuffer_memory(info) < 0 ) { + printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__); + return -ENOMEM; + } + + mgsl_reset_rx_dma_buffers( info ); + mgsl_reset_tx_dma_buffers( info ); + + return 0; + +} /* end of mgsl_allocate_dma_buffers() */ + +/* + * mgsl_alloc_buffer_list_memory() + * + * Allocate a common DMA buffer for use as the + * receive and transmit buffer lists. + * + * A buffer list is a set of buffer entries where each entry contains + * a pointer to an actual buffer and a pointer to the next buffer entry + * (plus some other info about the buffer). + * + * The buffer entries for a list are built to form a circular list so + * that when the entire list has been traversed you start back at the + * beginning. + * + * This function allocates memory for just the buffer entries. + * The links (pointer to next entry) are filled in with the physical + * address of the next entry so the adapter can navigate the list + * using bus master DMA. The pointers to the actual buffers are filled + * out later when the actual buffers are allocated. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info ) +{ + unsigned int i; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory. */ + info->buffer_list = info->memory_base + info->last_mem_alloc; + info->buffer_list_phys = info->last_mem_alloc; + info->last_mem_alloc += BUFFERLISTSIZE; + } else { + /* ISA adapter uses system memory. */ + /* The buffer lists are allocated as a common buffer that both */ + /* the processor and adapter can access. This allows the driver to */ + /* inspect portions of the buffer while other portions are being */ + /* updated by the adapter using Bus Master DMA. */ + + info->buffer_list = dma_alloc_coherent(NULL, BUFFERLISTSIZE, &info->buffer_list_dma_addr, GFP_KERNEL); + if (info->buffer_list == NULL) + return -ENOMEM; + info->buffer_list_phys = (u32)(info->buffer_list_dma_addr); + } + + /* We got the memory for the buffer entry lists. */ + /* Initialize the memory block to all zeros. */ + memset( info->buffer_list, 0, BUFFERLISTSIZE ); + + /* Save virtual address pointers to the receive and */ + /* transmit buffer lists. (Receive 1st). These pointers will */ + /* be used by the processor to access the lists. */ + info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list += info->rx_buffer_count; + + /* + * Build the links for the buffer entry lists such that + * two circular lists are built. (Transmit and Receive). + * + * Note: the links are physical addresses + * which are read by the adapter to determine the next + * buffer entry to use. + */ + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->rx_buffer_list[i].phys_entry = + info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->rx_buffer_list[i].link = info->buffer_list_phys; + + if ( i < info->rx_buffer_count - 1 ) + info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + for ( i = 0; i < info->tx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->tx_buffer_list[i].phys_entry = info->buffer_list_phys + + ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->tx_buffer_list[i].link = info->buffer_list_phys + + info->rx_buffer_count * sizeof(DMABUFFERENTRY); + + if ( i < info->tx_buffer_count - 1 ) + info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + return 0; + +} /* end of mgsl_alloc_buffer_list_memory() */ + +/* Free DMA buffers allocated for use as the + * receive and transmit buffer lists. + * Warning: + * + * The data transfer buffers associated with the buffer list + * MUST be freed before freeing the buffer list itself because + * the buffer list contains the information necessary to free + * the individual buffers! + */ +static void mgsl_free_buffer_list_memory( struct mgsl_struct *info ) +{ + if (info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI) + dma_free_coherent(NULL, BUFFERLISTSIZE, info->buffer_list, info->buffer_list_dma_addr); + + info->buffer_list = NULL; + info->rx_buffer_list = NULL; + info->tx_buffer_list = NULL; + +} /* end of mgsl_free_buffer_list_memory() */ + +/* + * mgsl_alloc_frame_memory() + * + * Allocate the frame DMA buffers used by the specified buffer list. + * Each DMA buffer will be one memory page in size. This is necessary + * because memory can fragment enough that it may be impossible + * contiguous pages. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: 0 if success, otherwise -ENOMEM + */ +static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount) +{ + int i; + u32 phys_addr; + + /* Allocate page sized buffers for the receive buffer list */ + + for ( i = 0; i < Buffercount; i++ ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory buffers. */ + BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc; + phys_addr = info->last_mem_alloc; + info->last_mem_alloc += DMABUFFERSIZE; + } else { + /* ISA adapter uses system memory. */ + BufferList[i].virt_addr = dma_alloc_coherent(NULL, DMABUFFERSIZE, &BufferList[i].dma_addr, GFP_KERNEL); + if (BufferList[i].virt_addr == NULL) + return -ENOMEM; + phys_addr = (u32)(BufferList[i].dma_addr); + } + BufferList[i].phys_addr = phys_addr; + } + + return 0; + +} /* end of mgsl_alloc_frame_memory() */ + +/* + * mgsl_free_frame_memory() + * + * Free the buffers associated with + * each buffer entry of a buffer list. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: None + */ +static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount) +{ + int i; + + if ( BufferList ) { + for ( i = 0 ; i < Buffercount ; i++ ) { + if ( BufferList[i].virt_addr ) { + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + dma_free_coherent(NULL, DMABUFFERSIZE, BufferList[i].virt_addr, BufferList[i].dma_addr); + BufferList[i].virt_addr = NULL; + } + } + } + +} /* end of mgsl_free_frame_memory() */ + +/* mgsl_free_dma_buffers() + * + * Free DMA buffers + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_free_dma_buffers( struct mgsl_struct *info ) +{ + mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count ); + mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count ); + mgsl_free_buffer_list_memory( info ); + +} /* end of mgsl_free_dma_buffers() */ + + +/* + * mgsl_alloc_intermediate_rxbuffer_memory() + * + * Allocate a buffer large enough to hold max_frame_size. This buffer + * is used to pass an assembled frame to the line discipline. + * + * Arguments: + * + * info pointer to device instance data + * + * Return Value: 0 if success, otherwise -ENOMEM + */ +static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info) +{ + info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA); + if ( info->intermediate_rxbuffer == NULL ) + return -ENOMEM; + + return 0; + +} /* end of mgsl_alloc_intermediate_rxbuffer_memory() */ + +/* + * mgsl_free_intermediate_rxbuffer_memory() + * + * + * Arguments: + * + * info pointer to device instance data + * + * Return Value: None + */ +static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info) +{ + kfree(info->intermediate_rxbuffer); + info->intermediate_rxbuffer = NULL; + +} /* end of mgsl_free_intermediate_rxbuffer_memory() */ + +/* + * mgsl_alloc_intermediate_txbuffer_memory() + * + * Allocate intermdiate transmit buffer(s) large enough to hold max_frame_size. + * This buffer is used to load transmit frames into the adapter's dma transfer + * buffers when there is sufficient space. + * + * Arguments: + * + * info pointer to device instance data + * + * Return Value: 0 if success, otherwise -ENOMEM + */ +static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info) +{ + int i; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s %s(%d) allocating %d tx holding buffers\n", + info->device_name, __FILE__,__LINE__,info->num_tx_holding_buffers); + + memset(info->tx_holding_buffers,0,sizeof(info->tx_holding_buffers)); + + for ( i=0; inum_tx_holding_buffers; ++i) { + info->tx_holding_buffers[i].buffer = + kmalloc(info->max_frame_size, GFP_KERNEL); + if (info->tx_holding_buffers[i].buffer == NULL) { + for (--i; i >= 0; i--) { + kfree(info->tx_holding_buffers[i].buffer); + info->tx_holding_buffers[i].buffer = NULL; + } + return -ENOMEM; + } + } + + return 0; + +} /* end of mgsl_alloc_intermediate_txbuffer_memory() */ + +/* + * mgsl_free_intermediate_txbuffer_memory() + * + * + * Arguments: + * + * info pointer to device instance data + * + * Return Value: None + */ +static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info) +{ + int i; + + for ( i=0; inum_tx_holding_buffers; ++i ) { + kfree(info->tx_holding_buffers[i].buffer); + info->tx_holding_buffers[i].buffer = NULL; + } + + info->get_tx_holding_index = 0; + info->put_tx_holding_index = 0; + info->tx_holding_count = 0; + +} /* end of mgsl_free_intermediate_txbuffer_memory() */ + + +/* + * load_next_tx_holding_buffer() + * + * attempts to load the next buffered tx request into the + * tx dma buffers + * + * Arguments: + * + * info pointer to device instance data + * + * Return Value: true if next buffered tx request loaded + * into adapter's tx dma buffer, + * false otherwise + */ +static bool load_next_tx_holding_buffer(struct mgsl_struct *info) +{ + bool ret = false; + + if ( info->tx_holding_count ) { + /* determine if we have enough tx dma buffers + * to accommodate the next tx frame + */ + struct tx_holding_buffer *ptx = + &info->tx_holding_buffers[info->get_tx_holding_index]; + int num_free = num_free_tx_dma_buffers(info); + int num_needed = ptx->buffer_size / DMABUFFERSIZE; + if ( ptx->buffer_size % DMABUFFERSIZE ) + ++num_needed; + + if (num_needed <= num_free) { + info->xmit_cnt = ptx->buffer_size; + mgsl_load_tx_dma_buffer(info,ptx->buffer,ptx->buffer_size); + + --info->tx_holding_count; + if ( ++info->get_tx_holding_index >= info->num_tx_holding_buffers) + info->get_tx_holding_index=0; + + /* restart transmit timer */ + mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000)); + + ret = true; + } + } + + return ret; +} + +/* + * save_tx_buffer_request() + * + * attempt to store transmit frame request for later transmission + * + * Arguments: + * + * info pointer to device instance data + * Buffer pointer to buffer containing frame to load + * BufferSize size in bytes of frame in Buffer + * + * Return Value: 1 if able to store, 0 otherwise + */ +static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize) +{ + struct tx_holding_buffer *ptx; + + if ( info->tx_holding_count >= info->num_tx_holding_buffers ) { + return 0; /* all buffers in use */ + } + + ptx = &info->tx_holding_buffers[info->put_tx_holding_index]; + ptx->buffer_size = BufferSize; + memcpy( ptx->buffer, Buffer, BufferSize); + + ++info->tx_holding_count; + if ( ++info->put_tx_holding_index >= info->num_tx_holding_buffers) + info->put_tx_holding_index=0; + + return 1; +} + +static int mgsl_claim_resources(struct mgsl_struct *info) +{ + if (request_region(info->io_base,info->io_addr_size,"synclink") == NULL) { + printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->io_base); + return -ENODEV; + } + info->io_addr_requested = true; + + if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags, + info->device_name, info ) < 0 ) { + printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, info->irq_level ); + goto errout; + } + info->irq_requested = true; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + if (request_mem_region(info->phys_memory_base,0x40000,"synclink") == NULL) { + printk( "%s(%d):mem addr conflict device %s Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base); + goto errout; + } + info->shared_mem_requested = true; + if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclink") == NULL) { + printk( "%s(%d):lcr mem addr conflict device %s Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base + info->lcr_offset); + goto errout; + } + info->lcr_mem_requested = true; + + info->memory_base = ioremap_nocache(info->phys_memory_base, + 0x40000); + if (!info->memory_base) { + printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + goto errout; + } + + if ( !mgsl_memory_test(info) ) { + printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + goto errout; + } + + info->lcr_base = ioremap_nocache(info->phys_lcr_base, + PAGE_SIZE); + if (!info->lcr_base) { + printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); + goto errout; + } + info->lcr_base += info->lcr_offset; + + } else { + /* claim DMA channel */ + + if (request_dma(info->dma_level,info->device_name) < 0){ + printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + info->dma_requested = true; + + /* ISA adapter uses bus master DMA */ + set_dma_mode(info->dma_level,DMA_MODE_CASCADE); + enable_dma(info->dma_level); + } + + if ( mgsl_allocate_dma_buffers(info) < 0 ) { + printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + goto errout; + } + + return 0; +errout: + mgsl_release_resources(info); + return -ENODEV; + +} /* end of mgsl_claim_resources() */ + +static void mgsl_release_resources(struct mgsl_struct *info) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) entry\n", + __FILE__,__LINE__,info->device_name ); + + if ( info->irq_requested ) { + free_irq(info->irq_level, info); + info->irq_requested = false; + } + if ( info->dma_requested ) { + disable_dma(info->dma_level); + free_dma(info->dma_level); + info->dma_requested = false; + } + mgsl_free_dma_buffers(info); + mgsl_free_intermediate_rxbuffer_memory(info); + mgsl_free_intermediate_txbuffer_memory(info); + + if ( info->io_addr_requested ) { + release_region(info->io_base,info->io_addr_size); + info->io_addr_requested = false; + } + if ( info->shared_mem_requested ) { + release_mem_region(info->phys_memory_base,0x40000); + info->shared_mem_requested = false; + } + if ( info->lcr_mem_requested ) { + release_mem_region(info->phys_lcr_base + info->lcr_offset,128); + info->lcr_mem_requested = false; + } + if (info->memory_base){ + iounmap(info->memory_base); + info->memory_base = NULL; + } + if (info->lcr_base){ + iounmap(info->lcr_base - info->lcr_offset); + info->lcr_base = NULL; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) exit\n", + __FILE__,__LINE__,info->device_name ); + +} /* end of mgsl_release_resources() */ + +/* mgsl_add_device() + * + * Add the specified device instance data structure to the + * global linked list of devices and increment the device count. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_add_device( struct mgsl_struct *info ) +{ + info->next_device = NULL; + info->line = mgsl_device_count; + sprintf(info->device_name,"ttySL%d",info->line); + + if (info->line < MAX_TOTAL_DEVICES) { + if (maxframe[info->line]) + info->max_frame_size = maxframe[info->line]; + + if (txdmabufs[info->line]) { + info->num_tx_dma_buffers = txdmabufs[info->line]; + if (info->num_tx_dma_buffers < 1) + info->num_tx_dma_buffers = 1; + } + + if (txholdbufs[info->line]) { + info->num_tx_holding_buffers = txholdbufs[info->line]; + if (info->num_tx_holding_buffers < 1) + info->num_tx_holding_buffers = 1; + else if (info->num_tx_holding_buffers > MAX_TX_HOLDING_BUFFERS) + info->num_tx_holding_buffers = MAX_TX_HOLDING_BUFFERS; + } + } + + mgsl_device_count++; + + if ( !mgsl_device_list ) + mgsl_device_list = info; + else { + struct mgsl_struct *current_dev = mgsl_device_list; + while( current_dev->next_device ) + current_dev = current_dev->next_device; + current_dev->next_device = info; + } + + if ( info->max_frame_size < 4096 ) + info->max_frame_size = 4096; + else if ( info->max_frame_size > 65535 ) + info->max_frame_size = 65535; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + printk( "SyncLink PCI v%d %s: IO=%04X IRQ=%d Mem=%08X,%08X MaxFrameSize=%u\n", + info->hw_version + 1, info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base, + info->max_frame_size ); + } else { + printk( "SyncLink ISA %s: IO=%04X IRQ=%d DMA=%d MaxFrameSize=%u\n", + info->device_name, info->io_base, info->irq_level, info->dma_level, + info->max_frame_size ); + } + +#if SYNCLINK_GENERIC_HDLC + hdlcdev_init(info); +#endif + +} /* end of mgsl_add_device() */ + +static const struct tty_port_operations mgsl_port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, +}; + + +/* mgsl_allocate_device() + * + * Allocate and initialize a device instance structure + * + * Arguments: none + * Return Value: pointer to mgsl_struct if success, otherwise NULL + */ +static struct mgsl_struct* mgsl_allocate_device(void) +{ + struct mgsl_struct *info; + + info = kzalloc(sizeof(struct mgsl_struct), + GFP_KERNEL); + + if (!info) { + printk("Error can't allocate device instance data\n"); + } else { + tty_port_init(&info->port); + info->port.ops = &mgsl_port_ops; + info->magic = MGSL_MAGIC; + INIT_WORK(&info->task, mgsl_bh_handler); + info->max_frame_size = 4096; + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; + init_waitqueue_head(&info->status_event_wait_q); + init_waitqueue_head(&info->event_wait_q); + spin_lock_init(&info->irq_spinlock); + spin_lock_init(&info->netlock); + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + info->idle_mode = HDLC_TXIDLE_FLAGS; + info->num_tx_dma_buffers = 1; + info->num_tx_holding_buffers = 0; + } + + return info; + +} /* end of mgsl_allocate_device()*/ + +static const struct tty_operations mgsl_ops = { + .open = mgsl_open, + .close = mgsl_close, + .write = mgsl_write, + .put_char = mgsl_put_char, + .flush_chars = mgsl_flush_chars, + .write_room = mgsl_write_room, + .chars_in_buffer = mgsl_chars_in_buffer, + .flush_buffer = mgsl_flush_buffer, + .ioctl = mgsl_ioctl, + .throttle = mgsl_throttle, + .unthrottle = mgsl_unthrottle, + .send_xchar = mgsl_send_xchar, + .break_ctl = mgsl_break, + .wait_until_sent = mgsl_wait_until_sent, + .set_termios = mgsl_set_termios, + .stop = mgsl_stop, + .start = mgsl_start, + .hangup = mgsl_hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, + .get_icount = msgl_get_icount, + .proc_fops = &mgsl_proc_fops, +}; + +/* + * perform tty device initialization + */ +static int mgsl_init_tty(void) +{ + int rc; + + serial_driver = alloc_tty_driver(128); + if (!serial_driver) + return -ENOMEM; + + serial_driver->owner = THIS_MODULE; + serial_driver->driver_name = "synclink"; + serial_driver->name = "ttySL"; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + 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->init_termios.c_ispeed = 9600; + serial_driver->init_termios.c_ospeed = 9600; + serial_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(serial_driver, &mgsl_ops); + if ((rc = tty_register_driver(serial_driver)) < 0) { + printk("%s(%d):Couldn't register serial driver\n", + __FILE__,__LINE__); + put_tty_driver(serial_driver); + serial_driver = NULL; + return rc; + } + + printk("%s %s, tty major#%d\n", + driver_name, driver_version, + serial_driver->major); + return 0; +} + +/* enumerate user specified ISA adapters + */ +static void mgsl_enum_isa_devices(void) +{ + struct mgsl_struct *info; + int i; + + /* Check for user specified ISA devices */ + + for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("ISA device specified io=%04X,irq=%d,dma=%d\n", + io[i], irq[i], dma[i] ); + + info = mgsl_allocate_device(); + if ( !info ) { + /* error allocating device instance data */ + if ( debug_level >= DEBUG_LEVEL_ERROR ) + printk( "can't allocate device instance data.\n"); + continue; + } + + /* Copy user configuration info to device instance data */ + info->io_base = (unsigned int)io[i]; + info->irq_level = (unsigned int)irq[i]; + info->irq_level = irq_canonicalize(info->irq_level); + info->dma_level = (unsigned int)dma[i]; + info->bus_type = MGSL_BUS_TYPE_ISA; + info->io_addr_size = 16; + info->irq_flags = 0; + + mgsl_add_device( info ); + } +} + +static void synclink_cleanup(void) +{ + int rc; + struct mgsl_struct *info; + struct mgsl_struct *tmp; + + printk("Unloading %s: %s\n", driver_name, driver_version); + + if (serial_driver) { + if ((rc = tty_unregister_driver(serial_driver))) + printk("%s(%d) failed to unregister tty driver err=%d\n", + __FILE__,__LINE__,rc); + put_tty_driver(serial_driver); + } + + info = mgsl_device_list; + while(info) { +#if SYNCLINK_GENERIC_HDLC + hdlcdev_exit(info); +#endif + mgsl_release_resources(info); + tmp = info; + info = info->next_device; + kfree(tmp); + } + + if (pci_registered) + pci_unregister_driver(&synclink_pci_driver); +} + +static int __init synclink_init(void) +{ + int rc; + + if (break_on_load) { + mgsl_get_text_ptr(); + BREAKPOINT(); + } + + printk("%s %s\n", driver_name, driver_version); + + mgsl_enum_isa_devices(); + if ((rc = pci_register_driver(&synclink_pci_driver)) < 0) + printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc); + else + pci_registered = true; + + if ((rc = mgsl_init_tty()) < 0) + goto error; + + return 0; + +error: + synclink_cleanup(); + return rc; +} + +static void __exit synclink_exit(void) +{ + synclink_cleanup(); +} + +module_init(synclink_init); +module_exit(synclink_exit); + +/* + * usc_RTCmd() + * + * Issue a USC Receive/Transmit command to the + * Channel Command/Address Register (CCAR). + * + * Notes: + * + * The command is encoded in the most significant 5 bits <15..11> + * of the CCAR value. Bits <10..7> of the CCAR must be preserved + * and Bits <6..0> must be written as zeros. + * + * Arguments: + * + * info pointer to device information structure + * Cmd command mask (use symbolic macros) + * + * Return Value: + * + * None + */ +static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* output command to CCAR in bits <15..11> */ + /* preserve bits <10..7>, bits <6..0> must be zero */ + + outw( Cmd + info->loopback_bits, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_RTCmd() */ + +/* + * usc_DmaCmd() + * + * Issue a DMA command to the DMA Command/Address Register (DCAR). + * + * Arguments: + * + * info pointer to device information structure + * Cmd DMA command mask (usc_DmaCmd_XX Macros) + * + * Return Value: + * + * None + */ +static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* write command mask to DCAR */ + outw( Cmd + info->mbre_bit, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_DmaCmd() */ + +/* + * usc_OutDmaReg() + * + * Write a 16-bit value to a USC DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) for write + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +static void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + outw( RegValue, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_OutDmaReg() */ + +/* + * usc_InDmaReg() + * + * Read a 16-bit value from a DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to read from + * + * Return Value: + * + * The 16-bit value read from register + * + */ +static u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + return inw( info->io_base ); + +} /* end of usc_InDmaReg() */ + +/* + * + * usc_OutReg() + * + * Write a 16-bit value to a USC serial channel register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to write to + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +static void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + outw( RegValue, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_OutReg() */ + +/* + * usc_InReg() + * + * Reads a 16-bit value from a USC serial channel register + * + * Arguments: + * + * info pointer to device extension + * RegAddr register address (number) to read from + * + * Return Value: + * + * 16-bit value read from register + */ +static u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + return inw( info->io_base + CCAR ); + +} /* end of usc_InReg() */ + +/* usc_set_sdlc_mode() + * + * Set up the adapter for SDLC DMA communications. + * + * Arguments: info pointer to device instance data + * Return Value: NONE + */ +static void usc_set_sdlc_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + bool PreSL1660; + + /* + * determine if the IUSC on the adapter is pre-SL1660. If + * not, take advantage of the UnderWait feature of more + * modern chips. If an underrun occurs and this bit is set, + * the transmitter will idle the programmed idle pattern + * until the driver has time to service the underrun. Otherwise, + * the dma controller may get the cycles previously requested + * and begin transmitting queued tx data. + */ + usc_OutReg(info,TMCR,0x1f); + RegValue=usc_InReg(info,TMDR); + PreSL1660 = (RegValue == IUSC_PRE_SL1660); + + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + { + /* + ** Channel Mode Register (CMR) + ** + ** <15..14> 10 Tx Sub Modes, Send Flag on Underrun + ** <13> 0 0 = Transmit Disabled (initially) + ** <12> 0 1 = Consecutive Idles share common 0 + ** <11..8> 1110 Transmitter Mode = HDLC/SDLC Loop + ** <7..4> 0000 Rx Sub Modes, addr/ctrl field handling + ** <3..0> 0110 Receiver Mode = HDLC/SDLC + ** + ** 1000 1110 0000 0110 = 0x8e06 + */ + RegValue = 0x8e06; + + /*-------------------------------------------------- + * ignore user options for UnderRun Actions and + * preambles + *--------------------------------------------------*/ + } + else + { + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, Underrun Action + * <13> 0 1 = Send Preamble before opening flag + * <12> 0 1 = Consecutive Idles share common 0 + * <11..8> 0110 Transmitter mode = HDLC/SDLC + * <7..4> 0000 Rx Sub modes, addr/ctrl field handling + * <3..0> 0110 Receiver mode = HDLC/SDLC + * + * 0000 0110 0000 0110 = 0x0606 + */ + if (info->params.mode == MGSL_MODE_RAW) { + RegValue = 0x0001; /* Set Receive mode = external sync */ + + usc_OutReg( info, IOCR, /* Set IOCR DCD is RxSync Detect Input */ + (unsigned short)((usc_InReg(info, IOCR) & ~(BIT13|BIT12)) | BIT12)); + + /* + * TxSubMode: + * CMR <15> 0 Don't send CRC on Tx Underrun + * CMR <14> x undefined + * CMR <13> 0 Send preamble before openning sync + * CMR <12> 0 Send 8-bit syncs, 1=send Syncs per TxLength + * + * TxMode: + * CMR <11-8) 0100 MonoSync + * + * 0x00 0100 xxxx xxxx 04xx + */ + RegValue |= 0x0400; + } + else { + + RegValue = 0x0606; + + if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) + RegValue |= BIT14; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) + RegValue |= BIT15; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) + RegValue |= BIT15 + BIT14; + } + + if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) + RegValue |= BIT13; + } + + if ( info->params.mode == MGSL_MODE_HDLC && + (info->params.flags & HDLC_FLAG_SHARE_ZERO) ) + RegValue |= BIT12; + + if ( info->params.addr_filter != 0xff ) + { + /* set up receive address filtering */ + usc_OutReg( info, RSR, info->params.addr_filter ); + RegValue |= BIT4; + } + + usc_OutReg( info, CMR, RegValue ); + info->cmr_value = RegValue; + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Set CRC to all 1s (use for SDLC/HDLC) + * <9> 0 1 = Include Receive chars in CRC + * <8> 1 1 = Use Abort/PE bit as abort indicator + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0101 0000 0000 = 0x0500 + */ + + RegValue = 0x0500; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) + RegValue |= BIT9; + else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) + RegValue |= ( BIT12 | BIT10 | BIT9 ); + + usc_OutReg( info, RMR, RegValue ); + + /* Set the Receive count Limit Register (RCLR) to 0xffff. */ + /* When an opening flag of an SDLC frame is recognized the */ + /* Receive Character count (RCC) is loaded with the value in */ + /* RCLR. The RCC is decremented for each received byte. The */ + /* value of RCC is stored after the closing flag of the frame */ + /* allowing the frame size to be computed. */ + + usc_OutReg( info, RCLR, RCLRVALUE ); + + usc_RCmd( info, RCmd_SelectRicrdma_level ); + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO DMA Request Level + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 1 Queued status reflects oldest 2 bytes in FIFO + * <2> 0 Abort/PE IA + * <1> 1 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0000 1000 = 0x000a + */ + + /* Carry over the Exit Hunt and Idle Received bits */ + /* in case they have been armed by usc_ArmEvents. */ + + RegValue = usc_InReg( info, RICR ) & 0xc0; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, RICR, (u16)(0x030a | RegValue) ); + else + usc_OutReg( info, RICR, (u16)(0x140a | RegValue) ); + + /* Unlatch all Rx status bits and clear Rx status IRQ Pending */ + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Start CRC as all 1s (use for SDLC/HDLC) + * <9> 0 1 = Tx CRC Enabled + * <8> 0 1 = Append CRC to end of transmit frame + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0100 0000 0000 = 0x0400 + */ + + RegValue = 0x0400; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) + RegValue |= BIT9 + BIT8; + else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) + RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8); + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + usc_TCmd( info, TCmd_SelectTicrdma_level ); + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO DMA Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 0 Idle Sent IA + * <5> 1 Abort Sent IA + * <4> 1 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 1 1 = Wait for SW Trigger to Start Frame + * <1> 1 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0011 0110 = 0x0036 + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TICR, 0x0736 ); + else + usc_OutReg( info, TICR, 0x1436 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + /* + ** Transmit Command/Status Register (TCSR) + ** + ** <15..12> 0000 TCmd + ** <11> 0/1 UnderWait + ** <10..08> 000 TxIdle + ** <7> x PreSent + ** <6> x IdleSent + ** <5> x AbortSent + ** <4> x EOF/EOM Sent + ** <3> x CRC Sent + ** <2> x All Sent + ** <1> x TxUnder + ** <0> x TxEmpty + ** + ** 0000 0000 0000 0000 = 0x0000 + */ + info->tcsr_value = 0; + + if ( !PreSL1660 ) + info->tcsr_value |= TCSR_UNDERWAIT; + + usc_OutReg( info, TCSR, info->tcsr_value ); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Source = Disabled + * <13..12> 00 counter 0 Source = Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> XXX TxCLK comes from Port 0 + * <2..0> XXX RxCLK comes from Port 1 + * + * 0000 1111 0111 0111 = 0x0f77 + */ + + RegValue = 0x0f40; + + if ( info->params.flags & HDLC_FLAG_RXC_DPLL ) + RegValue |= 0x0003; /* RxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) + RegValue |= 0x0004; /* RxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN) + RegValue |= 0x0006; /* RxCLK from TXC Input */ + else + RegValue |= 0x0007; /* RxCLK from Port1 */ + + if ( info->params.flags & HDLC_FLAG_TXC_DPLL ) + RegValue |= 0x0018; /* TxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) + RegValue |= 0x0020; /* TxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN) + RegValue |= 0x0038; /* RxCLK from TXC Input */ + else + RegValue |= 0x0030; /* TxCLK from Port0 */ + + usc_OutReg( info, CMCR, RegValue ); + + + /* Hardware Configuration Register (HCR) + * + * <15..14> 00 CTR0 Divisor:00=32,01=16,10=8,11=4 + * <13> 0 CTR1DSel:0=CTR0Div determines CTR0Div + * <12> 0 CVOK:0=report code violation in biphase + * <11..10> 00 DPLL Divisor:00=32,01=16,10=8,11=4 + * <9..8> XX DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level + * <7..6> 00 reserved + * <5> 0 BRG1 mode:0=continuous,1=single cycle + * <4> X BRG1 Enable + * <3..2> 00 reserved + * <1> 0 BRG0 mode:0=continuous,1=single cycle + * <0> 0 BRG0 Enable + */ + + RegValue = 0x0000; + + if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { + u32 XtalSpeed; + u32 DpllDivisor; + u16 Tc; + + /* DPLL is enabled. Use BRG1 to provide continuous reference clock */ + /* for DPLL. DPLL mode in HCR is dependent on the encoding used. */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { + DpllDivisor = 16; + RegValue |= BIT10; + } + else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { + DpllDivisor = 8; + RegValue |= BIT11; + } + else + DpllDivisor = 32; + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + /*-------------------------------------------------- + * ejz: for DPLL mode, application should use the + * same clock speed as the partner system, even + * though clocking is derived from the input RxData. + * In case the user uses a 0 for the clock speed, + * default to 0xffffffff and don't try to divide by + * zero + *--------------------------------------------------*/ + if ( info->params.clock_speed ) + { + Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); + if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) + / info->params.clock_speed) ) + Tc--; + } + else + Tc = -1; + + + /* Write 16-bit Time Constant for BRG1 */ + usc_OutReg( info, TC1R, Tc ); + + RegValue |= BIT4; /* enable BRG1 */ + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZ: + case HDLC_ENCODING_NRZB: + case HDLC_ENCODING_NRZI_MARK: + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break; + case HDLC_ENCODING_BIPHASE_MARK: + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; + case HDLC_ENCODING_BIPHASE_LEVEL: + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; + } + } + + usc_OutReg( info, HCR, RegValue ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL Sync (RW) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x1020 ); + + + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) { + usc_OutReg( info, SICR, + (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) ); + } + + + /* enable Master Interrupt Enable bit (MIE) */ + usc_EnableMasterIrqBit( info ); + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + + TRANSMIT_STATUS + TRANSMIT_DATA + MISC); + + /* arm RCC underflow interrupt */ + usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3)); + usc_EnableInterrupts(info, MISC); + + info->mbre_bit = 0; + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + info->mbre_bit = BIT8; + outw( BIT8, info->io_base ); /* set Master Bus Enable (DCAR) */ + + if (info->bus_type == MGSL_BUS_TYPE_ISA) { + /* Enable DMAEN (Port 7, Bit 14) */ + /* This connects the DMA request signal to the ISA bus */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14)); + } + + /* DMA Control Register (DCR) + * + * <15..14> 10 Priority mode = Alternating Tx/Rx + * 01 Rx has priority + * 00 Tx has priority + * + * <13> 1 Enable Priority Preempt per DCR<15..14> + * (WARNING DCR<11..10> must be 00 when this is 1) + * 0 Choose activate channel per DCR<11..10> + * + * <12> 0 Little Endian for Array/List + * <11..10> 00 Both Channels can use each bus grant + * <9..6> 0000 reserved + * <5> 0 7 CLK - Minimum Bus Re-request Interval + * <4> 0 1 = drive D/C and S/D pins + * <3> 1 1 = Add one wait state to all DMA cycles. + * <2> 0 1 = Strobe /UAS on every transfer. + * <1..0> 11 Addr incrementing only affects LS24 bits + * + * 0110 0000 0000 1011 = 0x600b + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter does not need DMA wait state */ + usc_OutDmaReg( info, DCR, 0xa00b ); + } + else + usc_OutDmaReg( info, DCR, 0x800b ); + + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in Arrary/List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, RDMR, 0xf200 ); + + + /* Transmit DMA mode Register (TDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 TCBinA/L = fetch Tx Control Block from List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on end of frame + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (Read Only so write as 0) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, TDMR, 0xf200 ); + + + /* DMA Interrupt Control Register (DICR) + * + * <15> 1 DMA Interrupt Enable + * <14> 0 1 = Disable IEO from USC + * <13> 0 1 = Don't provide vector during IntAck + * <12> 1 1 = Include status in Vector + * <10..2> 0 reserved, Must be 0s + * <1> 0 1 = Rx DMA Interrupt Enabled + * <0> 0 1 = Tx DMA Interrupt Enabled + * + * 1001 0000 0000 0000 = 0x9000 + */ + + usc_OutDmaReg( info, DICR, 0x9000 ); + + usc_InDmaReg( info, RDMR ); /* clear pending receive DMA IRQ bits */ + usc_InDmaReg( info, TDMR ); /* clear pending transmit DMA IRQ bits */ + usc_OutDmaReg( info, CDIR, 0x0303 ); /* clear IUS and Pending for Tx and Rx */ + + /* Channel Control Register (CCR) + * + * <15..14> 10 Use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length + * <9..8> 00 Preamble Pattern + * <7..6> 10 Use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 1000 0000 1000 0000 = 0x8080 + */ + + RegValue = 0x8080; + + switch ( info->params.preamble_length ) { + case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; + case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; + case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; + } + + switch ( info->params.preamble ) { + case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; + case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; + case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; + case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; + } + + usc_OutReg( info, CCR, RegValue ); + + + /* + * Burst/Dwell Control Register + * + * <15..8> 0x20 Maximum number of transfers per bus grant + * <7..0> 0x00 Maximum number of clock cycles per bus grant + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* don't limit bus occupancy on PCI adapter */ + usc_OutDmaReg( info, BDCR, 0x0000 ); + } + else + usc_OutDmaReg( info, BDCR, 0x2000 ); + + usc_stop_transmitter(info); + usc_stop_receiver(info); + +} /* end of usc_set_sdlc_mode() */ + +/* usc_enable_loopback() + * + * Set the 16C32 for internal loopback mode. + * The TxCLK and RxCLK signals are generated from the BRG0 and + * the TxD is looped back to the RxD internally. + * + * Arguments: info pointer to device instance data + * enable 1 = enable loopback, 0 = disable + * Return Value: None + */ +static void usc_enable_loopback(struct mgsl_struct *info, int enable) +{ + if (enable) { + /* blank external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + /* Write 16-bit Time Constant for BRG0 */ + /* use clock speed if available, otherwise use 8 for diagnostics */ + if (info->params.clock_speed) { + if (info->bus_type == MGSL_BUS_TYPE_PCI) + usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1)); + else + usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1)); + } else + usc_OutReg(info, TC0R, (u16)8); + + /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0 + mode = Continuous Set Bit 0 to enable BRG0. */ + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004)); + + /* set Internal Data loopback mode */ + info->loopback_bits = 0x300; + outw( 0x0300, info->io_base + CCAR ); + } else { + /* enable external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); + + /* clear Internal Data loopback mode */ + info->loopback_bits = 0; + outw( 0,info->io_base + CCAR ); + } + +} /* end of usc_enable_loopback() */ + +/* usc_enable_aux_clock() + * + * Enabled the AUX clock output at the specified frequency. + * + * Arguments: + * + * info pointer to device extension + * data_rate data rate of clock in bits per second + * A data rate of 0 disables the AUX clock. + * + * Return Value: None + */ +static void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate ) +{ + u32 XtalSpeed; + u16 Tc; + + if ( data_rate ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + + Tc = (u16)(XtalSpeed/data_rate); + if ( !(((XtalSpeed % data_rate) * 2) / data_rate) ) + Tc--; + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, Tc ); + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_aux_clock() */ + +/* + * + * usc_process_rxoverrun_sync() + * + * This function processes a receive overrun by resetting the + * receive DMA buffers and issuing a Purge Rx FIFO command + * to allow the receiver to continue receiving. + * + * Arguments: + * + * info pointer to device extension + * + * Return Value: None + */ +static void usc_process_rxoverrun_sync( struct mgsl_struct *info ) +{ + int start_index; + int end_index; + int frame_start_index; + bool start_of_frame_found = false; + bool end_of_frame_found = false; + bool reprogram_dma = false; + + DMABUFFERENTRY *buffer_list = info->rx_buffer_list; + u32 phys_addr; + + usc_DmaCmd( info, DmaCmd_PauseRxChannel ); + usc_RCmd( info, RCmd_EnterHuntmode ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + /* CurrentRxBuffer points to the 1st buffer of the next */ + /* possibly available receive frame. */ + + frame_start_index = start_index = end_index = info->current_rx_buffer; + + /* Search for an unfinished string of buffers. This means */ + /* that a receive frame started (at least one buffer with */ + /* count set to zero) but there is no terminiting buffer */ + /* (status set to non-zero). */ + + while( !buffer_list[end_index].count ) + { + /* Count field has been reset to zero by 16C32. */ + /* This buffer is currently in use. */ + + if ( !start_of_frame_found ) + { + start_of_frame_found = true; + frame_start_index = end_index; + end_of_frame_found = false; + } + + if ( buffer_list[end_index].status ) + { + /* Status field has been set by 16C32. */ + /* This is the last buffer of a received frame. */ + + /* We want to leave the buffers for this frame intact. */ + /* Move on to next possible frame. */ + + start_of_frame_found = false; + end_of_frame_found = true; + } + + /* advance to next buffer entry in linked list */ + end_index++; + if ( end_index == info->rx_buffer_count ) + end_index = 0; + + if ( start_index == end_index ) + { + /* The entire list has been searched with all Counts == 0 and */ + /* all Status == 0. The receive buffers are */ + /* completely screwed, reset all receive buffers! */ + mgsl_reset_rx_dma_buffers( info ); + frame_start_index = 0; + start_of_frame_found = false; + reprogram_dma = true; + break; + } + } + + if ( start_of_frame_found && !end_of_frame_found ) + { + /* There is an unfinished string of receive DMA buffers */ + /* as a result of the receiver overrun. */ + + /* Reset the buffers for the unfinished frame */ + /* and reprogram the receive DMA controller to start */ + /* at the 1st buffer of unfinished frame. */ + + start_index = frame_start_index; + + do + { + *((unsigned long *)&(info->rx_buffer_list[start_index++].count)) = DMABUFFERSIZE; + + /* Adjust index for wrap around. */ + if ( start_index == info->rx_buffer_count ) + start_index = 0; + + } while( start_index != end_index ); + + reprogram_dma = true; + } + + if ( reprogram_dma ) + { + usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); + usc_ClearIrqPendingBits(info, RECEIVE_DATA|RECEIVE_STATUS); + usc_UnlatchRxstatusBits(info, RECEIVE_DATA|RECEIVE_STATUS); + + usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); + + /* This empties the receive FIFO and loads the RCC with RCLR */ + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + + /* program 16C32 with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[frame_start_index].phys_entry; + usc_OutDmaReg( info, NRARL, (u16)phys_addr ); + usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_EnableInterrupts( info, RECEIVE_STATUS ); + + /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ + /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ + + usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) + usc_EnableReceiver(info,ENABLE_AUTO_DCD); + else + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } + else + { + /* This empties the receive FIFO and loads the RCC with RCLR */ + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + } + +} /* end of usc_process_rxoverrun_sync() */ + +/* usc_stop_receiver() + * + * Disable USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_stop_receiver( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* Disable receive DMA channel. */ + /* This also disables receive DMA channel interrupts */ + usc_DmaCmd( info, DmaCmd_ResetRxChannel ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); + + /* This empties the receive FIFO and loads the RCC with RCLR */ + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + info->rx_enabled = false; + info->rx_overflow = false; + info->rx_rcc_underrun = false; + +} /* end of stop_receiver() */ + +/* usc_start_receiver() + * + * Enable the USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_start_receiver( struct mgsl_struct *info ) +{ + u32 phys_addr; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + mgsl_reset_rx_dma_buffers( info ); + usc_stop_receiver( info ); + + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + if ( info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW ) { + /* DMA mode Transfers */ + /* Program the DMA controller. */ + /* Enable the DMA controller end of buffer interrupt. */ + + /* program 16C32 with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (u16)phys_addr ); + usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_EnableInterrupts( info, RECEIVE_STATUS ); + + /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ + /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ + + usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) + usc_EnableReceiver(info,ENABLE_AUTO_DCD); + else + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } else { + usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); + usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_EnableInterrupts(info, RECEIVE_DATA); + + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_RCmd( info, RCmd_EnterHuntmode ); + + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } + + usc_OutReg( info, CCSR, 0x1020 ); + + info->rx_enabled = true; + +} /* end of usc_start_receiver() */ + +/* usc_start_transmitter() + * + * Enable the USC transmitter and send a transmit frame if + * one is loaded in the DMA buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_start_transmitter( struct mgsl_struct *info ) +{ + u32 phys_addr; + unsigned int FrameSize; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if ( info->xmit_cnt ) { + + /* If auto RTS enabled and RTS is inactive, then assert */ + /* RTS and set a flag indicating that the driver should */ + /* negate RTS when the transmission completes. */ + + info->drop_rts_on_tx_done = false; + + if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { + usc_get_serial_signals( info ); + if ( !(info->serial_signals & SerialSignal_RTS) ) { + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals( info ); + info->drop_rts_on_tx_done = true; + } + } + + + if ( info->params.mode == MGSL_MODE_ASYNC ) { + if ( !info->tx_active ) { + usc_UnlatchTxstatusBits(info, TXSTATUS_ALL); + usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA); + usc_EnableInterrupts(info, TRANSMIT_DATA); + usc_load_txfifo(info); + } + } else { + /* Disable transmit DMA controller while programming. */ + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + + /* Transmit DMA buffer is loaded, so program USC */ + /* to send the frame contained in the buffers. */ + + FrameSize = info->tx_buffer_list[info->start_tx_dma_buffer].rcc; + + /* if operating in Raw sync mode, reset the rcc component + * of the tx dma buffer entry, otherwise, the serial controller + * will send a closing sync char after this count. + */ + if ( info->params.mode == MGSL_MODE_RAW ) + info->tx_buffer_list[info->start_tx_dma_buffer].rcc = 0; + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, (u16)FrameSize ); + + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + phys_addr = info->tx_buffer_list[info->start_tx_dma_buffer].phys_entry; + usc_OutDmaReg( info, NTARL, (u16)phys_addr ); + usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_EnableInterrupts( info, TRANSMIT_STATUS ); + + if ( info->params.mode == MGSL_MODE_RAW && + info->num_tx_dma_buffers > 1 ) { + /* When running external sync mode, attempt to 'stream' transmit */ + /* by filling tx dma buffers as they become available. To do this */ + /* we need to enable Tx DMA EOB Status interrupts : */ + /* */ + /* 1. Arm End of Buffer (EOB) Transmit DMA Interrupt (BIT2 of TDIAR) */ + /* 2. Enable Transmit DMA Interrupts (BIT0 of DICR) */ + + usc_OutDmaReg( info, TDIAR, BIT2|BIT3 ); + usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT0) ); + } + + /* Initialize Transmit DMA Channel */ + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + usc_TCmd( info, TCmd_SendFrame ); + + mod_timer(&info->tx_timer, jiffies + + msecs_to_jiffies(5000)); + } + info->tx_active = true; + } + + if ( !info->tx_enabled ) { + info->tx_enabled = true; + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) + usc_EnableTransmitter(info,ENABLE_AUTO_CTS); + else + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + } + +} /* end of usc_start_transmitter() */ + +/* usc_stop_transmitter() + * + * Stops the transmitter and DMA + * + * Arguments: info pointer to device isntance data + * Return Value: None + */ +static void usc_stop_transmitter( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + del_timer(&info->tx_timer); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + + usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + info->tx_enabled = false; + info->tx_active = false; + +} /* end of usc_stop_transmitter() */ + +/* usc_load_txfifo() + * + * Fill the transmit FIFO until the FIFO is full or + * there is no more data to load. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +static void usc_load_txfifo( struct mgsl_struct *info ) +{ + int Fifocount; + u8 TwoBytes[2]; + + if ( !info->xmit_cnt && !info->x_char ) + return; + + /* Select transmit FIFO status readback in TICR */ + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + /* load the Transmit FIFO until FIFOs full or all data sent */ + + while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) { + /* there is more space in the transmit FIFO and */ + /* there is more data in transmit buffer */ + + if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) { + /* write a 16-bit word from transmit buffer to 16C32 */ + + TwoBytes[0] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + TwoBytes[1] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + + outw( *((u16 *)TwoBytes), info->io_base + DATAREG); + + info->xmit_cnt -= 2; + info->icount.tx += 2; + } else { + /* only 1 byte left to transmit or 1 FIFO slot left */ + + outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY), + info->io_base + CCAR ); + + if (info->x_char) { + /* transmit pending high priority char */ + outw( info->x_char,info->io_base + CCAR ); + info->x_char = 0; + } else { + outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR ); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + info->icount.tx++; + } + } + +} /* end of usc_load_txfifo() */ + +/* usc_reset() + * + * Reset the adapter to a known state and prepare it for further use. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_reset( struct mgsl_struct *info ) +{ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + int i; + u32 readval; + + /* Set BIT30 of Misc Control Register */ + /* (Local Control Register 0x50) to force reset of USC. */ + + volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); + u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28); + + info->misc_ctrl_value |= BIT30; + *MiscCtrl = info->misc_ctrl_value; + + /* + * Force at least 170ns delay before clearing + * reset bit. Each read from LCR takes at least + * 30ns so 10 times for 300ns to be safe. + */ + for(i=0;i<10;i++) + readval = *MiscCtrl; + + info->misc_ctrl_value &= ~BIT30; + *MiscCtrl = info->misc_ctrl_value; + + *LCR0BRDR = BUS_DESCRIPTOR( + 1, // Write Strobe Hold (0-3) + 2, // Write Strobe Delay (0-3) + 2, // Read Strobe Delay (0-3) + 0, // NWDD (Write data-data) (0-3) + 4, // NWAD (Write Addr-data) (0-31) + 0, // NXDA (Read/Write Data-Addr) (0-3) + 0, // NRDD (Read Data-Data) (0-3) + 5 // NRAD (Read Addr-Data) (0-31) + ); + } else { + /* do HW reset */ + outb( 0,info->io_base + 8 ); + } + + info->mbre_bit = 0; + info->loopback_bits = 0; + info->usc_idle_mode = 0; + + /* + * Program the Bus Configuration Register (BCR) + * + * <15> 0 Don't use separate address + * <14..6> 0 reserved + * <5..4> 00 IAckmode = Default, don't care + * <3> 1 Bus Request Totem Pole output + * <2> 1 Use 16 Bit data bus + * <1> 0 IRQ Totem Pole output + * <0> 0 Don't Shift Right Addr + * + * 0000 0000 0000 1100 = 0x000c + * + * By writing to io_base + SDPIN the Wait/Ack pin is + * programmed to work as a Wait pin. + */ + + outw( 0x000c,info->io_base + SDPIN ); + + + outw( 0,info->io_base ); + outw( 0,info->io_base + CCAR ); + + /* select little endian byte ordering */ + usc_RTCmd( info, RTCmd_SelectLittleEndian ); + + + /* Port Control Register (PCR) + * + * <15..14> 11 Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled) + * <13..12> 11 Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled) + * <11..10> 00 Port 5 is Input (No Connect, Don't Care) + * <9..8> 00 Port 4 is Input (No Connect, Don't Care) + * <7..6> 11 Port 3 is Output (~RTS, Bit 6 : 0 = Enabled ) + * <5..4> 11 Port 2 is Output (~DTR, Bit 4 : 0 = Enabled ) + * <3..2> 01 Port 1 is Input (Dedicated RxC) + * <1..0> 01 Port 0 is Input (Dedicated TxC) + * + * 1111 0000 1111 0101 = 0xf0f5 + */ + + usc_OutReg( info, PCR, 0xf0f5 ); + + + /* + * Input/Output Control Register + * + * <15..14> 00 CTS is active low input + * <13..12> 00 DCD is active low input + * <11..10> 00 TxREQ pin is input (DSR) + * <9..8> 00 RxREQ pin is input (RI) + * <7..6> 00 TxD is output (Transmit Data) + * <5..3> 000 TxC Pin in Input (14.7456MHz Clock) + * <2..0> 100 RxC is Output (drive with BRG0) + * + * 0000 0000 0000 0100 = 0x0004 + */ + + usc_OutReg( info, IOCR, 0x0004 ); + +} /* end of usc_reset() */ + +/* usc_set_async_mode() + * + * Program adapter for asynchronous communications. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_set_async_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + + /* disable interrupts while programming USC */ + usc_DisableMasterIrqBit( info ); + + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + + usc_loopback_frame( info ); + + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, 00 = 1 Stop Bit + * <13..12> 00 00 = 16X Clock + * <11..8> 0000 Transmitter mode = Asynchronous + * <7..6> 00 reserved? + * <5..4> 00 Rx Sub modes, 00 = 16X Clock + * <3..0> 0000 Receiver mode = Asynchronous + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + if ( info->params.stop_bits != 1 ) + RegValue |= BIT14; + usc_OutReg( info, CMR, RegValue ); + + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, RMR, RegValue ); + + + /* Set IRQ trigger level */ + + usc_RCmd( info, RCmd_SelectRicrIntLevel ); + + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO IRQ Request Level + * + * Note: For async mode the receive FIFO level must be set + * to 0 to avoid the situation where the FIFO contains fewer bytes + * than the trigger level and no more data is expected. + * + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 0 Queued status reflects oldest byte in FIFO + * <2> 0 Abort/PE IA + * <1> 0 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB) + */ + + usc_OutReg( info, RICR, 0x0000 ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + /* Set IRQ trigger level */ + + usc_TCmd( info, TCmd_SelectTicrIntLevel ); + + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO IRQ Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 1 Idle Sent IA + * <5> 0 Abort Sent IA + * <4> 0 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 0 1 = Wait for SW Trigger to Start Frame + * <1> 0 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0100 0000 = 0x0040 + */ + + usc_OutReg( info, TICR, 0x1f40 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + usc_enable_async_clock( info, info->params.data_rate ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL in Sync status (RO) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x0020 ); + + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableMasterIrqBit( info ); + + if (info->bus_type == MGSL_BUS_TYPE_ISA) { + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); + } + + if (info->params.loopback) { + info->loopback_bits = 0x300; + outw(0x0300, info->io_base + CCAR); + } + +} /* end of usc_set_async_mode() */ + +/* usc_loopback_frame() + * + * Loop back a small (2 byte) dummy SDLC frame. + * Interrupts and DMA are NOT used. The purpose of this is to + * clear any 'stale' status info left over from running in async mode. + * + * The 16C32 shows the strange behaviour of marking the 1st + * received SDLC frame with a CRC error even when there is no + * CRC error. To get around this a small dummy from of 2 bytes + * is looped back when switching from async to sync mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_loopback_frame( struct mgsl_struct *info ) +{ + int i; + unsigned long oldmode = info->params.mode; + + info->params.mode = MGSL_MODE_HDLC; + + usc_DisableMasterIrqBit( info ); + + usc_set_sdlc_mode( info ); + usc_enable_loopback( info, 1 ); + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, 0 ); + + /* Channel Control Register (CCR) + * + * <15..14> 00 Don't use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length = 8-Bits + * <9..8> 01 Preamble Pattern = flags + * <7..6> 10 Don't use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 0000 0001 0000 0000 = 0x0100 + */ + + usc_OutReg( info, CCR, 0x0100 ); + + /* SETUP RECEIVER */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + + /* SETUP TRANSMITTER */ + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, 2 ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* unlatch Tx status bits, and start transmit channel. */ + usc_UnlatchTxstatusBits(info,TXSTATUS_ALL); + outw(0,info->io_base + DATAREG); + + /* ENABLE TRANSMITTER */ + usc_TCmd( info, TCmd_SendFrame ); + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + + /* WAIT FOR RECEIVE COMPLETE */ + for (i=0 ; i<1000 ; i++) + if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) + break; + + /* clear Internal Data loopback mode */ + usc_enable_loopback(info, 0); + + usc_EnableMasterIrqBit(info); + + info->params.mode = oldmode; + +} /* end of usc_loopback_frame() */ + +/* usc_set_sync_mode() Programs the USC for SDLC communications. + * + * Arguments: info pointer to adapter info structure + * Return Value: None + */ +static void usc_set_sync_mode( struct mgsl_struct *info ) +{ + usc_loopback_frame( info ); + usc_set_sdlc_mode( info ); + + if (info->bus_type == MGSL_BUS_TYPE_ISA) { + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); + } + + usc_enable_aux_clock(info, info->params.clock_speed); + + if (info->params.loopback) + usc_enable_loopback(info,1); + +} /* end of mgsl_set_sync_mode() */ + +/* usc_set_txidle() Set the HDLC idle mode for the transmitter. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_set_txidle( struct mgsl_struct *info ) +{ + u16 usc_idle_mode = IDLEMODE_FLAGS; + + /* Map API idle mode to USC register bits */ + + switch( info->idle_mode ){ + case HDLC_TXIDLE_FLAGS: usc_idle_mode = IDLEMODE_FLAGS; break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break; + case HDLC_TXIDLE_ZEROS: usc_idle_mode = IDLEMODE_ZERO; break; + case HDLC_TXIDLE_ONES: usc_idle_mode = IDLEMODE_ONE; break; + case HDLC_TXIDLE_ALT_MARK_SPACE: usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break; + case HDLC_TXIDLE_SPACE: usc_idle_mode = IDLEMODE_SPACE; break; + case HDLC_TXIDLE_MARK: usc_idle_mode = IDLEMODE_MARK; break; + } + + info->usc_idle_mode = usc_idle_mode; + //usc_OutReg(info, TCSR, usc_idle_mode); + info->tcsr_value &= ~IDLEMODE_MASK; /* clear idle mode bits */ + info->tcsr_value += usc_idle_mode; + usc_OutReg(info, TCSR, info->tcsr_value); + + /* + * if SyncLink WAN adapter is running in external sync mode, the + * transmitter has been set to Monosync in order to try to mimic + * a true raw outbound bit stream. Monosync still sends an open/close + * sync char at the start/end of a frame. Try to match those sync + * patterns to the idle mode set here + */ + if ( info->params.mode == MGSL_MODE_RAW ) { + unsigned char syncpat = 0; + switch( info->idle_mode ) { + case HDLC_TXIDLE_FLAGS: + syncpat = 0x7e; + break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: + syncpat = 0x55; + break; + case HDLC_TXIDLE_ZEROS: + case HDLC_TXIDLE_SPACE: + syncpat = 0x00; + break; + case HDLC_TXIDLE_ONES: + case HDLC_TXIDLE_MARK: + syncpat = 0xff; + break; + case HDLC_TXIDLE_ALT_MARK_SPACE: + syncpat = 0xaa; + break; + } + + usc_SetTransmitSyncChars(info,syncpat,syncpat); + } + +} /* end of usc_set_txidle() */ + +/* usc_get_serial_signals() + * + * Query the adapter for the state of the V24 status (input) signals. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_get_serial_signals( struct mgsl_struct *info ) +{ + u16 status; + + /* clear all serial signals except DTR and RTS */ + info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + + /* Read the Misc Interrupt status Register (MISR) to get */ + /* the V24 status signals. */ + + status = usc_InReg( info, MISR ); + + /* set serial signal bits to reflect MISR */ + + if ( status & MISCSTATUS_CTS ) + info->serial_signals |= SerialSignal_CTS; + + if ( status & MISCSTATUS_DCD ) + info->serial_signals |= SerialSignal_DCD; + + if ( status & MISCSTATUS_RI ) + info->serial_signals |= SerialSignal_RI; + + if ( status & MISCSTATUS_DSR ) + info->serial_signals |= SerialSignal_DSR; + +} /* end of usc_get_serial_signals() */ + +/* usc_set_serial_signals() + * + * Set the state of DTR and RTS based on contents of + * serial_signals member of device extension. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void usc_set_serial_signals( struct mgsl_struct *info ) +{ + u16 Control; + unsigned char V24Out = info->serial_signals; + + /* get the current value of the Port Control Register (PCR) */ + + Control = usc_InReg( info, PCR ); + + if ( V24Out & SerialSignal_RTS ) + Control &= ~(BIT6); + else + Control |= BIT6; + + if ( V24Out & SerialSignal_DTR ) + Control &= ~(BIT4); + else + Control |= BIT4; + + usc_OutReg( info, PCR, Control ); + +} /* end of usc_set_serial_signals() */ + +/* usc_enable_async_clock() + * + * Enable the async clock at the specified frequency. + * + * Arguments: info pointer to device instance data + * data_rate data rate of clock in bps + * 0 disables the AUX clock. + * Return Value: None + */ +static void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate ) +{ + if ( data_rate ) { + /* + * Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + + /* + * Write 16-bit Time Constant for BRG0 + * Time Constant = (ClkSpeed / data_rate) - 1 + * ClkSpeed = 921600 (ISA), 691200 (PCI) + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) ); + else + usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) ); + + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, + (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + + usc_OutReg( info, IOCR, + (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_async_clock() */ + +/* + * Buffer Structures: + * + * Normal memory access uses virtual addresses that can make discontiguous + * physical memory pages appear to be contiguous in the virtual address + * space (the processors memory mapping handles the conversions). + * + * DMA transfers require physically contiguous memory. This is because + * the DMA system controller and DMA bus masters deal with memory using + * only physical addresses. + * + * This causes a problem under Windows NT when large DMA buffers are + * needed. Fragmentation of the nonpaged pool prevents allocations of + * physically contiguous buffers larger than the PAGE_SIZE. + * + * However the 16C32 supports Bus Master Scatter/Gather DMA which + * allows DMA transfers to physically discontiguous buffers. Information + * about each data transfer buffer is contained in a memory structure + * called a 'buffer entry'. A list of buffer entries is maintained + * to track and control the use of the data transfer buffers. + * + * To support this strategy we will allocate sufficient PAGE_SIZE + * contiguous memory buffers to allow for the total required buffer + * space. + * + * The 16C32 accesses the list of buffer entries using Bus Master + * DMA. Control information is read from the buffer entries by the + * 16C32 to control data transfers. status information is written to + * the buffer entries by the 16C32 to indicate the status of completed + * transfers. + * + * The CPU writes control information to the buffer entries to control + * the 16C32 and reads status information from the buffer entries to + * determine information about received and transmitted frames. + * + * Because the CPU and 16C32 (adapter) both need simultaneous access + * to the buffer entries, the buffer entry memory is allocated with + * HalAllocateCommonBuffer(). This restricts the size of the buffer + * entry list to PAGE_SIZE. + * + * The actual data buffers on the other hand will only be accessed + * by the CPU or the adapter but not by both simultaneously. This allows + * Scatter/Gather packet based DMA procedures for using physically + * discontiguous pages. + */ + +/* + * mgsl_reset_tx_dma_buffers() + * + * Set the count for all transmit buffers to 0 to indicate the + * buffer is available for use and set the current buffer to the + * first buffer. This effectively makes all buffers free and + * discards any data in buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info ) +{ + unsigned int i; + + for ( i = 0; i < info->tx_buffer_count; i++ ) { + *((unsigned long *)&(info->tx_buffer_list[i].count)) = 0; + } + + info->current_tx_buffer = 0; + info->start_tx_dma_buffer = 0; + info->tx_dma_buffers_used = 0; + + info->get_tx_holding_index = 0; + info->put_tx_holding_index = 0; + info->tx_holding_count = 0; + +} /* end of mgsl_reset_tx_dma_buffers() */ + +/* + * num_free_tx_dma_buffers() + * + * returns the number of free tx dma buffers available + * + * Arguments: info pointer to device instance data + * Return Value: number of free tx dma buffers + */ +static int num_free_tx_dma_buffers(struct mgsl_struct *info) +{ + return info->tx_buffer_count - info->tx_dma_buffers_used; +} + +/* + * mgsl_reset_rx_dma_buffers() + * + * Set the count for all receive buffers to DMABUFFERSIZE + * and set the current buffer to the first buffer. This effectively + * makes all buffers free and discards any data in buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ) +{ + unsigned int i; + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE; +// info->rx_buffer_list[i].count = DMABUFFERSIZE; +// info->rx_buffer_list[i].status = 0; + } + + info->current_rx_buffer = 0; + +} /* end of mgsl_reset_rx_dma_buffers() */ + +/* + * mgsl_free_rx_frame_buffers() + * + * Free the receive buffers used by a received SDLC + * frame such that the buffers can be reused. + * + * Arguments: + * + * info pointer to device instance data + * StartIndex index of 1st receive buffer of frame + * EndIndex index of last receive buffer of frame + * + * Return Value: None + */ +static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ) +{ + bool Done = false; + DMABUFFERENTRY *pBufEntry; + unsigned int Index; + + /* Starting with 1st buffer entry of the frame clear the status */ + /* field and set the count field to DMA Buffer Size. */ + + Index = StartIndex; + + while( !Done ) { + pBufEntry = &(info->rx_buffer_list[Index]); + + if ( Index == EndIndex ) { + /* This is the last buffer of the frame! */ + Done = true; + } + + /* reset current buffer for reuse */ +// pBufEntry->status = 0; +// pBufEntry->count = DMABUFFERSIZE; + *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE; + + /* advance to next buffer entry in linked list */ + Index++; + if ( Index == info->rx_buffer_count ) + Index = 0; + } + + /* set current buffer to next buffer after last buffer of frame */ + info->current_rx_buffer = Index; + +} /* end of free_rx_frame_buffers() */ + +/* mgsl_get_rx_frame() + * + * This function attempts to return a received SDLC frame from the + * receive DMA buffers. Only frames received without errors are returned. + * + * Arguments: info pointer to device extension + * Return Value: true if frame returned, otherwise false + */ +static bool mgsl_get_rx_frame(struct mgsl_struct *info) +{ + unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ + unsigned short status; + DMABUFFERENTRY *pBufEntry; + unsigned int framesize = 0; + bool ReturnCode = false; + unsigned long flags; + struct tty_struct *tty = info->port.tty; + bool return_frame = false; + + /* + * current_rx_buffer points to the 1st buffer of the next available + * receive frame. To find the last buffer of the frame look for + * a non-zero status field in the buffer entries. (The status + * field is set by the 16C32 after completing a receive frame. + */ + + StartIndex = EndIndex = info->current_rx_buffer; + + while( !info->rx_buffer_list[EndIndex].status ) { + /* + * If the count field of the buffer entry is non-zero then + * this buffer has not been used. (The 16C32 clears the count + * field when it starts using the buffer.) If an unused buffer + * is encountered then there are no frames available. + */ + + if ( info->rx_buffer_list[EndIndex].count ) + goto Cleanup; + + /* advance to next buffer entry in linked list */ + EndIndex++; + if ( EndIndex == info->rx_buffer_count ) + EndIndex = 0; + + /* if entire list searched then no frame available */ + if ( EndIndex == StartIndex ) { + /* If this occurs then something bad happened, + * all buffers have been 'used' but none mark + * the end of a frame. Reset buffers and receiver. + */ + + if ( info->rx_enabled ){ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + goto Cleanup; + } + } + + + /* check status of receive frame */ + + status = info->rx_buffer_list[EndIndex].status; + + if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + + RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & RXSTATUS_SHORT_FRAME ) + info->icount.rxshort++; + else if ( status & RXSTATUS_ABORT ) + info->icount.rxabort++; + else if ( status & RXSTATUS_OVERRUN ) + info->icount.rxover++; + else { + info->icount.rxcrc++; + if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) + return_frame = true; + } + framesize = 0; +#if SYNCLINK_GENERIC_HDLC + { + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; + } +#endif + } else + return_frame = true; + + if ( return_frame ) { + /* receive frame has no errors, get frame size. + * The frame size is the starting value of the RCC (which was + * set to 0xffff) minus the ending value of the RCC (decremented + * once for each receive character) minus 2 for the 16-bit CRC. + */ + + framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc; + + /* adjust frame size for CRC if any */ + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + framesize -= 2; + else if ( info->params.crc_type == HDLC_CRC_32_CCITT ) + framesize -= 4; + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n", + __FILE__,__LINE__,info->device_name,status,framesize); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr, + min_t(int, framesize, DMABUFFERSIZE),0); + + if (framesize) { + if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) && + ((framesize+1) > info->max_frame_size) ) || + (framesize > info->max_frame_size) ) + info->icount.rxlong++; + else { + /* copy dma buffer(s) to contiguous intermediate buffer */ + int copy_count = framesize; + int index = StartIndex; + unsigned char *ptmp = info->intermediate_rxbuffer; + + if ( !(status & RXSTATUS_CRC_ERROR)) + info->icount.rxok++; + + while(copy_count) { + int partial_count; + if ( copy_count > DMABUFFERSIZE ) + partial_count = DMABUFFERSIZE; + else + partial_count = copy_count; + + pBufEntry = &(info->rx_buffer_list[index]); + memcpy( ptmp, pBufEntry->virt_addr, partial_count ); + ptmp += partial_count; + copy_count -= partial_count; + + if ( ++index == info->rx_buffer_count ) + index = 0; + } + + if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) { + ++framesize; + *ptmp = (status & RXSTATUS_CRC_ERROR ? + RX_CRC_ERROR : + RX_OK); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + printk("%s(%d):mgsl_get_rx_frame(%s) rx frame status=%d\n", + __FILE__,__LINE__,info->device_name, + *ptmp); + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_rx(info,info->intermediate_rxbuffer,framesize); + else +#endif + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); + } + } + /* Free the buffers used by this frame. */ + mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex ); + + ReturnCode = true; + +Cleanup: + + if ( info->rx_enabled && info->rx_overflow ) { + /* The receiver needs to restarted because of + * a receive overflow (buffer or FIFO). If the + * receive buffers are now empty, then restart receiver. + */ + + if ( !info->rx_buffer_list[EndIndex].status && + info->rx_buffer_list[EndIndex].count ) { + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + return ReturnCode; + +} /* end of mgsl_get_rx_frame() */ + +/* mgsl_get_raw_rx_frame() + * + * This function attempts to return a received frame from the + * receive DMA buffers when running in external loop mode. In this mode, + * we will return at most one DMABUFFERSIZE frame to the application. + * The USC receiver is triggering off of DCD going active to start a new + * frame, and DCD going inactive to terminate the frame (similar to + * processing a closing flag character). + * + * In this routine, we will return DMABUFFERSIZE "chunks" at a time. + * If DCD goes inactive, the last Rx DMA Buffer will have a non-zero + * status field and the RCC field will indicate the length of the + * entire received frame. We take this RCC field and get the modulus + * of RCC and DMABUFFERSIZE to determine if number of bytes in the + * last Rx DMA buffer and return that last portion of the frame. + * + * Arguments: info pointer to device extension + * Return Value: true if frame returned, otherwise false + */ +static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info) +{ + unsigned int CurrentIndex, NextIndex; + unsigned short status; + DMABUFFERENTRY *pBufEntry; + unsigned int framesize = 0; + bool ReturnCode = false; + unsigned long flags; + struct tty_struct *tty = info->port.tty; + + /* + * current_rx_buffer points to the 1st buffer of the next available + * receive frame. The status field is set by the 16C32 after + * completing a receive frame. If the status field of this buffer + * is zero, either the USC is still filling this buffer or this + * is one of a series of buffers making up a received frame. + * + * If the count field of this buffer is zero, the USC is either + * using this buffer or has used this buffer. Look at the count + * field of the next buffer. If that next buffer's count is + * non-zero, the USC is still actively using the current buffer. + * Otherwise, if the next buffer's count field is zero, the + * current buffer is complete and the USC is using the next + * buffer. + */ + CurrentIndex = NextIndex = info->current_rx_buffer; + ++NextIndex; + if ( NextIndex == info->rx_buffer_count ) + NextIndex = 0; + + if ( info->rx_buffer_list[CurrentIndex].status != 0 || + (info->rx_buffer_list[CurrentIndex].count == 0 && + info->rx_buffer_list[NextIndex].count == 0)) { + /* + * Either the status field of this dma buffer is non-zero + * (indicating the last buffer of a receive frame) or the next + * buffer is marked as in use -- implying this buffer is complete + * and an intermediate buffer for this received frame. + */ + + status = info->rx_buffer_list[CurrentIndex].status; + + if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + + RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & RXSTATUS_SHORT_FRAME ) + info->icount.rxshort++; + else if ( status & RXSTATUS_ABORT ) + info->icount.rxabort++; + else if ( status & RXSTATUS_OVERRUN ) + info->icount.rxover++; + else + info->icount.rxcrc++; + framesize = 0; + } else { + /* + * A receive frame is available, get frame size and status. + * + * The frame size is the starting value of the RCC (which was + * set to 0xffff) minus the ending value of the RCC (decremented + * once for each receive character) minus 2 or 4 for the 16-bit + * or 32-bit CRC. + * + * If the status field is zero, this is an intermediate buffer. + * It's size is 4K. + * + * If the DMA Buffer Entry's Status field is non-zero, the + * receive operation completed normally (ie: DCD dropped). The + * RCC field is valid and holds the received frame size. + * It is possible that the RCC field will be zero on a DMA buffer + * entry with a non-zero status. This can occur if the total + * frame size (number of bytes between the time DCD goes active + * to the time DCD goes inactive) exceeds 65535 bytes. In this + * case the 16C32 has underrun on the RCC count and appears to + * stop updating this counter to let us know the actual received + * frame size. If this happens (non-zero status and zero RCC), + * simply return the entire RxDMA Buffer + */ + if ( status ) { + /* + * In the event that the final RxDMA Buffer is + * terminated with a non-zero status and the RCC + * field is zero, we interpret this as the RCC + * having underflowed (received frame > 65535 bytes). + * + * Signal the event to the user by passing back + * a status of RxStatus_CrcError returning the full + * buffer and let the app figure out what data is + * actually valid + */ + if ( info->rx_buffer_list[CurrentIndex].rcc ) + framesize = RCLRVALUE - info->rx_buffer_list[CurrentIndex].rcc; + else + framesize = DMABUFFERSIZE; + } + else + framesize = DMABUFFERSIZE; + } + + if ( framesize > DMABUFFERSIZE ) { + /* + * if running in raw sync mode, ISR handler for + * End Of Buffer events terminates all buffers at 4K. + * If this frame size is said to be >4K, get the + * actual number of bytes of the frame in this buffer. + */ + framesize = framesize % DMABUFFERSIZE; + } + + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk("%s(%d):mgsl_get_raw_rx_frame(%s) status=%04X size=%d\n", + __FILE__,__LINE__,info->device_name,status,framesize); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr, + min_t(int, framesize, DMABUFFERSIZE),0); + + if (framesize) { + /* copy dma buffer(s) to contiguous intermediate buffer */ + /* NOTE: we never copy more than DMABUFFERSIZE bytes */ + + pBufEntry = &(info->rx_buffer_list[CurrentIndex]); + memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize); + info->icount.rxok++; + + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); + } + + /* Free the buffers used by this frame. */ + mgsl_free_rx_frame_buffers( info, CurrentIndex, CurrentIndex ); + + ReturnCode = true; + } + + + if ( info->rx_enabled && info->rx_overflow ) { + /* The receiver needs to restarted because of + * a receive overflow (buffer or FIFO). If the + * receive buffers are now empty, then restart receiver. + */ + + if ( !info->rx_buffer_list[CurrentIndex].status && + info->rx_buffer_list[CurrentIndex].count ) { + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + return ReturnCode; + +} /* end of mgsl_get_raw_rx_frame() */ + +/* mgsl_load_tx_dma_buffer() + * + * Load the transmit DMA buffer with the specified data. + * + * Arguments: + * + * info pointer to device extension + * Buffer pointer to buffer containing frame to load + * BufferSize size in bytes of frame in Buffer + * + * Return Value: None + */ +static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, + const char *Buffer, unsigned int BufferSize) +{ + unsigned short Copycount; + unsigned int i = 0; + DMABUFFERENTRY *pBufEntry; + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1); + + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + /* set CMR:13 to start transmit when + * next GoAhead (abort) is received + */ + info->cmr_value |= BIT13; + } + + /* begin loading the frame in the next available tx dma + * buffer, remember it's starting location for setting + * up tx dma operation + */ + i = info->current_tx_buffer; + info->start_tx_dma_buffer = i; + + /* Setup the status and RCC (Frame Size) fields of the 1st */ + /* buffer entry in the transmit DMA buffer list. */ + + info->tx_buffer_list[i].status = info->cmr_value & 0xf000; + info->tx_buffer_list[i].rcc = BufferSize; + info->tx_buffer_list[i].count = BufferSize; + + /* Copy frame data from 1st source buffer to the DMA buffers. */ + /* The frame data may span multiple DMA buffers. */ + + while( BufferSize ){ + /* Get a pointer to next DMA buffer entry. */ + pBufEntry = &info->tx_buffer_list[i++]; + + if ( i == info->tx_buffer_count ) + i=0; + + /* Calculate the number of bytes that can be copied from */ + /* the source buffer to this DMA buffer. */ + if ( BufferSize > DMABUFFERSIZE ) + Copycount = DMABUFFERSIZE; + else + Copycount = BufferSize; + + /* Actually copy data from source buffer to DMA buffer. */ + /* Also set the data count for this individual DMA buffer. */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount); + else + memcpy(pBufEntry->virt_addr, Buffer, Copycount); + + pBufEntry->count = Copycount; + + /* Advance source pointer and reduce remaining data count. */ + Buffer += Copycount; + BufferSize -= Copycount; + + ++info->tx_dma_buffers_used; + } + + /* remember next available tx dma buffer */ + info->current_tx_buffer = i; + +} /* end of mgsl_load_tx_dma_buffer() */ + +/* + * mgsl_register_test() + * + * Performs a register test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: true if test passed, otherwise false + */ +static bool mgsl_register_test( struct mgsl_struct *info ) +{ + static unsigned short BitPatterns[] = + { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f }; + static unsigned int Patterncount = ARRAY_SIZE(BitPatterns); + unsigned int i; + bool rc = true; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + + /* Verify the reset state of some registers. */ + + if ( (usc_InReg( info, SICR ) != 0) || + (usc_InReg( info, IVR ) != 0) || + (usc_InDmaReg( info, DIVR ) != 0) ){ + rc = false; + } + + if ( rc ){ + /* Write bit patterns to various registers but do it out of */ + /* sync, then read back and verify values. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + usc_OutReg( info, TC0R, BitPatterns[i] ); + usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] ); + usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] ); + usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] ); + usc_OutReg( info, RSR, BitPatterns[(i+4)%Patterncount] ); + usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] ); + + if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) || + (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) || + (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) || + (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) || + (usc_InReg( info, RSR ) != BitPatterns[(i+4)%Patterncount]) || + (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){ + rc = false; + break; + } + } + } + + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return rc; + +} /* end of mgsl_register_test() */ + +/* mgsl_irq_test() Perform interrupt test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: true if test passed, otherwise false + */ +static bool mgsl_irq_test( struct mgsl_struct *info ) +{ + unsigned long EndTime; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + + /* + * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. + * The ISR sets irq_occurred to true. + */ + + info->irq_occurred = false; + + /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */ + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); + + usc_EnableMasterIrqBit(info); + usc_EnableInterrupts(info, IO_PIN); + usc_ClearIrqPendingBits(info, IO_PIN); + + usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED); + usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + EndTime=100; + while( EndTime-- && !info->irq_occurred ) { + msleep_interruptible(10); + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return info->irq_occurred; + +} /* end of mgsl_irq_test() */ + +/* mgsl_dma_test() + * + * Perform a DMA test of the 16C32. A small frame is + * transmitted via DMA from a transmit buffer to a receive buffer + * using single buffer DMA mode. + * + * Arguments: info pointer to device instance data + * Return Value: true if test passed, otherwise false + */ +static bool mgsl_dma_test( struct mgsl_struct *info ) +{ + unsigned short FifoLevel; + unsigned long phys_addr; + unsigned int FrameSize; + unsigned int i; + char *TmpPtr; + bool rc = true; + unsigned short status=0; + unsigned long EndTime; + unsigned long flags; + MGSL_PARAMS tmp_params; + + /* save current port options */ + memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); + /* load default port options */ + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + +#define TESTFRAMESIZE 40 + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup 16C32 for SDLC DMA transfer mode */ + + usc_reset(info); + usc_set_sdlc_mode(info); + usc_enable_loopback(info,1); + + /* Reprogram the RDMR so that the 16C32 does NOT clear the count + * field of the buffer entry after fetching buffer address. This + * way we can detect a DMA failure for a DMA read (which should be + * non-destructive to system memory) before we try and write to + * memory (where a failure could corrupt system memory). + */ + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in List entry + * <12> 0 1 = Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1110 0010 0000 0000 = 0xe200 + */ + + usc_OutDmaReg( info, RDMR, 0xe200 ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */ + + FrameSize = TESTFRAMESIZE; + + /* setup 1st transmit buffer entry: */ + /* with frame size and transmit control word */ + + info->tx_buffer_list[0].count = FrameSize; + info->tx_buffer_list[0].rcc = FrameSize; + info->tx_buffer_list[0].status = 0x4000; + + /* build a transmit frame in 1st transmit DMA buffer */ + + TmpPtr = info->tx_buffer_list[0].virt_addr; + for (i = 0; i < FrameSize; i++ ) + *TmpPtr++ = i; + + /* setup 1st receive buffer entry: */ + /* clear status, set max receive buffer size */ + + info->rx_buffer_list[0].status = 0; + info->rx_buffer_list[0].count = FrameSize + 4; + + /* zero out the 1st receive buffer */ + + memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 ); + + /* Set count field of next buffer entries to prevent */ + /* 16C32 from using buffers after the 1st one. */ + + info->tx_buffer_list[1].count = 0; + info->rx_buffer_list[1].count = 0; + + + /***************************/ + /* Program 16C32 receiver. */ + /***************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup DMA transfers */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + /* program 16C32 receiver with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) ); + + /* Clear the Rx DMA status bits (read RDMR) and start channel */ + usc_InDmaReg( info, RDMR ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + + /* Enable Receiver (RMR <1..0> = 10) */ + usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /*************************************************************/ + /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */ + /*************************************************************/ + + /* Wait 100ms for interrupt. */ + EndTime = jiffies + msecs_to_jiffies(100); + + for(;;) { + if (time_after(jiffies, EndTime)) { + rc = false; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InDmaReg( info, RDMR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( !(status & BIT4) && (status & BIT5) ) { + /* INITG (BIT 4) is inactive (no entry read in progress) AND */ + /* BUSY (BIT 5) is active (channel still active). */ + /* This means the buffer entry read has completed. */ + break; + } + } + + + /******************************/ + /* Program 16C32 transmitter. */ + /******************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + + usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + + phys_addr = info->tx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) ); + + /* unlatch Tx status bits, and start transmit channel. */ + + usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0f00) | 0xfa) ); + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + /* wait for DMA controller to fill transmit FIFO */ + + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /**********************************/ + /* WAIT FOR TRANSMIT FIFO TO FILL */ + /**********************************/ + + /* Wait 100ms */ + EndTime = jiffies + msecs_to_jiffies(100); + + for(;;) { + if (time_after(jiffies, EndTime)) { + rc = false; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + FifoLevel = usc_InReg(info, TICR) >> 8; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( FifoLevel < 16 ) + break; + else + if ( FrameSize < 32 ) { + /* This frame is smaller than the entire transmit FIFO */ + /* so wait for the entire frame to be loaded. */ + if ( FifoLevel <= (32 - FrameSize) ) + break; + } + } + + + if ( rc ) + { + /* Enable 16C32 transmitter. */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */ + usc_TCmd( info, TCmd_SendFrame ); + usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /******************************/ + /* WAIT FOR TRANSMIT COMPLETE */ + /******************************/ + + /* Wait 100ms */ + EndTime = jiffies + msecs_to_jiffies(100); + + /* While timer not expired wait for transmit complete */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { + if (time_after(jiffies, EndTime)) { + rc = false; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + + if ( rc ){ + /* CHECK FOR TRANSMIT ERRORS */ + if ( status & (BIT5 + BIT1) ) + rc = false; + } + + if ( rc ) { + /* WAIT FOR RECEIVE COMPLETE */ + + /* Wait 100ms */ + EndTime = jiffies + msecs_to_jiffies(100); + + /* Wait for 16C32 to write receive status to buffer entry. */ + status=info->rx_buffer_list[0].status; + while ( status == 0 ) { + if (time_after(jiffies, EndTime)) { + rc = false; + break; + } + status=info->rx_buffer_list[0].status; + } + } + + + if ( rc ) { + /* CHECK FOR RECEIVE ERRORS */ + status = info->rx_buffer_list[0].status; + + if ( status & (BIT8 + BIT3 + BIT1) ) { + /* receive error has occurred */ + rc = false; + } else { + if ( memcmp( info->tx_buffer_list[0].virt_addr , + info->rx_buffer_list[0].virt_addr, FrameSize ) ){ + rc = false; + } + } + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* restore current port options */ + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + + return rc; + +} /* end of mgsl_dma_test() */ + +/* mgsl_adapter_test() + * + * Perform the register, IRQ, and DMA tests for the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise -ENODEV + */ +static int mgsl_adapter_test( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):Testing device %s\n", + __FILE__,__LINE__,info->device_name ); + + if ( !mgsl_register_test( info ) ) { + info->init_error = DiagStatus_AddressFailure; + printk( "%s(%d):Register test failure for device %s Addr=%04X\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) ); + return -ENODEV; + } + + if ( !mgsl_irq_test( info ) ) { + info->init_error = DiagStatus_IrqFailure; + printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); + return -ENODEV; + } + + if ( !mgsl_dma_test( info ) ) { + info->init_error = DiagStatus_DmaFailure; + printk( "%s(%d):DMA test failure for device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) ); + return -ENODEV; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):device %s passed diagnostics\n", + __FILE__,__LINE__,info->device_name ); + + return 0; + +} /* end of mgsl_adapter_test() */ + +/* mgsl_memory_test() + * + * Test the shared memory on a PCI adapter. + * + * Arguments: info pointer to device instance data + * Return Value: true if test passed, otherwise false + */ +static bool mgsl_memory_test( struct mgsl_struct *info ) +{ + static unsigned long BitPatterns[] = + { 0x0, 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; + unsigned long Patterncount = ARRAY_SIZE(BitPatterns); + unsigned long i; + unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long); + unsigned long * TestAddr; + + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + return true; + + TestAddr = (unsigned long *)info->memory_base; + + /* Test data lines with test pattern at one location. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + *TestAddr = BitPatterns[i]; + if ( *TestAddr != BitPatterns[i] ) + return false; + } + + /* Test address lines with incrementing pattern over */ + /* entire address range. */ + + for ( i = 0 ; i < TestLimit ; i++ ) { + *TestAddr = i * 4; + TestAddr++; + } + + TestAddr = (unsigned long *)info->memory_base; + + for ( i = 0 ; i < TestLimit ; i++ ) { + if ( *TestAddr != i * 4 ) + return false; + TestAddr++; + } + + memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE ); + + return true; + +} /* End Of mgsl_memory_test() */ + + +/* mgsl_load_pci_memory() + * + * Load a large block of data into the PCI shared memory. + * Use this instead of memcpy() or memmove() to move data + * into the PCI shared memory. + * + * Notes: + * + * This function prevents the PCI9050 interface chip from hogging + * the adapter local bus, which can starve the 16C32 by preventing + * 16C32 bus master cycles. + * + * The PCI9050 documentation says that the 9050 will always release + * control of the local bus after completing the current read + * or write operation. + * + * It appears that as long as the PCI9050 write FIFO is full, the + * PCI9050 treats all of the writes as a single burst transaction + * and will not release the bus. This causes DMA latency problems + * at high speeds when copying large data blocks to the shared + * memory. + * + * This function in effect, breaks the a large shared memory write + * into multiple transations by interleaving a shared memory read + * which will flush the write FIFO and 'complete' the write + * transation. This allows any pending DMA request to gain control + * of the local bus in a timely fasion. + * + * Arguments: + * + * TargetPtr pointer to target address in PCI shared memory + * SourcePtr pointer to source buffer for data + * count count in bytes of data to copy + * + * Return Value: None + */ +static void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, + unsigned short count ) +{ + /* 16 32-bit writes @ 60ns each = 960ns max latency on local bus */ +#define PCI_LOAD_INTERVAL 64 + + unsigned short Intervalcount = count / PCI_LOAD_INTERVAL; + unsigned short Index; + unsigned long Dummy; + + for ( Index = 0 ; Index < Intervalcount ; Index++ ) + { + memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL); + Dummy = *((volatile unsigned long *)TargetPtr); + TargetPtr += PCI_LOAD_INTERVAL; + SourcePtr += PCI_LOAD_INTERVAL; + } + + memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL ); + +} /* End Of mgsl_load_pci_memory() */ + +static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit) +{ + int i; + int linecount; + if (xmit) + printk("%s tx data:\n",info->device_name); + else + printk("%s rx data:\n",info->device_name); + + while(count) { + if (count > 16) + linecount = 16; + else + linecount = count; + + for(i=0;i=040 && data[i]<=0176) + printk("%c",data[i]); + else + printk("."); + } + printk("\n"); + + data += linecount; + count -= linecount; + } +} /* end of mgsl_trace_block() */ + +/* mgsl_tx_timeout() + * + * called when HDLC frame times out + * update stats and do tx completion processing + * + * Arguments: context pointer to device instance data + * Return Value: None + */ +static void mgsl_tx_timeout(unsigned long context) +{ + struct mgsl_struct *info = (struct mgsl_struct*)context; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_tx_timeout(%s)\n", + __FILE__,__LINE__,info->device_name); + if(info->tx_active && + (info->params.mode == MGSL_MODE_HDLC || + info->params.mode == MGSL_MODE_RAW) ) { + info->icount.txtimeout++; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + info->tx_active = false; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + mgsl_bh_transmit(info); + +} /* end of mgsl_tx_timeout() */ + +/* signal that there are no more frames to send, so that + * line is 'released' by echoing RxD to TxD when current + * transmission is complete (or immediately if no tx in progress). + */ +static int mgsl_loopmode_send_done( struct mgsl_struct * info ) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + if (info->tx_active) + info->loopmode_send_done_requested = true; + else + usc_loopmode_send_done(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; +} + +/* release the line by echoing RxD to TxD + * upon completion of a transmit frame + */ +static void usc_loopmode_send_done( struct mgsl_struct * info ) +{ + info->loopmode_send_done_requested = false; + /* clear CMR:13 to 0 to start echoing RxData to TxData */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* abort a transmit in progress while in HDLC LoopMode + */ +static void usc_loopmode_cancel_transmit( struct mgsl_struct * info ) +{ + /* reset tx dma channel and purge TxFifo */ + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_loopmode_send_done( info ); +} + +/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled + * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort) + * we must clear CMR:13 to begin repeating TxData to RxData + */ +static void usc_loopmode_insert_request( struct mgsl_struct * info ) +{ + info->loopmode_insert_requested = true; + + /* enable RxAbort irq. On next RxAbort, clear CMR:13 to + * begin repeating TxData on RxData (complete insertion) + */ + usc_OutReg( info, RICR, + (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) ); + + /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */ + info->cmr_value |= BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* return 1 if station is inserted into the loop, otherwise 0 + */ +static int usc_loopmode_active( struct mgsl_struct * info) +{ + return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ; +} + +#if SYNCLINK_GENERIC_HDLC + +/** + * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) + * set encoding and frame check sequence (FCS) options + * + * dev pointer to network device structure + * encoding serial encoding setting + * parity FCS setting + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + struct mgsl_struct *info = dev_to_port(dev); + unsigned char new_encoding; + unsigned short new_crctype; + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + switch (encoding) + { + case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; + case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; + case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; + case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; + case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; + default: return -EINVAL; + } + + switch (parity) + { + case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; + case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; + case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; + default: return -EINVAL; + } + + info->params.encoding = new_encoding; + info->params.crc_type = new_crctype; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + mgsl_program_hw(info); + + return 0; +} + +/** + * called by generic HDLC layer to send frame + * + * skb socket buffer containing HDLC frame + * dev pointer to network device structure + */ +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mgsl_struct *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name); + + /* stop sending until this frame completes */ + netif_stop_queue(dev); + + /* copy data to device buffers */ + info->xmit_cnt = skb->len; + mgsl_load_tx_dma_buffer(info, skb->data, skb->len); + + /* update network statistics */ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* done with socket buffer, so free it */ + dev_kfree_skb(skb); + + /* save start time for transmit timeout detection */ + dev->trans_start = jiffies; + + /* start hardware transmitter if necessary */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_active) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return NETDEV_TX_OK; +} + +/** + * called by network layer when interface enabled + * claim resources and initialize hardware + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_open(struct net_device *dev) +{ + struct mgsl_struct *info = dev_to_port(dev); + int rc; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); + + /* generic HDLC layer open processing */ + if ((rc = hdlc_open(dev))) + return rc; + + /* arbitrate between network and tty opens */ + spin_lock_irqsave(&info->netlock, flags); + if (info->port.count != 0 || info->netcount != 0) { + printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); + spin_unlock_irqrestore(&info->netlock, flags); + return -EBUSY; + } + info->netcount=1; + spin_unlock_irqrestore(&info->netlock, flags); + + /* claim resources and init adapter */ + if ((rc = startup(info)) != 0) { + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + return rc; + } + + /* assert DTR and RTS, apply hardware settings */ + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + mgsl_program_hw(info); + + /* enable network layer transmit */ + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* inform generic HDLC layer of current DCD status */ + spin_lock_irqsave(&info->irq_spinlock, flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock, flags); + if (info->serial_signals & SerialSignal_DCD) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + +/** + * called by network layer when interface is disabled + * shutdown hardware and release resources + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_close(struct net_device *dev) +{ + struct mgsl_struct *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name); + + netif_stop_queue(dev); + + /* shutdown adapter and release resources */ + shutdown(info); + + hdlc_close(dev); + + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + + return 0; +} + +/** + * called by network layer to process IOCTL call to network device + * + * dev pointer to network device structure + * ifr pointer to network interface request structure + * cmd IOCTL command code + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(sync_serial_settings); + sync_serial_settings new_line; + sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; + struct mgsl_struct *info = dev_to_port(dev); + unsigned int flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name); + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: /* return current sync_serial_settings */ + + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + + flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + + switch (flags){ + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; + case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; + default: new_line.clock_type = CLOCK_DEFAULT; + } + + new_line.clock_rate = info->params.clock_speed; + new_line.loopback = info->params.loopback ? 1:0; + + if (copy_to_user(line, &new_line, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + switch (new_line.clock_type) + { + case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; + case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; + case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; + case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; + case CLOCK_DEFAULT: flags = info->params.flags & + (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; + default: return -EINVAL; + } + + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; + + info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + info->params.flags |= flags; + + info->params.loopback = new_line.loopback; + + if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) + info->params.clock_speed = new_line.clock_rate; + else + info->params.clock_speed = 0; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + mgsl_program_hw(info); + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +/** + * called by network layer when transmit timeout is detected + * + * dev pointer to network device structure + */ +static void hdlcdev_tx_timeout(struct net_device *dev) +{ + struct mgsl_struct *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("hdlcdev_tx_timeout(%s)\n",dev->name); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_stop_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + netif_wake_queue(dev); +} + +/** + * called by device driver when transmit completes + * reenable network layer transmit if stopped + * + * info pointer to device instance information + */ +static void hdlcdev_tx_done(struct mgsl_struct *info) +{ + if (netif_queue_stopped(info->netdev)) + netif_wake_queue(info->netdev); +} + +/** + * called by device driver when frame received + * pass frame to network layer + * + * info pointer to device instance information + * buf pointer to buffer contianing frame data + * size count of data bytes in buf + */ +static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) +{ + struct sk_buff *skb = dev_alloc_skb(size); + struct net_device *dev = info->netdev; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("hdlcdev_rx(%s)\n", dev->name); + + if (skb == NULL) { + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; + return; + } + + memcpy(skb_put(skb, size), buf, size); + + skb->protocol = hdlc_type_trans(skb, dev); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; + + netif_rx(skb); +} + +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + +/** + * called by device driver when adding device instance + * do generic HDLC initialization + * + * info pointer to device instance information + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_init(struct mgsl_struct *info) +{ + int rc; + struct net_device *dev; + hdlc_device *hdlc; + + /* allocate and initialize network and HDLC layer objects */ + + if (!(dev = alloc_hdlcdev(info))) { + printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); + return -ENOMEM; + } + + /* for network layer reporting purposes only */ + dev->base_addr = info->io_base; + dev->irq = info->irq_level; + dev->dma = info->dma_level; + + /* network layer callbacks and settings */ + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; + dev->tx_queue_len = 50; + + /* generic HDLC layer callbacks and settings */ + hdlc = dev_to_hdlc(dev); + hdlc->attach = hdlcdev_attach; + hdlc->xmit = hdlcdev_xmit; + + /* register objects with HDLC layer */ + if ((rc = register_hdlc_device(dev))) { + printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); + free_netdev(dev); + return rc; + } + + info->netdev = dev; + return 0; +} + +/** + * called by device driver when removing device instance + * do generic HDLC cleanup + * + * info pointer to device instance information + */ +static void hdlcdev_exit(struct mgsl_struct *info) +{ + unregister_hdlc_device(info->netdev); + free_netdev(info->netdev); + info->netdev = NULL; +} + +#endif /* CONFIG_HDLC */ + + +static int __devinit synclink_init_one (struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct mgsl_struct *info; + + if (pci_enable_device(dev)) { + printk("error enabling pci device %p\n", dev); + return -EIO; + } + + if (!(info = mgsl_allocate_device())) { + printk("can't allocate device instance data.\n"); + return -EIO; + } + + /* Copy user configuration info to device instance data */ + + info->io_base = pci_resource_start(dev, 2); + info->irq_level = dev->irq; + info->phys_memory_base = pci_resource_start(dev, 3); + + /* Because veremap only works on page boundaries we must map + * a larger area than is actually implemented for the LCR + * memory range. We map a full page starting at the page boundary. + */ + info->phys_lcr_base = pci_resource_start(dev, 0); + info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); + info->phys_lcr_base &= ~(PAGE_SIZE-1); + + info->bus_type = MGSL_BUS_TYPE_PCI; + info->io_addr_size = 8; + info->irq_flags = IRQF_SHARED; + + if (dev->device == 0x0210) { + /* Version 1 PCI9030 based universal PCI adapter */ + info->misc_ctrl_value = 0x007c4080; + info->hw_version = 1; + } else { + /* Version 0 PCI9050 based 5V PCI adapter + * A PCI9050 bug prevents reading LCR registers if + * LCR base address bit 7 is set. Maintain shadow + * value so we can write to LCR misc control reg. + */ + info->misc_ctrl_value = 0x087e4546; + info->hw_version = 0; + } + + mgsl_add_device(info); + + return 0; +} + +static void __devexit synclink_remove_one (struct pci_dev *dev) +{ +} + diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c new file mode 100644 index 000000000000..a35dd549a008 --- /dev/null +++ b/drivers/tty/synclink_gt.c @@ -0,0 +1,5161 @@ +/* + * Device driver for Microgate SyncLink GT serial adapters. + * + * written by Paul Fulghum for Microgate Corporation + * paulkf@microgate.com + * + * Microgate and SyncLink are trademarks of Microgate Corporation + * + * This code is released under the GNU General Public License (GPL) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * DEBUG OUTPUT DEFINITIONS + * + * uncomment lines below to enable specific types of debug output + * + * DBGINFO information - most verbose output + * DBGERR serious errors + * DBGBH bottom half service routine debugging + * DBGISR interrupt service routine debugging + * DBGDATA output receive and transmit data + * DBGTBUF output transmit DMA buffers and registers + * DBGRBUF output receive DMA buffers and registers + */ + +#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt +#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt +#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt +#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt +#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label)) +/*#define DBGTBUF(info) dump_tbufs(info)*/ +/*#define DBGRBUF(info) dump_rbufs(info)*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) +#define SYNCLINK_GENERIC_HDLC 1 +#else +#define SYNCLINK_GENERIC_HDLC 0 +#endif + +/* + * module identification + */ +static char *driver_name = "SyncLink GT"; +static char *tty_driver_name = "synclink_gt"; +static char *tty_dev_prefix = "ttySLG"; +MODULE_LICENSE("GPL"); +#define MGSL_MAGIC 0x5401 +#define MAX_DEVICES 32 + +static struct pci_device_id pci_table[] = { + {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, pci_table); + +static int init_one(struct pci_dev *dev,const struct pci_device_id *ent); +static void remove_one(struct pci_dev *dev); +static struct pci_driver pci_driver = { + .name = "synclink_gt", + .id_table = pci_table, + .probe = init_one, + .remove = __devexit_p(remove_one), +}; + +static bool pci_registered; + +/* + * module configuration and status + */ +static struct slgt_info *slgt_device_list; +static int slgt_device_count; + +static int ttymajor; +static int debug_level; +static int maxframe[MAX_DEVICES]; + +module_param(ttymajor, int, 0); +module_param(debug_level, int, 0); +module_param_array(maxframe, int, NULL, 0); + +MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned"); +MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail"); +MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); + +/* + * tty support and callbacks + */ +static struct tty_driver *serial_driver; + +static int open(struct tty_struct *tty, struct file * filp); +static void close(struct tty_struct *tty, struct file * filp); +static void hangup(struct tty_struct *tty); +static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); + +static int write(struct tty_struct *tty, const unsigned char *buf, int count); +static int put_char(struct tty_struct *tty, unsigned char ch); +static void send_xchar(struct tty_struct *tty, char ch); +static void wait_until_sent(struct tty_struct *tty, int timeout); +static int write_room(struct tty_struct *tty); +static void flush_chars(struct tty_struct *tty); +static void flush_buffer(struct tty_struct *tty); +static void tx_hold(struct tty_struct *tty); +static void tx_release(struct tty_struct *tty); + +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static int chars_in_buffer(struct tty_struct *tty); +static void throttle(struct tty_struct * tty); +static void unthrottle(struct tty_struct * tty); +static int set_break(struct tty_struct *tty, int break_state); + +/* + * generic HDLC support and callbacks + */ +#if SYNCLINK_GENERIC_HDLC +#define dev_to_port(D) (dev_to_hdlc(D)->priv) +static void hdlcdev_tx_done(struct slgt_info *info); +static void hdlcdev_rx(struct slgt_info *info, char *buf, int size); +static int hdlcdev_init(struct slgt_info *info); +static void hdlcdev_exit(struct slgt_info *info); +#endif + + +/* + * device specific structures, macros and functions + */ + +#define SLGT_MAX_PORTS 4 +#define SLGT_REG_SIZE 256 + +/* + * conditional wait facility + */ +struct cond_wait { + struct cond_wait *next; + wait_queue_head_t q; + wait_queue_t wait; + unsigned int data; +}; +static void init_cond_wait(struct cond_wait *w, unsigned int data); +static void add_cond_wait(struct cond_wait **head, struct cond_wait *w); +static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w); +static void flush_cond_wait(struct cond_wait **head); + +/* + * DMA buffer descriptor and access macros + */ +struct slgt_desc +{ + __le16 count; + __le16 status; + __le32 pbuf; /* physical address of data buffer */ + __le32 next; /* physical address of next descriptor */ + + /* driver book keeping */ + char *buf; /* virtual address of data buffer */ + unsigned int pdesc; /* physical address of this descriptor */ + dma_addr_t buf_dma_addr; + unsigned short buf_count; +}; + +#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) +#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b)) +#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b)) +#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0)) +#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b)) +#define desc_count(a) (le16_to_cpu((a).count)) +#define desc_status(a) (le16_to_cpu((a).status)) +#define desc_complete(a) (le16_to_cpu((a).status) & BIT15) +#define desc_eof(a) (le16_to_cpu((a).status) & BIT2) +#define desc_crc_error(a) (le16_to_cpu((a).status) & BIT1) +#define desc_abort(a) (le16_to_cpu((a).status) & BIT0) +#define desc_residue(a) ((le16_to_cpu((a).status) & 0x38) >> 3) + +struct _input_signal_events { + int ri_up; + int ri_down; + int dsr_up; + int dsr_down; + int dcd_up; + int dcd_down; + int cts_up; + int cts_down; +}; + +/* + * device instance data structure + */ +struct slgt_info { + void *if_ptr; /* General purpose pointer (used by SPPP) */ + struct tty_port port; + + struct slgt_info *next_device; /* device list link */ + + int magic; + + char device_name[25]; + struct pci_dev *pdev; + + int port_count; /* count of ports on adapter */ + int adapter_num; /* adapter instance number */ + int port_num; /* port instance number */ + + /* array of pointers to port contexts on this adapter */ + struct slgt_info *port_array[SLGT_MAX_PORTS]; + + int line; /* tty line instance number */ + + struct mgsl_icount icount; + + int timeout; + int x_char; /* xon/xoff character */ + unsigned int read_status_mask; + unsigned int ignore_status_mask; + + wait_queue_head_t status_event_wait_q; + wait_queue_head_t event_wait_q; + struct timer_list tx_timer; + struct timer_list rx_timer; + + unsigned int gpio_present; + struct cond_wait *gpio_wait_q; + + spinlock_t lock; /* spinlock for synchronizing with ISR */ + + struct work_struct task; + u32 pending_bh; + bool bh_requested; + bool bh_running; + + int isr_overflow; + bool irq_requested; /* true if IRQ requested */ + bool irq_occurred; /* for diagnostics use */ + + /* device configuration */ + + unsigned int bus_type; + unsigned int irq_level; + unsigned long irq_flags; + + unsigned char __iomem * reg_addr; /* memory mapped registers address */ + u32 phys_reg_addr; + bool reg_addr_requested; + + MGSL_PARAMS params; /* communications parameters */ + u32 idle_mode; + u32 max_frame_size; /* as set by device config */ + + unsigned int rbuf_fill_level; + unsigned int rx_pio; + unsigned int if_mode; + unsigned int base_clock; + unsigned int xsync; + unsigned int xctrl; + + /* device status */ + + bool rx_enabled; + bool rx_restart; + + bool tx_enabled; + bool tx_active; + + unsigned char signals; /* serial signal states */ + int init_error; /* initialization error */ + + unsigned char *tx_buf; + int tx_count; + + char flag_buf[MAX_ASYNC_BUFFER_SIZE]; + char char_buf[MAX_ASYNC_BUFFER_SIZE]; + bool drop_rts_on_tx_done; + struct _input_signal_events input_signal_events; + + int dcd_chkcount; /* check counts to prevent */ + int cts_chkcount; /* too many IRQs if a signal */ + int dsr_chkcount; /* is floating */ + int ri_chkcount; + + char *bufs; /* virtual address of DMA buffer lists */ + dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */ + + unsigned int rbuf_count; + struct slgt_desc *rbufs; + unsigned int rbuf_current; + unsigned int rbuf_index; + unsigned int rbuf_fill_index; + unsigned short rbuf_fill_count; + + unsigned int tbuf_count; + struct slgt_desc *tbufs; + unsigned int tbuf_current; + unsigned int tbuf_start; + + unsigned char *tmp_rbuf; + unsigned int tmp_rbuf_count; + + /* SPPP/Cisco HDLC device parts */ + + int netcount; + spinlock_t netlock; +#if SYNCLINK_GENERIC_HDLC + struct net_device *netdev; +#endif + +}; + +static MGSL_PARAMS default_params = { + .mode = MGSL_MODE_HDLC, + .loopback = 0, + .flags = HDLC_FLAG_UNDERRUN_ABORT15, + .encoding = HDLC_ENCODING_NRZI_SPACE, + .clock_speed = 0, + .addr_filter = 0xff, + .crc_type = HDLC_CRC_16_CCITT, + .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS, + .preamble = HDLC_PREAMBLE_PATTERN_NONE, + .data_rate = 9600, + .data_bits = 8, + .stop_bits = 1, + .parity = ASYNC_PARITY_NONE +}; + + +#define BH_RECEIVE 1 +#define BH_TRANSMIT 2 +#define BH_STATUS 4 +#define IO_PIN_SHUTDOWN_LIMIT 100 + +#define DMABUFSIZE 256 +#define DESC_LIST_SIZE 4096 + +#define MASK_PARITY BIT1 +#define MASK_FRAMING BIT0 +#define MASK_BREAK BIT14 +#define MASK_OVERRUN BIT4 + +#define GSR 0x00 /* global status */ +#define JCR 0x04 /* JTAG control */ +#define IODR 0x08 /* GPIO direction */ +#define IOER 0x0c /* GPIO interrupt enable */ +#define IOVR 0x10 /* GPIO value */ +#define IOSR 0x14 /* GPIO interrupt status */ +#define TDR 0x80 /* tx data */ +#define RDR 0x80 /* rx data */ +#define TCR 0x82 /* tx control */ +#define TIR 0x84 /* tx idle */ +#define TPR 0x85 /* tx preamble */ +#define RCR 0x86 /* rx control */ +#define VCR 0x88 /* V.24 control */ +#define CCR 0x89 /* clock control */ +#define BDR 0x8a /* baud divisor */ +#define SCR 0x8c /* serial control */ +#define SSR 0x8e /* serial status */ +#define RDCSR 0x90 /* rx DMA control/status */ +#define TDCSR 0x94 /* tx DMA control/status */ +#define RDDAR 0x98 /* rx DMA descriptor address */ +#define TDDAR 0x9c /* tx DMA descriptor address */ +#define XSR 0x40 /* extended sync pattern */ +#define XCR 0x44 /* extended control */ + +#define RXIDLE BIT14 +#define RXBREAK BIT14 +#define IRQ_TXDATA BIT13 +#define IRQ_TXIDLE BIT12 +#define IRQ_TXUNDER BIT11 /* HDLC */ +#define IRQ_RXDATA BIT10 +#define IRQ_RXIDLE BIT9 /* HDLC */ +#define IRQ_RXBREAK BIT9 /* async */ +#define IRQ_RXOVER BIT8 +#define IRQ_DSR BIT7 +#define IRQ_CTS BIT6 +#define IRQ_DCD BIT5 +#define IRQ_RI BIT4 +#define IRQ_ALL 0x3ff0 +#define IRQ_MASTER BIT0 + +#define slgt_irq_on(info, mask) \ + wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask))) +#define slgt_irq_off(info, mask) \ + wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask))) + +static __u8 rd_reg8(struct slgt_info *info, unsigned int addr); +static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value); +static __u16 rd_reg16(struct slgt_info *info, unsigned int addr); +static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value); +static __u32 rd_reg32(struct slgt_info *info, unsigned int addr); +static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value); + +static void msc_set_vcr(struct slgt_info *info); + +static int startup(struct slgt_info *info); +static int block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info); +static void shutdown(struct slgt_info *info); +static void program_hw(struct slgt_info *info); +static void change_params(struct slgt_info *info); + +static int register_test(struct slgt_info *info); +static int irq_test(struct slgt_info *info); +static int loopback_test(struct slgt_info *info); +static int adapter_test(struct slgt_info *info); + +static void reset_adapter(struct slgt_info *info); +static void reset_port(struct slgt_info *info); +static void async_mode(struct slgt_info *info); +static void sync_mode(struct slgt_info *info); + +static void rx_stop(struct slgt_info *info); +static void rx_start(struct slgt_info *info); +static void reset_rbufs(struct slgt_info *info); +static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); +static void rdma_reset(struct slgt_info *info); +static bool rx_get_frame(struct slgt_info *info); +static bool rx_get_buf(struct slgt_info *info); + +static void tx_start(struct slgt_info *info); +static void tx_stop(struct slgt_info *info); +static void tx_set_idle(struct slgt_info *info); +static unsigned int free_tbuf_count(struct slgt_info *info); +static unsigned int tbuf_bytes(struct slgt_info *info); +static void reset_tbufs(struct slgt_info *info); +static void tdma_reset(struct slgt_info *info); +static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count); + +static void get_signals(struct slgt_info *info); +static void set_signals(struct slgt_info *info); +static void enable_loopback(struct slgt_info *info); +static void set_rate(struct slgt_info *info, u32 data_rate); + +static int bh_action(struct slgt_info *info); +static void bh_handler(struct work_struct *work); +static void bh_transmit(struct slgt_info *info); +static void isr_serial(struct slgt_info *info); +static void isr_rdma(struct slgt_info *info); +static void isr_txeom(struct slgt_info *info, unsigned short status); +static void isr_tdma(struct slgt_info *info); + +static int alloc_dma_bufs(struct slgt_info *info); +static void free_dma_bufs(struct slgt_info *info); +static int alloc_desc(struct slgt_info *info); +static void free_desc(struct slgt_info *info); +static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); +static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count); + +static int alloc_tmp_rbuf(struct slgt_info *info); +static void free_tmp_rbuf(struct slgt_info *info); + +static void tx_timeout(unsigned long context); +static void rx_timeout(unsigned long context); + +/* + * ioctl handlers + */ +static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount); +static int get_params(struct slgt_info *info, MGSL_PARAMS __user *params); +static int set_params(struct slgt_info *info, MGSL_PARAMS __user *params); +static int get_txidle(struct slgt_info *info, int __user *idle_mode); +static int set_txidle(struct slgt_info *info, int idle_mode); +static int tx_enable(struct slgt_info *info, int enable); +static int tx_abort(struct slgt_info *info); +static int rx_enable(struct slgt_info *info, int enable); +static int modem_input_wait(struct slgt_info *info,int arg); +static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); +static int tiocmget(struct tty_struct *tty); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static int set_break(struct tty_struct *tty, int break_state); +static int get_interface(struct slgt_info *info, int __user *if_mode); +static int set_interface(struct slgt_info *info, int if_mode); +static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); +static int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); +static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); +static int get_xsync(struct slgt_info *info, int __user *if_mode); +static int set_xsync(struct slgt_info *info, int if_mode); +static int get_xctrl(struct slgt_info *info, int __user *if_mode); +static int set_xctrl(struct slgt_info *info, int if_mode); + +/* + * driver functions + */ +static void add_device(struct slgt_info *info); +static void device_init(int adapter_num, struct pci_dev *pdev); +static int claim_resources(struct slgt_info *info); +static void release_resources(struct slgt_info *info); + +/* + * DEBUG OUTPUT CODE + */ +#ifndef DBGINFO +#define DBGINFO(fmt) +#endif +#ifndef DBGERR +#define DBGERR(fmt) +#endif +#ifndef DBGBH +#define DBGBH(fmt) +#endif +#ifndef DBGISR +#define DBGISR(fmt) +#endif + +#ifdef DBGDATA +static void trace_block(struct slgt_info *info, const char *data, int count, const char *label) +{ + int i; + int linecount; + printk("%s %s data:\n",info->device_name, label); + while(count) { + linecount = (count > 16) ? 16 : count; + for(i=0; i < linecount; i++) + printk("%02X ",(unsigned char)data[i]); + for(;i<17;i++) + printk(" "); + for(i=0;i=040 && data[i]<=0176) + printk("%c",data[i]); + else + printk("."); + } + printk("\n"); + data += linecount; + count -= linecount; + } +} +#else +#define DBGDATA(info, buf, size, label) +#endif + +#ifdef DBGTBUF +static void dump_tbufs(struct slgt_info *info) +{ + int i; + printk("tbuf_current=%d\n", info->tbuf_current); + for (i=0 ; i < info->tbuf_count ; i++) { + printk("%d: count=%04X status=%04X\n", + i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status)); + } +} +#else +#define DBGTBUF(info) +#endif + +#ifdef DBGRBUF +static void dump_rbufs(struct slgt_info *info) +{ + int i; + printk("rbuf_current=%d\n", info->rbuf_current); + for (i=0 ; i < info->rbuf_count ; i++) { + printk("%d: count=%04X status=%04X\n", + i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status)); + } +} +#else +#define DBGRBUF(info) +#endif + +static inline int sanity_check(struct slgt_info *info, char *devname, const char *name) +{ +#ifdef SANITY_CHECK + if (!info) { + printk("null struct slgt_info for (%s) in %s\n", devname, name); + return 1; + } + if (info->magic != MGSL_MAGIC) { + printk("bad magic number struct slgt_info (%s) in %s\n", devname, name); + return 1; + } +#else + if (!info) + return 1; +#endif + return 0; +} + +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + +/* tty callbacks */ + +static int open(struct tty_struct *tty, struct file *filp) +{ + struct slgt_info *info; + int retval, line; + unsigned long flags; + + line = tty->index; + if ((line < 0) || (line >= slgt_device_count)) { + DBGERR(("%s: open with invalid line #%d.\n", driver_name, line)); + return -ENODEV; + } + + info = slgt_device_list; + while(info && info->line != line) + info = info->next_device; + if (sanity_check(info, tty->name, "open")) + return -ENODEV; + if (info->init_error) { + DBGERR(("%s init error=%d\n", info->device_name, info->init_error)); + return -ENODEV; + } + + tty->driver_data = info; + info->port.tty = tty; + + DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); + + /* If port is closing, signal caller to try again */ + if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ + if (info->port.flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->port.close_wait); + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); + goto cleanup; + } + + mutex_lock(&info->port.mutex); + info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + spin_lock_irqsave(&info->netlock, flags); + if (info->netcount) { + retval = -EBUSY; + spin_unlock_irqrestore(&info->netlock, flags); + mutex_unlock(&info->port.mutex); + goto cleanup; + } + info->port.count++; + spin_unlock_irqrestore(&info->netlock, flags); + + if (info->port.count == 1) { + /* 1st open on this device, init hardware */ + retval = startup(info); + if (retval < 0) { + mutex_unlock(&info->port.mutex); + goto cleanup; + } + } + mutex_unlock(&info->port.mutex); + retval = block_til_ready(tty, filp, info); + if (retval) { + DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval)); + goto cleanup; + } + + retval = 0; + +cleanup: + if (retval) { + if (tty->count == 1) + info->port.tty = NULL; /* tty layer will release tty struct */ + if(info->port.count) + info->port.count--; + } + + DBGINFO(("%s open rc=%d\n", info->device_name, retval)); + return retval; +} + +static void close(struct tty_struct *tty, struct file *filp) +{ + struct slgt_info *info = tty->driver_data; + + if (sanity_check(info, tty->name, "close")) + return; + DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); + + if (tty_port_close_start(&info->port, tty, filp) == 0) + goto cleanup; + + mutex_lock(&info->port.mutex); + if (info->port.flags & ASYNC_INITIALIZED) + wait_until_sent(tty, info->timeout); + flush_buffer(tty); + tty_ldisc_flush(tty); + + shutdown(info); + mutex_unlock(&info->port.mutex); + + tty_port_close_end(&info->port, tty); + info->port.tty = NULL; +cleanup: + DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); +} + +static void hangup(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "hangup")) + return; + DBGINFO(("%s hangup\n", info->device_name)); + + flush_buffer(tty); + + mutex_lock(&info->port.mutex); + shutdown(info); + + spin_lock_irqsave(&info->port.lock, flags); + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + spin_unlock_irqrestore(&info->port.lock, flags); + mutex_unlock(&info->port.mutex); + + wake_up_interruptible(&info->port.open_wait); +} + +static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + DBGINFO(("%s set_termios\n", tty->driver->name)); + + change_params(info); + + /* Handle transition to B0 status */ + if (old_termios->c_cflag & CBAUD && + !(tty->termios->c_cflag & CBAUD)) { + info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + tty->termios->c_cflag & CBAUD) { + info->signals |= SerialSignal_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->signals |= SerialSignal_RTS; + } + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } + + /* Handle turning off CRTSCTS */ + if (old_termios->c_cflag & CRTSCTS && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + tx_release(tty); + } +} + +static void update_tx_timer(struct slgt_info *info) +{ + /* + * use worst case speed of 1200bps to calculate transmit timeout + * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) + */ + if (info->params.mode == MGSL_MODE_HDLC) { + int timeout = (tbuf_bytes(info) * 7) + 1000; + mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); + } +} + +static int write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int ret = 0; + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "write")) + return -EIO; + + DBGINFO(("%s write count=%d\n", info->device_name, count)); + + if (!info->tx_buf || (count > info->max_frame_size)) + return -EIO; + + if (!count || tty->stopped || tty->hw_stopped) + return 0; + + spin_lock_irqsave(&info->lock, flags); + + if (info->tx_count) { + /* send accumulated data from send_char() */ + if (!tx_load(info, info->tx_buf, info->tx_count)) + goto cleanup; + info->tx_count = 0; + } + + if (tx_load(info, buf, count)) + ret = count; + +cleanup: + spin_unlock_irqrestore(&info->lock, flags); + DBGINFO(("%s write rc=%d\n", info->device_name, ret)); + return ret; +} + +static int put_char(struct tty_struct *tty, unsigned char ch) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + int ret = 0; + + if (sanity_check(info, tty->name, "put_char")) + return 0; + DBGINFO(("%s put_char(%d)\n", info->device_name, ch)); + if (!info->tx_buf) + return 0; + spin_lock_irqsave(&info->lock,flags); + if (info->tx_count < info->max_frame_size) { + info->tx_buf[info->tx_count++] = ch; + ret = 1; + } + spin_unlock_irqrestore(&info->lock,flags); + return ret; +} + +static void send_xchar(struct tty_struct *tty, char ch) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "send_xchar")) + return; + DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch)); + info->x_char = ch; + if (ch) { + spin_lock_irqsave(&info->lock,flags); + if (!info->tx_enabled) + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +static void wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct slgt_info *info = tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (!info ) + return; + if (sanity_check(info, tty->name, "wait_until_sent")) + return; + DBGINFO(("%s wait_until_sent entry\n", info->device_name)); + if (!(info->port.flags & ASYNC_INITIALIZED)) + goto exit; + + orig_jiffies = jiffies; + + /* Set check interval to 1/5 of estimated time to + * send a character, and make it at least 1. The check + * interval should also be less than the timeout. + * Note: use tight timings here to satisfy the NIST-PCTS. + */ + + if (info->params.data_rate) { + char_time = info->timeout/(32 * 5); + if (!char_time) + char_time++; + } else + char_time = 1; + + if (timeout) + char_time = min_t(unsigned long, char_time, timeout); + + while (info->tx_active) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +exit: + DBGINFO(("%s wait_until_sent exit\n", info->device_name)); +} + +static int write_room(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + int ret; + + if (sanity_check(info, tty->name, "write_room")) + return 0; + ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; + DBGINFO(("%s write_room=%d\n", info->device_name, ret)); + return ret; +} + +static void flush_chars(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "flush_chars")) + return; + DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count)); + + if (info->tx_count <= 0 || tty->stopped || + tty->hw_stopped || !info->tx_buf) + return; + + DBGINFO(("%s flush_chars start transmit\n", info->device_name)); + + spin_lock_irqsave(&info->lock,flags); + if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) + info->tx_count = 0; + spin_unlock_irqrestore(&info->lock,flags); +} + +static void flush_buffer(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "flush_buffer")) + return; + DBGINFO(("%s flush_buffer\n", info->device_name)); + + spin_lock_irqsave(&info->lock, flags); + info->tx_count = 0; + spin_unlock_irqrestore(&info->lock, flags); + + tty_wakeup(tty); +} + +/* + * throttle (stop) transmitter + */ +static void tx_hold(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "tx_hold")) + return; + DBGINFO(("%s tx_hold\n", info->device_name)); + spin_lock_irqsave(&info->lock,flags); + if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC) + tx_stop(info); + spin_unlock_irqrestore(&info->lock,flags); +} + +/* + * release (start) transmitter + */ +static void tx_release(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "tx_release")) + return; + DBGINFO(("%s tx_release\n", info->device_name)); + spin_lock_irqsave(&info->lock, flags); + if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) + info->tx_count = 0; + spin_unlock_irqrestore(&info->lock, flags); +} + +/* + * Service an IOCTL request + * + * Arguments + * + * tty pointer to tty instance data + * cmd IOCTL command code + * arg command argument/context + * + * Return 0 if success, otherwise error code + */ +static int ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct slgt_info *info = tty->driver_data; + void __user *argp = (void __user *)arg; + int ret; + + if (sanity_check(info, tty->name, "ioctl")) + return -ENODEV; + DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case MGSL_IOCWAITEVENT: + return wait_mgsl_event(info, argp); + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); + case MGSL_IOCSGPIO: + return set_gpio(info, argp); + case MGSL_IOCGGPIO: + return get_gpio(info, argp); + case MGSL_IOCWAITGPIO: + return wait_gpio(info, argp); + case MGSL_IOCGXSYNC: + return get_xsync(info, argp); + case MGSL_IOCSXSYNC: + return set_xsync(info, (int)arg); + case MGSL_IOCGXCTRL: + return get_xctrl(info, argp); + case MGSL_IOCSXCTRL: + return set_xctrl(info, (int)arg); + } + mutex_lock(&info->port.mutex); + switch (cmd) { + case MGSL_IOCGPARAMS: + ret = get_params(info, argp); + break; + case MGSL_IOCSPARAMS: + ret = set_params(info, argp); + break; + case MGSL_IOCGTXIDLE: + ret = get_txidle(info, argp); + break; + case MGSL_IOCSTXIDLE: + ret = set_txidle(info, (int)arg); + break; + case MGSL_IOCTXENABLE: + ret = tx_enable(info, (int)arg); + break; + case MGSL_IOCRXENABLE: + ret = rx_enable(info, (int)arg); + break; + case MGSL_IOCTXABORT: + ret = tx_abort(info); + break; + case MGSL_IOCGSTATS: + ret = get_stats(info, argp); + break; + case MGSL_IOCGIF: + ret = get_interface(info, argp); + break; + case MGSL_IOCSIF: + ret = set_interface(info,(int)arg); + break; + default: + ret = -ENOIOCTLCMD; + } + mutex_unlock(&info->port.mutex); + return ret; +} + +static int get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) + +{ + struct slgt_info *info = tty->driver_data; + struct mgsl_icount cnow; /* kernel counter temps */ + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->lock,flags); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + + return 0; +} + +/* + * support for 32 bit ioctl calls on 64 bit systems + */ +#ifdef CONFIG_COMPAT +static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s get_params32\n", info->device_name)); + memset(&tmp_params, 0, sizeof(tmp_params)); + tmp_params.mode = (compat_ulong_t)info->params.mode; + tmp_params.loopback = info->params.loopback; + tmp_params.flags = info->params.flags; + tmp_params.encoding = info->params.encoding; + tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; + tmp_params.addr_filter = info->params.addr_filter; + tmp_params.crc_type = info->params.crc_type; + tmp_params.preamble_length = info->params.preamble_length; + tmp_params.preamble = info->params.preamble; + tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; + tmp_params.data_bits = info->params.data_bits; + tmp_params.stop_bits = info->params.stop_bits; + tmp_params.parity = info->params.parity; + if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + return 0; +} + +static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s set_params32\n", info->device_name)); + if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + + spin_lock(&info->lock); + if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) { + info->base_clock = tmp_params.clock_speed; + } else { + info->params.mode = tmp_params.mode; + info->params.loopback = tmp_params.loopback; + info->params.flags = tmp_params.flags; + info->params.encoding = tmp_params.encoding; + info->params.clock_speed = tmp_params.clock_speed; + info->params.addr_filter = tmp_params.addr_filter; + info->params.crc_type = tmp_params.crc_type; + info->params.preamble_length = tmp_params.preamble_length; + info->params.preamble = tmp_params.preamble; + info->params.data_rate = tmp_params.data_rate; + info->params.data_bits = tmp_params.data_bits; + info->params.stop_bits = tmp_params.stop_bits; + info->params.parity = tmp_params.parity; + } + spin_unlock(&info->lock); + + program_hw(info); + + return 0; +} + +static long slgt_compat_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct slgt_info *info = tty->driver_data; + int rc = -ENOIOCTLCMD; + + if (sanity_check(info, tty->name, "compat_ioctl")) + return -ENODEV; + DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); + + switch (cmd) { + + case MGSL_IOCSPARAMS32: + rc = set_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS32: + rc = get_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS: + case MGSL_IOCSPARAMS: + case MGSL_IOCGTXIDLE: + case MGSL_IOCGSTATS: + case MGSL_IOCWAITEVENT: + case MGSL_IOCGIF: + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: + case MGSL_IOCGXSYNC: + case MGSL_IOCGXCTRL: + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: + case MGSL_IOCTXABORT: + case TIOCMIWAIT: + case MGSL_IOCSIF: + case MGSL_IOCSXSYNC: + case MGSL_IOCSXCTRL: + rc = ioctl(tty, cmd, arg); + break; + } + + DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); + return rc; +} +#else +#define slgt_compat_ioctl NULL +#endif /* ifdef CONFIG_COMPAT */ + +/* + * proc fs support + */ +static inline void line_info(struct seq_file *m, struct slgt_info *info) +{ + char stat_buf[30]; + unsigned long flags; + + seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", + info->device_name, info->phys_reg_addr, + info->irq_level, info->max_frame_size); + + /* output current serial signal states */ + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (info->signals & SerialSignal_RTS) + strcat(stat_buf, "|RTS"); + if (info->signals & SerialSignal_CTS) + strcat(stat_buf, "|CTS"); + if (info->signals & SerialSignal_DTR) + strcat(stat_buf, "|DTR"); + if (info->signals & SerialSignal_DSR) + strcat(stat_buf, "|DSR"); + if (info->signals & SerialSignal_DCD) + strcat(stat_buf, "|CD"); + if (info->signals & SerialSignal_RI) + strcat(stat_buf, "|RI"); + + if (info->params.mode != MGSL_MODE_ASYNC) { + seq_printf(m, "\tHDLC txok:%d rxok:%d", + info->icount.txok, info->icount.rxok); + if (info->icount.txunder) + seq_printf(m, " txunder:%d", info->icount.txunder); + if (info->icount.txabort) + seq_printf(m, " txabort:%d", info->icount.txabort); + if (info->icount.rxshort) + seq_printf(m, " rxshort:%d", info->icount.rxshort); + if (info->icount.rxlong) + seq_printf(m, " rxlong:%d", info->icount.rxlong); + if (info->icount.rxover) + seq_printf(m, " rxover:%d", info->icount.rxover); + if (info->icount.rxcrc) + seq_printf(m, " rxcrc:%d", info->icount.rxcrc); + } else { + seq_printf(m, "\tASYNC tx:%d rx:%d", + info->icount.tx, info->icount.rx); + if (info->icount.frame) + seq_printf(m, " fe:%d", info->icount.frame); + if (info->icount.parity) + seq_printf(m, " pe:%d", info->icount.parity); + if (info->icount.brk) + seq_printf(m, " brk:%d", info->icount.brk); + if (info->icount.overrun) + seq_printf(m, " oe:%d", info->icount.overrun); + } + + /* Append serial signal status to end */ + seq_printf(m, " %s\n", stat_buf+1); + + seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + info->tx_active,info->bh_requested,info->bh_running, + info->pending_bh); +} + +/* Called to print information about devices + */ +static int synclink_gt_proc_show(struct seq_file *m, void *v) +{ + struct slgt_info *info; + + seq_puts(m, "synclink_gt driver\n"); + + info = slgt_device_list; + while( info ) { + line_info(m, info); + info = info->next_device; + } + return 0; +} + +static int synclink_gt_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, synclink_gt_proc_show, NULL); +} + +static const struct file_operations synclink_gt_proc_fops = { + .owner = THIS_MODULE, + .open = synclink_gt_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* + * return count of bytes in transmit buffer + */ +static int chars_in_buffer(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + int count; + if (sanity_check(info, tty->name, "chars_in_buffer")) + return 0; + count = tbuf_bytes(info); + DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count)); + return count; +} + +/* + * signal remote device to throttle send data (our receive data) + */ +static void throttle(struct tty_struct * tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "throttle")) + return; + DBGINFO(("%s throttle\n", info->device_name)); + if (I_IXOFF(tty)) + send_xchar(tty, STOP_CHAR(tty)); + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock,flags); + info->signals &= ~SerialSignal_RTS; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +/* + * signal remote device to stop throttling send data (our receive data) + */ +static void unthrottle(struct tty_struct * tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "unthrottle")) + return; + DBGINFO(("%s unthrottle\n", info->device_name)); + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + send_xchar(tty, START_CHAR(tty)); + } + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock,flags); + info->signals |= SerialSignal_RTS; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +/* + * set or clear transmit break condition + * break_state -1=set break condition, 0=clear + */ +static int set_break(struct tty_struct *tty, int break_state) +{ + struct slgt_info *info = tty->driver_data; + unsigned short value; + unsigned long flags; + + if (sanity_check(info, tty->name, "set_break")) + return -EINVAL; + DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); + + spin_lock_irqsave(&info->lock,flags); + value = rd_reg16(info, TCR); + if (break_state == -1) + value |= BIT6; + else + value &= ~BIT6; + wr_reg16(info, TCR, value); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +#if SYNCLINK_GENERIC_HDLC + +/** + * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) + * set encoding and frame check sequence (FCS) options + * + * dev pointer to network device structure + * encoding serial encoding setting + * parity FCS setting + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + struct slgt_info *info = dev_to_port(dev); + unsigned char new_encoding; + unsigned short new_crctype; + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + DBGINFO(("%s hdlcdev_attach\n", info->device_name)); + + switch (encoding) + { + case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; + case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; + case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; + case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; + case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; + default: return -EINVAL; + } + + switch (parity) + { + case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; + case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; + case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; + default: return -EINVAL; + } + + info->params.encoding = new_encoding; + info->params.crc_type = new_crctype; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + program_hw(info); + + return 0; +} + +/** + * called by generic HDLC layer to send frame + * + * skb socket buffer containing HDLC frame + * dev pointer to network device structure + */ +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct slgt_info *info = dev_to_port(dev); + unsigned long flags; + + DBGINFO(("%s hdlc_xmit\n", dev->name)); + + if (!skb->len) + return NETDEV_TX_OK; + + /* stop sending until this frame completes */ + netif_stop_queue(dev); + + /* update network statistics */ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* save start time for transmit timeout detection */ + dev->trans_start = jiffies; + + spin_lock_irqsave(&info->lock, flags); + tx_load(info, skb->data, skb->len); + spin_unlock_irqrestore(&info->lock, flags); + + /* done with socket buffer, so free it */ + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/** + * called by network layer when interface enabled + * claim resources and initialize hardware + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_open(struct net_device *dev) +{ + struct slgt_info *info = dev_to_port(dev); + int rc; + unsigned long flags; + + if (!try_module_get(THIS_MODULE)) + return -EBUSY; + + DBGINFO(("%s hdlcdev_open\n", dev->name)); + + /* generic HDLC layer open processing */ + if ((rc = hdlc_open(dev))) + return rc; + + /* arbitrate between network and tty opens */ + spin_lock_irqsave(&info->netlock, flags); + if (info->port.count != 0 || info->netcount != 0) { + DBGINFO(("%s hdlc_open busy\n", dev->name)); + spin_unlock_irqrestore(&info->netlock, flags); + return -EBUSY; + } + info->netcount=1; + spin_unlock_irqrestore(&info->netlock, flags); + + /* claim resources and init adapter */ + if ((rc = startup(info)) != 0) { + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + return rc; + } + + /* assert DTR and RTS, apply hardware settings */ + info->signals |= SerialSignal_RTS + SerialSignal_DTR; + program_hw(info); + + /* enable network layer transmit */ + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* inform generic HDLC layer of current DCD status */ + spin_lock_irqsave(&info->lock, flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock, flags); + if (info->signals & SerialSignal_DCD) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + +/** + * called by network layer when interface is disabled + * shutdown hardware and release resources + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_close(struct net_device *dev) +{ + struct slgt_info *info = dev_to_port(dev); + unsigned long flags; + + DBGINFO(("%s hdlcdev_close\n", dev->name)); + + netif_stop_queue(dev); + + /* shutdown adapter and release resources */ + shutdown(info); + + hdlc_close(dev); + + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + + module_put(THIS_MODULE); + return 0; +} + +/** + * called by network layer to process IOCTL call to network device + * + * dev pointer to network device structure + * ifr pointer to network interface request structure + * cmd IOCTL command code + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(sync_serial_settings); + sync_serial_settings new_line; + sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; + struct slgt_info *info = dev_to_port(dev); + unsigned int flags; + + DBGINFO(("%s hdlcdev_ioctl\n", dev->name)); + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + memset(&new_line, 0, sizeof(new_line)); + + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: /* return current sync_serial_settings */ + + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + + flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + + switch (flags){ + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; + case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; + default: new_line.clock_type = CLOCK_DEFAULT; + } + + new_line.clock_rate = info->params.clock_speed; + new_line.loopback = info->params.loopback ? 1:0; + + if (copy_to_user(line, &new_line, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + switch (new_line.clock_type) + { + case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; + case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; + case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; + case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; + case CLOCK_DEFAULT: flags = info->params.flags & + (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; + default: return -EINVAL; + } + + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; + + info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + info->params.flags |= flags; + + info->params.loopback = new_line.loopback; + + if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) + info->params.clock_speed = new_line.clock_rate; + else + info->params.clock_speed = 0; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + program_hw(info); + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +/** + * called by network layer when transmit timeout is detected + * + * dev pointer to network device structure + */ +static void hdlcdev_tx_timeout(struct net_device *dev) +{ + struct slgt_info *info = dev_to_port(dev); + unsigned long flags; + + DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + spin_lock_irqsave(&info->lock,flags); + tx_stop(info); + spin_unlock_irqrestore(&info->lock,flags); + + netif_wake_queue(dev); +} + +/** + * called by device driver when transmit completes + * reenable network layer transmit if stopped + * + * info pointer to device instance information + */ +static void hdlcdev_tx_done(struct slgt_info *info) +{ + if (netif_queue_stopped(info->netdev)) + netif_wake_queue(info->netdev); +} + +/** + * called by device driver when frame received + * pass frame to network layer + * + * info pointer to device instance information + * buf pointer to buffer contianing frame data + * size count of data bytes in buf + */ +static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) +{ + struct sk_buff *skb = dev_alloc_skb(size); + struct net_device *dev = info->netdev; + + DBGINFO(("%s hdlcdev_rx\n", dev->name)); + + if (skb == NULL) { + DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); + dev->stats.rx_dropped++; + return; + } + + memcpy(skb_put(skb, size), buf, size); + + skb->protocol = hdlc_type_trans(skb, dev); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; + + netif_rx(skb); +} + +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + +/** + * called by device driver when adding device instance + * do generic HDLC initialization + * + * info pointer to device instance information + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_init(struct slgt_info *info) +{ + int rc; + struct net_device *dev; + hdlc_device *hdlc; + + /* allocate and initialize network and HDLC layer objects */ + + if (!(dev = alloc_hdlcdev(info))) { + printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); + return -ENOMEM; + } + + /* for network layer reporting purposes only */ + dev->mem_start = info->phys_reg_addr; + dev->mem_end = info->phys_reg_addr + SLGT_REG_SIZE - 1; + dev->irq = info->irq_level; + + /* network layer callbacks and settings */ + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; + dev->tx_queue_len = 50; + + /* generic HDLC layer callbacks and settings */ + hdlc = dev_to_hdlc(dev); + hdlc->attach = hdlcdev_attach; + hdlc->xmit = hdlcdev_xmit; + + /* register objects with HDLC layer */ + if ((rc = register_hdlc_device(dev))) { + printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); + free_netdev(dev); + return rc; + } + + info->netdev = dev; + return 0; +} + +/** + * called by device driver when removing device instance + * do generic HDLC cleanup + * + * info pointer to device instance information + */ +static void hdlcdev_exit(struct slgt_info *info) +{ + unregister_hdlc_device(info->netdev); + free_netdev(info->netdev); + info->netdev = NULL; +} + +#endif /* ifdef CONFIG_HDLC */ + +/* + * get async data from rx DMA buffers + */ +static void rx_async(struct slgt_info *info) +{ + struct tty_struct *tty = info->port.tty; + struct mgsl_icount *icount = &info->icount; + unsigned int start, end; + unsigned char *p; + unsigned char status; + struct slgt_desc *bufs = info->rbufs; + int i, count; + int chars = 0; + int stat; + unsigned char ch; + + start = end = info->rbuf_current; + + while(desc_complete(bufs[end])) { + count = desc_count(bufs[end]) - info->rbuf_index; + p = bufs[end].buf + info->rbuf_index; + + DBGISR(("%s rx_async count=%d\n", info->device_name, count)); + DBGDATA(info, p, count, "rx"); + + for(i=0 ; i < count; i+=2, p+=2) { + ch = *p; + icount->rx++; + + stat = 0; + + if ((status = *(p+1) & (BIT1 + BIT0))) { + if (status & BIT1) + icount->parity++; + else if (status & BIT0) + icount->frame++; + /* discard char if tty control flags say so */ + if (status & info->ignore_status_mask) + continue; + if (status & BIT1) + stat = TTY_PARITY; + else if (status & BIT0) + stat = TTY_FRAME; + } + if (tty) { + tty_insert_flip_char(tty, ch, stat); + chars++; + } + } + + if (i < count) { + /* receive buffer not completed */ + info->rbuf_index += i; + mod_timer(&info->rx_timer, jiffies + 1); + break; + } + + info->rbuf_index = 0; + free_rbufs(info, end, end); + + if (++end == info->rbuf_count) + end = 0; + + /* if entire list searched then no frame available */ + if (end == start) + break; + } + + if (tty && chars) + tty_flip_buffer_push(tty); +} + +/* + * return next bottom half action to perform + */ +static int bh_action(struct slgt_info *info) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&info->lock,flags); + + if (info->pending_bh & BH_RECEIVE) { + info->pending_bh &= ~BH_RECEIVE; + rc = BH_RECEIVE; + } else if (info->pending_bh & BH_TRANSMIT) { + info->pending_bh &= ~BH_TRANSMIT; + rc = BH_TRANSMIT; + } else if (info->pending_bh & BH_STATUS) { + info->pending_bh &= ~BH_STATUS; + rc = BH_STATUS; + } else { + /* Mark BH routine as complete */ + info->bh_running = false; + info->bh_requested = false; + rc = 0; + } + + spin_unlock_irqrestore(&info->lock,flags); + + return rc; +} + +/* + * perform bottom half processing + */ +static void bh_handler(struct work_struct *work) +{ + struct slgt_info *info = container_of(work, struct slgt_info, task); + int action; + + if (!info) + return; + info->bh_running = true; + + while((action = bh_action(info))) { + switch (action) { + case BH_RECEIVE: + DBGBH(("%s bh receive\n", info->device_name)); + switch(info->params.mode) { + case MGSL_MODE_ASYNC: + rx_async(info); + break; + case MGSL_MODE_HDLC: + while(rx_get_frame(info)); + break; + case MGSL_MODE_RAW: + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: + while(rx_get_buf(info)); + break; + } + /* restart receiver if rx DMA buffers exhausted */ + if (info->rx_restart) + rx_start(info); + break; + case BH_TRANSMIT: + bh_transmit(info); + break; + case BH_STATUS: + DBGBH(("%s bh status\n", info->device_name)); + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + break; + default: + DBGBH(("%s unknown action\n", info->device_name)); + break; + } + } + DBGBH(("%s bh_handler exit\n", info->device_name)); +} + +static void bh_transmit(struct slgt_info *info) +{ + struct tty_struct *tty = info->port.tty; + + DBGBH(("%s bh_transmit\n", info->device_name)); + if (tty) + tty_wakeup(tty); +} + +static void dsr_change(struct slgt_info *info, unsigned short status) +{ + if (status & BIT3) { + info->signals |= SerialSignal_DSR; + info->input_signal_events.dsr_up++; + } else { + info->signals &= ~SerialSignal_DSR; + info->input_signal_events.dsr_down++; + } + DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); + if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { + slgt_irq_off(info, IRQ_DSR); + return; + } + info->icount.dsr++; + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + info->pending_bh |= BH_STATUS; +} + +static void cts_change(struct slgt_info *info, unsigned short status) +{ + if (status & BIT2) { + info->signals |= SerialSignal_CTS; + info->input_signal_events.cts_up++; + } else { + info->signals &= ~SerialSignal_CTS; + info->input_signal_events.cts_down++; + } + DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); + if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { + slgt_irq_off(info, IRQ_CTS); + return; + } + info->icount.cts++; + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + info->pending_bh |= BH_STATUS; + + if (info->port.flags & ASYNC_CTS_FLOW) { + if (info->port.tty) { + if (info->port.tty->hw_stopped) { + if (info->signals & SerialSignal_CTS) { + info->port.tty->hw_stopped = 0; + info->pending_bh |= BH_TRANSMIT; + return; + } + } else { + if (!(info->signals & SerialSignal_CTS)) + info->port.tty->hw_stopped = 1; + } + } + } +} + +static void dcd_change(struct slgt_info *info, unsigned short status) +{ + if (status & BIT1) { + info->signals |= SerialSignal_DCD; + info->input_signal_events.dcd_up++; + } else { + info->signals &= ~SerialSignal_DCD; + info->input_signal_events.dcd_down++; + } + DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); + if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { + slgt_irq_off(info, IRQ_DCD); + return; + } + info->icount.dcd++; +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) { + if (info->signals & SerialSignal_DCD) + netif_carrier_on(info->netdev); + else + netif_carrier_off(info->netdev); + } +#endif + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + info->pending_bh |= BH_STATUS; + + if (info->port.flags & ASYNC_CHECK_CD) { + if (info->signals & SerialSignal_DCD) + wake_up_interruptible(&info->port.open_wait); + else { + if (info->port.tty) + tty_hangup(info->port.tty); + } + } +} + +static void ri_change(struct slgt_info *info, unsigned short status) +{ + if (status & BIT0) { + info->signals |= SerialSignal_RI; + info->input_signal_events.ri_up++; + } else { + info->signals &= ~SerialSignal_RI; + info->input_signal_events.ri_down++; + } + DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); + if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { + slgt_irq_off(info, IRQ_RI); + return; + } + info->icount.rng++; + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + info->pending_bh |= BH_STATUS; +} + +static void isr_rxdata(struct slgt_info *info) +{ + unsigned int count = info->rbuf_fill_count; + unsigned int i = info->rbuf_fill_index; + unsigned short reg; + + while (rd_reg16(info, SSR) & IRQ_RXDATA) { + reg = rd_reg16(info, RDR); + DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg)); + if (desc_complete(info->rbufs[i])) { + /* all buffers full */ + rx_stop(info); + info->rx_restart = 1; + continue; + } + info->rbufs[i].buf[count++] = (unsigned char)reg; + /* async mode saves status byte to buffer for each data byte */ + if (info->params.mode == MGSL_MODE_ASYNC) + info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8); + if (count == info->rbuf_fill_level || (reg & BIT10)) { + /* buffer full or end of frame */ + set_desc_count(info->rbufs[i], count); + set_desc_status(info->rbufs[i], BIT15 | (reg >> 8)); + info->rbuf_fill_count = count = 0; + if (++i == info->rbuf_count) + i = 0; + info->pending_bh |= BH_RECEIVE; + } + } + + info->rbuf_fill_index = i; + info->rbuf_fill_count = count; +} + +static void isr_serial(struct slgt_info *info) +{ + unsigned short status = rd_reg16(info, SSR); + + DBGISR(("%s isr_serial status=%04X\n", info->device_name, status)); + + wr_reg16(info, SSR, status); /* clear pending */ + + info->irq_occurred = true; + + if (info->params.mode == MGSL_MODE_ASYNC) { + if (status & IRQ_TXIDLE) { + if (info->tx_active) + isr_txeom(info, status); + } + if (info->rx_pio && (status & IRQ_RXDATA)) + isr_rxdata(info); + if ((status & IRQ_RXBREAK) && (status & RXBREAK)) { + info->icount.brk++; + /* process break detection if tty control allows */ + if (info->port.tty) { + if (!(status & info->ignore_status_mask)) { + if (info->read_status_mask & MASK_BREAK) { + tty_insert_flip_char(info->port.tty, 0, TTY_BREAK); + if (info->port.flags & ASYNC_SAK) + do_SAK(info->port.tty); + } + } + } + } + } else { + if (status & (IRQ_TXIDLE + IRQ_TXUNDER)) + isr_txeom(info, status); + if (info->rx_pio && (status & IRQ_RXDATA)) + isr_rxdata(info); + if (status & IRQ_RXIDLE) { + if (status & RXIDLE) + info->icount.rxidle++; + else + info->icount.exithunt++; + wake_up_interruptible(&info->event_wait_q); + } + + if (status & IRQ_RXOVER) + rx_start(info); + } + + if (status & IRQ_DSR) + dsr_change(info, status); + if (status & IRQ_CTS) + cts_change(info, status); + if (status & IRQ_DCD) + dcd_change(info, status); + if (status & IRQ_RI) + ri_change(info, status); +} + +static void isr_rdma(struct slgt_info *info) +{ + unsigned int status = rd_reg32(info, RDCSR); + + DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status)); + + /* RDCSR (rx DMA control/status) + * + * 31..07 reserved + * 06 save status byte to DMA buffer + * 05 error + * 04 eol (end of list) + * 03 eob (end of buffer) + * 02 IRQ enable + * 01 reset + * 00 enable + */ + wr_reg32(info, RDCSR, status); /* clear pending */ + + if (status & (BIT5 + BIT4)) { + DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name)); + info->rx_restart = true; + } + info->pending_bh |= BH_RECEIVE; +} + +static void isr_tdma(struct slgt_info *info) +{ + unsigned int status = rd_reg32(info, TDCSR); + + DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status)); + + /* TDCSR (tx DMA control/status) + * + * 31..06 reserved + * 05 error + * 04 eol (end of list) + * 03 eob (end of buffer) + * 02 IRQ enable + * 01 reset + * 00 enable + */ + wr_reg32(info, TDCSR, status); /* clear pending */ + + if (status & (BIT5 + BIT4 + BIT3)) { + // another transmit buffer has completed + // run bottom half to get more send data from user + info->pending_bh |= BH_TRANSMIT; + } +} + +/* + * return true if there are unsent tx DMA buffers, otherwise false + * + * if there are unsent buffers then info->tbuf_start + * is set to index of first unsent buffer + */ +static bool unsent_tbufs(struct slgt_info *info) +{ + unsigned int i = info->tbuf_current; + bool rc = false; + + /* + * search backwards from last loaded buffer (precedes tbuf_current) + * for first unsent buffer (desc_count > 0) + */ + + do { + if (i) + i--; + else + i = info->tbuf_count - 1; + if (!desc_count(info->tbufs[i])) + break; + info->tbuf_start = i; + rc = true; + } while (i != info->tbuf_current); + + return rc; +} + +static void isr_txeom(struct slgt_info *info, unsigned short status) +{ + DBGISR(("%s txeom status=%04x\n", info->device_name, status)); + + slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); + tdma_reset(info); + if (status & IRQ_TXUNDER) { + unsigned short val = rd_reg16(info, TCR); + wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ + wr_reg16(info, TCR, val); /* clear reset bit */ + } + + if (info->tx_active) { + if (info->params.mode != MGSL_MODE_ASYNC) { + if (status & IRQ_TXUNDER) + info->icount.txunder++; + else if (status & IRQ_TXIDLE) + info->icount.txok++; + } + + if (unsent_tbufs(info)) { + tx_start(info); + update_tx_timer(info); + return; + } + info->tx_active = false; + + del_timer(&info->tx_timer); + + if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { + info->signals &= ~SerialSignal_RTS; + info->drop_rts_on_tx_done = false; + set_signals(info); + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + { + if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { + tx_stop(info); + return; + } + info->pending_bh |= BH_TRANSMIT; + } + } +} + +static void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state) +{ + struct cond_wait *w, *prev; + + /* wake processes waiting for specific transitions */ + for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) { + if (w->data & changed) { + w->data = state; + wake_up_interruptible(&w->q); + if (prev != NULL) + prev->next = w->next; + else + info->gpio_wait_q = w->next; + } else + prev = w; + } +} + +/* interrupt service routine + * + * irq interrupt number + * dev_id device ID supplied during interrupt registration + */ +static irqreturn_t slgt_interrupt(int dummy, void *dev_id) +{ + struct slgt_info *info = dev_id; + unsigned int gsr; + unsigned int i; + + DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); + + while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { + DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); + info->irq_occurred = true; + for(i=0; i < info->port_count ; i++) { + if (info->port_array[i] == NULL) + continue; + spin_lock(&info->port_array[i]->lock); + if (gsr & (BIT8 << i)) + isr_serial(info->port_array[i]); + if (gsr & (BIT16 << (i*2))) + isr_rdma(info->port_array[i]); + if (gsr & (BIT17 << (i*2))) + isr_tdma(info->port_array[i]); + spin_unlock(&info->port_array[i]->lock); + } + } + + if (info->gpio_present) { + unsigned int state; + unsigned int changed; + spin_lock(&info->lock); + while ((changed = rd_reg32(info, IOSR)) != 0) { + DBGISR(("%s iosr=%08x\n", info->device_name, changed)); + /* read latched state of GPIO signals */ + state = rd_reg32(info, IOVR); + /* clear pending GPIO interrupt bits */ + wr_reg32(info, IOSR, changed); + for (i=0 ; i < info->port_count ; i++) { + if (info->port_array[i] != NULL) + isr_gpio(info->port_array[i], changed, state); + } + } + spin_unlock(&info->lock); + } + + for(i=0; i < info->port_count ; i++) { + struct slgt_info *port = info->port_array[i]; + if (port == NULL) + continue; + spin_lock(&port->lock); + if ((port->port.count || port->netcount) && + port->pending_bh && !port->bh_running && + !port->bh_requested) { + DBGISR(("%s bh queued\n", port->device_name)); + schedule_work(&port->task); + port->bh_requested = true; + } + spin_unlock(&port->lock); + } + + DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); + return IRQ_HANDLED; +} + +static int startup(struct slgt_info *info) +{ + DBGINFO(("%s startup\n", info->device_name)); + + if (info->port.flags & ASYNC_INITIALIZED) + return 0; + + if (!info->tx_buf) { + info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); + if (!info->tx_buf) { + DBGERR(("%s can't allocate tx buffer\n", info->device_name)); + return -ENOMEM; + } + } + + info->pending_bh = 0; + + memset(&info->icount, 0, sizeof(info->icount)); + + /* program hardware for current parameters */ + change_params(info); + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags |= ASYNC_INITIALIZED; + + return 0; +} + +/* + * called by close() and hangup() to shutdown hardware + */ +static void shutdown(struct slgt_info *info) +{ + unsigned long flags; + + if (!(info->port.flags & ASYNC_INITIALIZED)) + return; + + DBGINFO(("%s shutdown\n", info->device_name)); + + /* clear status wait queue because status changes */ + /* can't happen after shutting down the hardware */ + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + del_timer_sync(&info->tx_timer); + del_timer_sync(&info->rx_timer); + + kfree(info->tx_buf); + info->tx_buf = NULL; + + spin_lock_irqsave(&info->lock,flags); + + tx_stop(info); + rx_stop(info); + + slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); + + if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + set_signals(info); + } + + flush_cond_wait(&info->gpio_wait_q); + + spin_unlock_irqrestore(&info->lock,flags); + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags &= ~ASYNC_INITIALIZED; +} + +static void program_hw(struct slgt_info *info) +{ + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + + rx_stop(info); + tx_stop(info); + + if (info->params.mode != MGSL_MODE_ASYNC || + info->netcount) + sync_mode(info); + else + async_mode(info); + + set_signals(info); + + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + + slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI); + get_signals(info); + + if (info->netcount || + (info->port.tty && info->port.tty->termios->c_cflag & CREAD)) + rx_start(info); + + spin_unlock_irqrestore(&info->lock,flags); +} + +/* + * reconfigure adapter based on new parameters + */ +static void change_params(struct slgt_info *info) +{ + unsigned cflag; + int bits_per_char; + + if (!info->port.tty || !info->port.tty->termios) + return; + DBGINFO(("%s change_params\n", info->device_name)); + + cflag = info->port.tty->termios->c_cflag; + + /* if B0 rate (hangup) specified then negate DTR and RTS */ + /* otherwise assert DTR and RTS */ + if (cflag & CBAUD) + info->signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + + /* byte size and parity */ + + switch (cflag & CSIZE) { + case CS5: info->params.data_bits = 5; break; + case CS6: info->params.data_bits = 6; break; + case CS7: info->params.data_bits = 7; break; + case CS8: info->params.data_bits = 8; break; + default: info->params.data_bits = 7; break; + } + + info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1; + + if (cflag & PARENB) + info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN; + else + info->params.parity = ASYNC_PARITY_NONE; + + /* calculate number of jiffies to transmit a full + * FIFO (32 bytes) at specified data rate + */ + bits_per_char = info->params.data_bits + + info->params.stop_bits + 1; + + info->params.data_rate = tty_get_baud_rate(info->port.tty); + + if (info->params.data_rate) { + info->timeout = (32*HZ*bits_per_char) / + info->params.data_rate; + } + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->port.flags |= ASYNC_CTS_FLOW; + else + info->port.flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->port.flags &= ~ASYNC_CHECK_CD; + else + info->port.flags |= ASYNC_CHECK_CD; + + /* process tty input control flags */ + + info->read_status_mask = IRQ_RXOVER; + if (I_INPCK(info->port.tty)) + info->read_status_mask |= MASK_PARITY | MASK_FRAMING; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask |= MASK_BREAK; + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; + if (I_IGNBRK(info->port.tty)) { + info->ignore_status_mask |= MASK_BREAK; + /* If ignoring parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= MASK_OVERRUN; + } + + program_hw(info); +} + +static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount) +{ + DBGINFO(("%s get_stats\n", info->device_name)); + if (!user_icount) { + memset(&info->icount, 0, sizeof(info->icount)); + } else { + if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount))) + return -EFAULT; + } + return 0; +} + +static int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params) +{ + DBGINFO(("%s get_params\n", info->device_name)); + if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS))) + return -EFAULT; + return 0; +} + +static int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params) +{ + unsigned long flags; + MGSL_PARAMS tmp_params; + + DBGINFO(("%s set_params\n", info->device_name)); + if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS))) + return -EFAULT; + + spin_lock_irqsave(&info->lock, flags); + if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) + info->base_clock = tmp_params.clock_speed; + else + memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS)); + spin_unlock_irqrestore(&info->lock, flags); + + program_hw(info); + + return 0; +} + +static int get_txidle(struct slgt_info *info, int __user *idle_mode) +{ + DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode)); + if (put_user(info->idle_mode, idle_mode)) + return -EFAULT; + return 0; +} + +static int set_txidle(struct slgt_info *info, int idle_mode) +{ + unsigned long flags; + DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode)); + spin_lock_irqsave(&info->lock,flags); + info->idle_mode = idle_mode; + if (info->params.mode != MGSL_MODE_ASYNC) + tx_set_idle(info); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int tx_enable(struct slgt_info *info, int enable) +{ + unsigned long flags; + DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable)); + spin_lock_irqsave(&info->lock,flags); + if (enable) { + if (!info->tx_enabled) + tx_start(info); + } else { + if (info->tx_enabled) + tx_stop(info); + } + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +/* + * abort transmit HDLC frame + */ +static int tx_abort(struct slgt_info *info) +{ + unsigned long flags; + DBGINFO(("%s tx_abort\n", info->device_name)); + spin_lock_irqsave(&info->lock,flags); + tdma_reset(info); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int rx_enable(struct slgt_info *info, int enable) +{ + unsigned long flags; + unsigned int rbuf_fill_level; + DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); + spin_lock_irqsave(&info->lock,flags); + /* + * enable[31..16] = receive DMA buffer fill level + * 0 = noop (leave fill level unchanged) + * fill level must be multiple of 4 and <= buffer size + */ + rbuf_fill_level = ((unsigned int)enable) >> 16; + if (rbuf_fill_level) { + if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { + spin_unlock_irqrestore(&info->lock, flags); + return -EINVAL; + } + info->rbuf_fill_level = rbuf_fill_level; + if (rbuf_fill_level < 128) + info->rx_pio = 1; /* PIO mode */ + else + info->rx_pio = 0; /* DMA mode */ + rx_stop(info); /* restart receiver to use new fill level */ + } + + /* + * enable[1..0] = receiver enable command + * 0 = disable + * 1 = enable + * 2 = enable or force hunt mode if already enabled + */ + enable &= 3; + if (enable) { + if (!info->rx_enabled) + rx_start(info); + else if (enable == 2) { + /* force hunt mode (write 1 to RCR[3]) */ + wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); + } + } else { + if (info->rx_enabled) + rx_stop(info); + } + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +/* + * wait for specified event to occur + */ +static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr) +{ + unsigned long flags; + int s; + int rc=0; + struct mgsl_icount cprev, cnow; + int events; + int mask; + struct _input_signal_events oldsigs, newsigs; + DECLARE_WAITQUEUE(wait, current); + + if (get_user(mask, mask_ptr)) + return -EFAULT; + + DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask)); + + spin_lock_irqsave(&info->lock,flags); + + /* return immediately if state matches requested events */ + get_signals(info); + s = info->signals; + + events = mask & + ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + + ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + + ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + + ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); + if (events) { + spin_unlock_irqrestore(&info->lock,flags); + goto exit; + } + + /* save current irq counts */ + cprev = info->icount; + oldsigs = info->input_signal_events; + + /* enable hunt and idle irqs if needed */ + if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { + unsigned short val = rd_reg16(info, SCR); + if (!(val & IRQ_RXIDLE)) + wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE)); + } + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&info->event_wait_q, &wait); + + spin_unlock_irqrestore(&info->lock,flags); + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get current irq counts */ + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + newsigs = info->input_signal_events; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + /* if no change, wait aborted for some reason */ + if (newsigs.dsr_up == oldsigs.dsr_up && + newsigs.dsr_down == oldsigs.dsr_down && + newsigs.dcd_up == oldsigs.dcd_up && + newsigs.dcd_down == oldsigs.dcd_down && + newsigs.cts_up == oldsigs.cts_up && + newsigs.cts_down == oldsigs.cts_down && + newsigs.ri_up == oldsigs.ri_up && + newsigs.ri_down == oldsigs.ri_down && + cnow.exithunt == cprev.exithunt && + cnow.rxidle == cprev.rxidle) { + rc = -EIO; + break; + } + + events = mask & + ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + + (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + + (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + + (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + + (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + + (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + + (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + + (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + + (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + + (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); + if (events) + break; + + cprev = cnow; + oldsigs = newsigs; + } + + remove_wait_queue(&info->event_wait_q, &wait); + set_current_state(TASK_RUNNING); + + + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + spin_lock_irqsave(&info->lock,flags); + if (!waitqueue_active(&info->event_wait_q)) { + /* disable enable exit hunt mode/idle rcvd IRQs */ + wr_reg16(info, SCR, + (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE)); + } + spin_unlock_irqrestore(&info->lock,flags); + } +exit: + if (rc == 0) + rc = put_user(events, mask_ptr); + return rc; +} + +static int get_interface(struct slgt_info *info, int __user *if_mode) +{ + DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode)); + if (put_user(info->if_mode, if_mode)) + return -EFAULT; + return 0; +} + +static int set_interface(struct slgt_info *info, int if_mode) +{ + unsigned long flags; + unsigned short val; + + DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode)); + spin_lock_irqsave(&info->lock,flags); + info->if_mode = if_mode; + + msc_set_vcr(info); + + /* TCR (tx control) 07 1=RTS driver control */ + val = rd_reg16(info, TCR); + if (info->if_mode & MGSL_INTERFACE_RTS_EN) + val |= BIT7; + else + val &= ~BIT7; + wr_reg16(info, TCR, val); + + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int get_xsync(struct slgt_info *info, int __user *xsync) +{ + DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); + if (put_user(info->xsync, xsync)) + return -EFAULT; + return 0; +} + +/* + * set extended sync pattern (1 to 4 bytes) for extended sync mode + * + * sync pattern is contained in least significant bytes of value + * most significant byte of sync pattern is oldest (1st sent/detected) + */ +static int set_xsync(struct slgt_info *info, int xsync) +{ + unsigned long flags; + + DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); + spin_lock_irqsave(&info->lock, flags); + info->xsync = xsync; + wr_reg32(info, XSR, xsync); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + +static int get_xctrl(struct slgt_info *info, int __user *xctrl) +{ + DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); + if (put_user(info->xctrl, xctrl)) + return -EFAULT; + return 0; +} + +/* + * set extended control options + * + * xctrl[31:19] reserved, must be zero + * xctrl[18:17] extended sync pattern length in bytes + * 00 = 1 byte in xsr[7:0] + * 01 = 2 bytes in xsr[15:0] + * 10 = 3 bytes in xsr[23:0] + * 11 = 4 bytes in xsr[31:0] + * xctrl[16] 1 = enable terminal count, 0=disabled + * xctrl[15:0] receive terminal count for fixed length packets + * value is count minus one (0 = 1 byte packet) + * when terminal count is reached, receiver + * automatically returns to hunt mode and receive + * FIFO contents are flushed to DMA buffers with + * end of frame (EOF) status + */ +static int set_xctrl(struct slgt_info *info, int xctrl) +{ + unsigned long flags; + + DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); + spin_lock_irqsave(&info->lock, flags); + info->xctrl = xctrl; + wr_reg32(info, XCR, xctrl); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + +/* + * set general purpose IO pin state and direction + * + * user_gpio fields: + * state each bit indicates a pin state + * smask set bit indicates pin state to set + * dir each bit indicates a pin direction (0=input, 1=output) + * dmask set bit indicates pin direction to set + */ +static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) +{ + unsigned long flags; + struct gpio_desc gpio; + __u32 data; + + if (!info->gpio_present) + return -EINVAL; + if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) + return -EFAULT; + DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n", + info->device_name, gpio.state, gpio.smask, + gpio.dir, gpio.dmask)); + + spin_lock_irqsave(&info->port_array[0]->lock, flags); + if (gpio.dmask) { + data = rd_reg32(info, IODR); + data |= gpio.dmask & gpio.dir; + data &= ~(gpio.dmask & ~gpio.dir); + wr_reg32(info, IODR, data); + } + if (gpio.smask) { + data = rd_reg32(info, IOVR); + data |= gpio.smask & gpio.state; + data &= ~(gpio.smask & ~gpio.state); + wr_reg32(info, IOVR, data); + } + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); + + return 0; +} + +/* + * get general purpose IO pin state and direction + */ +static int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) +{ + struct gpio_desc gpio; + if (!info->gpio_present) + return -EINVAL; + gpio.state = rd_reg32(info, IOVR); + gpio.smask = 0xffffffff; + gpio.dir = rd_reg32(info, IODR); + gpio.dmask = 0xffffffff; + if (copy_to_user(user_gpio, &gpio, sizeof(gpio))) + return -EFAULT; + DBGINFO(("%s get_gpio state=%08x dir=%08x\n", + info->device_name, gpio.state, gpio.dir)); + return 0; +} + +/* + * conditional wait facility + */ +static void init_cond_wait(struct cond_wait *w, unsigned int data) +{ + init_waitqueue_head(&w->q); + init_waitqueue_entry(&w->wait, current); + w->data = data; +} + +static void add_cond_wait(struct cond_wait **head, struct cond_wait *w) +{ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&w->q, &w->wait); + w->next = *head; + *head = w; +} + +static void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw) +{ + struct cond_wait *w, *prev; + remove_wait_queue(&cw->q, &cw->wait); + set_current_state(TASK_RUNNING); + for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) { + if (w == cw) { + if (prev != NULL) + prev->next = w->next; + else + *head = w->next; + break; + } + } +} + +static void flush_cond_wait(struct cond_wait **head) +{ + while (*head != NULL) { + wake_up_interruptible(&(*head)->q); + *head = (*head)->next; + } +} + +/* + * wait for general purpose I/O pin(s) to enter specified state + * + * user_gpio fields: + * state - bit indicates target pin state + * smask - set bit indicates watched pin + * + * The wait ends when at least one watched pin enters the specified + * state. When 0 (no error) is returned, user_gpio->state is set to the + * state of all GPIO pins when the wait ends. + * + * Note: Each pin may be a dedicated input, dedicated output, or + * configurable input/output. The number and configuration of pins + * varies with the specific adapter model. Only input pins (dedicated + * or configured) can be monitored with this function. + */ +static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) +{ + unsigned long flags; + int rc = 0; + struct gpio_desc gpio; + struct cond_wait wait; + u32 state; + + if (!info->gpio_present) + return -EINVAL; + if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) + return -EFAULT; + DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n", + info->device_name, gpio.state, gpio.smask)); + /* ignore output pins identified by set IODR bit */ + if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0) + return -EINVAL; + init_cond_wait(&wait, gpio.smask); + + spin_lock_irqsave(&info->port_array[0]->lock, flags); + /* enable interrupts for watched pins */ + wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); + /* get current pin states */ + state = rd_reg32(info, IOVR); + + if (gpio.smask & ~(state ^ gpio.state)) { + /* already in target state */ + gpio.state = state; + } else { + /* wait for target state */ + add_cond_wait(&info->gpio_wait_q, &wait); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); + schedule(); + if (signal_pending(current)) + rc = -ERESTARTSYS; + else + gpio.state = wait.data; + spin_lock_irqsave(&info->port_array[0]->lock, flags); + remove_cond_wait(&info->gpio_wait_q, &wait); + } + + /* disable all GPIO interrupts if no waiting processes */ + if (info->gpio_wait_q == NULL) + wr_reg32(info, IOER, 0); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); + + if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) + rc = -EFAULT; + return rc; +} + +static int modem_input_wait(struct slgt_info *info,int arg) +{ + unsigned long flags; + int rc; + struct mgsl_icount cprev, cnow; + DECLARE_WAITQUEUE(wait, current); + + /* save current irq counts */ + spin_lock_irqsave(&info->lock,flags); + cprev = info->icount; + add_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get new irq counts */ + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + /* if no change, wait aborted for some reason */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; + break; + } + + /* check for change in caller specified modem input */ + if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || + (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || + (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || + (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { + rc = 0; + break; + } + + cprev = cnow; + } + remove_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_RUNNING); + return rc; +} + +/* + * return state of serial control and status signals + */ +static int tiocmget(struct tty_struct *tty) +{ + struct slgt_info *info = tty->driver_data; + unsigned int result; + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) + + ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) + + ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) + + ((info->signals & SerialSignal_RI) ? TIOCM_RNG:0) + + ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) + + ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0); + + DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result)); + return result; +} + +/* + * set modem control signals (DTR/RTS) + * + * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit + * TIOCMSET = set/clear signal values + * value bit mask for command + */ +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct slgt_info *info = tty->driver_data; + unsigned long flags; + + DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear)); + + if (set & TIOCM_RTS) + info->signals |= SerialSignal_RTS; + if (set & TIOCM_DTR) + info->signals |= SerialSignal_DTR; + if (clear & TIOCM_RTS) + info->signals &= ~SerialSignal_RTS; + if (clear & TIOCM_DTR) + info->signals &= ~SerialSignal_DTR; + + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int carrier_raised(struct tty_port *port) +{ + unsigned long flags; + struct slgt_info *info = container_of(port, struct slgt_info, port); + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + return (info->signals & SerialSignal_DCD) ? 1 : 0; +} + +static void dtr_rts(struct tty_port *port, int on) +{ + unsigned long flags; + struct slgt_info *info = container_of(port, struct slgt_info, port); + + spin_lock_irqsave(&info->lock,flags); + if (on) + info->signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); +} + + +/* + * block current process until the device is ready to open + */ +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct slgt_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + bool do_clocal = false; + bool extra_count = false; + unsigned long flags; + int cd; + struct tty_port *port = &info->port; + + DBGINFO(("%s block_til_ready\n", tty->driver->name)); + + if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + /* nonblock mode is set or port is not enabled */ + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = true; + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + spin_lock_irqsave(&info->lock, flags); + if (!tty_hung_up_p(filp)) { + extra_count = true; + port->count--; + } + spin_unlock_irqrestore(&info->lock, flags); + port->blocked_open++; + + while (1) { + if ((tty->termios->c_cflag & CBAUD)) + tty_port_raise_dtr_rts(port); + + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + cd = tty_port_carrier_raised(port); + + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd )) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); + tty_unlock(); + schedule(); + tty_lock(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + + if (extra_count) + port->count++; + port->blocked_open--; + + if (!retval) + port->flags |= ASYNC_NORMAL_ACTIVE; + + DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); + return retval; +} + +static int alloc_tmp_rbuf(struct slgt_info *info) +{ + info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); + if (info->tmp_rbuf == NULL) + return -ENOMEM; + return 0; +} + +static void free_tmp_rbuf(struct slgt_info *info) +{ + kfree(info->tmp_rbuf); + info->tmp_rbuf = NULL; +} + +/* + * allocate DMA descriptor lists. + */ +static int alloc_desc(struct slgt_info *info) +{ + unsigned int i; + unsigned int pbufs; + + /* allocate memory to hold descriptor lists */ + info->bufs = pci_alloc_consistent(info->pdev, DESC_LIST_SIZE, &info->bufs_dma_addr); + if (info->bufs == NULL) + return -ENOMEM; + + memset(info->bufs, 0, DESC_LIST_SIZE); + + info->rbufs = (struct slgt_desc*)info->bufs; + info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count; + + pbufs = (unsigned int)info->bufs_dma_addr; + + /* + * Build circular lists of descriptors + */ + + for (i=0; i < info->rbuf_count; i++) { + /* physical address of this descriptor */ + info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc)); + + /* physical address of next descriptor */ + if (i == info->rbuf_count - 1) + info->rbufs[i].next = cpu_to_le32(pbufs); + else + info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc))); + set_desc_count(info->rbufs[i], DMABUFSIZE); + } + + for (i=0; i < info->tbuf_count; i++) { + /* physical address of this descriptor */ + info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc)); + + /* physical address of next descriptor */ + if (i == info->tbuf_count - 1) + info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc)); + else + info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc))); + } + + return 0; +} + +static void free_desc(struct slgt_info *info) +{ + if (info->bufs != NULL) { + pci_free_consistent(info->pdev, DESC_LIST_SIZE, info->bufs, info->bufs_dma_addr); + info->bufs = NULL; + info->rbufs = NULL; + info->tbufs = NULL; + } +} + +static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) +{ + int i; + for (i=0; i < count; i++) { + if ((bufs[i].buf = pci_alloc_consistent(info->pdev, DMABUFSIZE, &bufs[i].buf_dma_addr)) == NULL) + return -ENOMEM; + bufs[i].pbuf = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr); + } + return 0; +} + +static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) +{ + int i; + for (i=0; i < count; i++) { + if (bufs[i].buf == NULL) + continue; + pci_free_consistent(info->pdev, DMABUFSIZE, bufs[i].buf, bufs[i].buf_dma_addr); + bufs[i].buf = NULL; + } +} + +static int alloc_dma_bufs(struct slgt_info *info) +{ + info->rbuf_count = 32; + info->tbuf_count = 32; + + if (alloc_desc(info) < 0 || + alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 || + alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 || + alloc_tmp_rbuf(info) < 0) { + DBGERR(("%s DMA buffer alloc fail\n", info->device_name)); + return -ENOMEM; + } + reset_rbufs(info); + return 0; +} + +static void free_dma_bufs(struct slgt_info *info) +{ + if (info->bufs) { + free_bufs(info, info->rbufs, info->rbuf_count); + free_bufs(info, info->tbufs, info->tbuf_count); + free_desc(info); + } + free_tmp_rbuf(info); +} + +static int claim_resources(struct slgt_info *info) +{ + if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) { + DBGERR(("%s reg addr conflict, addr=%08X\n", + info->device_name, info->phys_reg_addr)); + info->init_error = DiagStatus_AddressConflict; + goto errout; + } + else + info->reg_addr_requested = true; + + info->reg_addr = ioremap_nocache(info->phys_reg_addr, SLGT_REG_SIZE); + if (!info->reg_addr) { + DBGERR(("%s cant map device registers, addr=%08X\n", + info->device_name, info->phys_reg_addr)); + info->init_error = DiagStatus_CantAssignPciResources; + goto errout; + } + return 0; + +errout: + release_resources(info); + return -ENODEV; +} + +static void release_resources(struct slgt_info *info) +{ + if (info->irq_requested) { + free_irq(info->irq_level, info); + info->irq_requested = false; + } + + if (info->reg_addr_requested) { + release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE); + info->reg_addr_requested = false; + } + + if (info->reg_addr) { + iounmap(info->reg_addr); + info->reg_addr = NULL; + } +} + +/* Add the specified device instance data structure to the + * global linked list of devices and increment the device count. + */ +static void add_device(struct slgt_info *info) +{ + char *devstr; + + info->next_device = NULL; + info->line = slgt_device_count; + sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line); + + if (info->line < MAX_DEVICES) { + if (maxframe[info->line]) + info->max_frame_size = maxframe[info->line]; + } + + slgt_device_count++; + + if (!slgt_device_list) + slgt_device_list = info; + else { + struct slgt_info *current_dev = slgt_device_list; + while(current_dev->next_device) + current_dev = current_dev->next_device; + current_dev->next_device = info; + } + + if (info->max_frame_size < 4096) + info->max_frame_size = 4096; + else if (info->max_frame_size > 65535) + info->max_frame_size = 65535; + + switch(info->pdev->device) { + case SYNCLINK_GT_DEVICE_ID: + devstr = "GT"; + break; + case SYNCLINK_GT2_DEVICE_ID: + devstr = "GT2"; + break; + case SYNCLINK_GT4_DEVICE_ID: + devstr = "GT4"; + break; + case SYNCLINK_AC_DEVICE_ID: + devstr = "AC"; + info->params.mode = MGSL_MODE_ASYNC; + break; + default: + devstr = "(unknown model)"; + } + printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n", + devstr, info->device_name, info->phys_reg_addr, + info->irq_level, info->max_frame_size); + +#if SYNCLINK_GENERIC_HDLC + hdlcdev_init(info); +#endif +} + +static const struct tty_port_operations slgt_port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, +}; + +/* + * allocate device instance structure, return NULL on failure + */ +static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) +{ + struct slgt_info *info; + + info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL); + + if (!info) { + DBGERR(("%s device alloc failed adapter=%d port=%d\n", + driver_name, adapter_num, port_num)); + } else { + tty_port_init(&info->port); + info->port.ops = &slgt_port_ops; + info->magic = MGSL_MAGIC; + INIT_WORK(&info->task, bh_handler); + info->max_frame_size = 4096; + info->base_clock = 14745600; + info->rbuf_fill_level = DMABUFSIZE; + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; + init_waitqueue_head(&info->status_event_wait_q); + init_waitqueue_head(&info->event_wait_q); + spin_lock_init(&info->netlock); + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + info->idle_mode = HDLC_TXIDLE_FLAGS; + info->adapter_num = adapter_num; + info->port_num = port_num; + + setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info); + setup_timer(&info->rx_timer, rx_timeout, (unsigned long)info); + + /* Copy configuration info to device instance data */ + info->pdev = pdev; + info->irq_level = pdev->irq; + info->phys_reg_addr = pci_resource_start(pdev,0); + + info->bus_type = MGSL_BUS_TYPE_PCI; + info->irq_flags = IRQF_SHARED; + + info->init_error = -1; /* assume error, set to 0 on successful init */ + } + + return info; +} + +static void device_init(int adapter_num, struct pci_dev *pdev) +{ + struct slgt_info *port_array[SLGT_MAX_PORTS]; + int i; + int port_count = 1; + + if (pdev->device == SYNCLINK_GT2_DEVICE_ID) + port_count = 2; + else if (pdev->device == SYNCLINK_GT4_DEVICE_ID) + port_count = 4; + + /* allocate device instances for all ports */ + for (i=0; i < port_count; ++i) { + port_array[i] = alloc_dev(adapter_num, i, pdev); + if (port_array[i] == NULL) { + for (--i; i >= 0; --i) + kfree(port_array[i]); + return; + } + } + + /* give copy of port_array to all ports and add to device list */ + for (i=0; i < port_count; ++i) { + memcpy(port_array[i]->port_array, port_array, sizeof(port_array)); + add_device(port_array[i]); + port_array[i]->port_count = port_count; + spin_lock_init(&port_array[i]->lock); + } + + /* Allocate and claim adapter resources */ + if (!claim_resources(port_array[0])) { + + alloc_dma_bufs(port_array[0]); + + /* copy resource information from first port to others */ + for (i = 1; i < port_count; ++i) { + port_array[i]->irq_level = port_array[0]->irq_level; + port_array[i]->reg_addr = port_array[0]->reg_addr; + alloc_dma_bufs(port_array[i]); + } + + if (request_irq(port_array[0]->irq_level, + slgt_interrupt, + port_array[0]->irq_flags, + port_array[0]->device_name, + port_array[0]) < 0) { + DBGERR(("%s request_irq failed IRQ=%d\n", + port_array[0]->device_name, + port_array[0]->irq_level)); + } else { + port_array[0]->irq_requested = true; + adapter_test(port_array[0]); + for (i=1 ; i < port_count ; i++) { + port_array[i]->init_error = port_array[0]->init_error; + port_array[i]->gpio_present = port_array[0]->gpio_present; + } + } + } + + for (i=0; i < port_count; ++i) + tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev)); +} + +static int __devinit init_one(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + if (pci_enable_device(dev)) { + printk("error enabling pci device %p\n", dev); + return -EIO; + } + pci_set_master(dev); + device_init(slgt_device_count, dev); + return 0; +} + +static void __devexit remove_one(struct pci_dev *dev) +{ +} + +static const struct tty_operations ops = { + .open = open, + .close = close, + .write = write, + .put_char = put_char, + .flush_chars = flush_chars, + .write_room = write_room, + .chars_in_buffer = chars_in_buffer, + .flush_buffer = flush_buffer, + .ioctl = ioctl, + .compat_ioctl = slgt_compat_ioctl, + .throttle = throttle, + .unthrottle = unthrottle, + .send_xchar = send_xchar, + .break_ctl = set_break, + .wait_until_sent = wait_until_sent, + .set_termios = set_termios, + .stop = tx_hold, + .start = tx_release, + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, + .get_icount = get_icount, + .proc_fops = &synclink_gt_proc_fops, +}; + +static void slgt_cleanup(void) +{ + int rc; + struct slgt_info *info; + struct slgt_info *tmp; + + printk(KERN_INFO "unload %s\n", driver_name); + + if (serial_driver) { + for (info=slgt_device_list ; info != NULL ; info=info->next_device) + tty_unregister_device(serial_driver, info->line); + if ((rc = tty_unregister_driver(serial_driver))) + DBGERR(("tty_unregister_driver error=%d\n", rc)); + put_tty_driver(serial_driver); + } + + /* reset devices */ + info = slgt_device_list; + while(info) { + reset_port(info); + info = info->next_device; + } + + /* release devices */ + info = slgt_device_list; + while(info) { +#if SYNCLINK_GENERIC_HDLC + hdlcdev_exit(info); +#endif + free_dma_bufs(info); + free_tmp_rbuf(info); + if (info->port_num == 0) + release_resources(info); + tmp = info; + info = info->next_device; + kfree(tmp); + } + + if (pci_registered) + pci_unregister_driver(&pci_driver); +} + +/* + * Driver initialization entry point. + */ +static int __init slgt_init(void) +{ + int rc; + + printk(KERN_INFO "%s\n", driver_name); + + serial_driver = alloc_tty_driver(MAX_DEVICES); + if (!serial_driver) { + printk("%s can't allocate tty driver\n", driver_name); + return -ENOMEM; + } + + /* Initialize the tty_driver structure */ + + serial_driver->owner = THIS_MODULE; + serial_driver->driver_name = tty_driver_name; + serial_driver->name = tty_dev_prefix; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + 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->init_termios.c_ispeed = 9600; + serial_driver->init_termios.c_ospeed = 9600; + serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(serial_driver, &ops); + if ((rc = tty_register_driver(serial_driver)) < 0) { + DBGERR(("%s can't register serial driver\n", driver_name)); + put_tty_driver(serial_driver); + serial_driver = NULL; + goto error; + } + + printk(KERN_INFO "%s, tty major#%d\n", + driver_name, serial_driver->major); + + slgt_device_count = 0; + if ((rc = pci_register_driver(&pci_driver)) < 0) { + printk("%s pci_register_driver error=%d\n", driver_name, rc); + goto error; + } + pci_registered = true; + + if (!slgt_device_list) + printk("%s no devices found\n",driver_name); + + return 0; + +error: + slgt_cleanup(); + return rc; +} + +static void __exit slgt_exit(void) +{ + slgt_cleanup(); +} + +module_init(slgt_init); +module_exit(slgt_exit); + +/* + * register access routines + */ + +#define CALC_REGADDR() \ + unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \ + if (addr >= 0x80) \ + reg_addr += (info->port_num) * 32; \ + else if (addr >= 0x40) \ + reg_addr += (info->port_num) * 16; + +static __u8 rd_reg8(struct slgt_info *info, unsigned int addr) +{ + CALC_REGADDR(); + return readb((void __iomem *)reg_addr); +} + +static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value) +{ + CALC_REGADDR(); + writeb(value, (void __iomem *)reg_addr); +} + +static __u16 rd_reg16(struct slgt_info *info, unsigned int addr) +{ + CALC_REGADDR(); + return readw((void __iomem *)reg_addr); +} + +static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value) +{ + CALC_REGADDR(); + writew(value, (void __iomem *)reg_addr); +} + +static __u32 rd_reg32(struct slgt_info *info, unsigned int addr) +{ + CALC_REGADDR(); + return readl((void __iomem *)reg_addr); +} + +static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value) +{ + CALC_REGADDR(); + writel(value, (void __iomem *)reg_addr); +} + +static void rdma_reset(struct slgt_info *info) +{ + unsigned int i; + + /* set reset bit */ + wr_reg32(info, RDCSR, BIT1); + + /* wait for enable bit cleared */ + for(i=0 ; i < 1000 ; i++) + if (!(rd_reg32(info, RDCSR) & BIT0)) + break; +} + +static void tdma_reset(struct slgt_info *info) +{ + unsigned int i; + + /* set reset bit */ + wr_reg32(info, TDCSR, BIT1); + + /* wait for enable bit cleared */ + for(i=0 ; i < 1000 ; i++) + if (!(rd_reg32(info, TDCSR) & BIT0)) + break; +} + +/* + * enable internal loopback + * TxCLK and RxCLK are generated from BRG + * and TxD is looped back to RxD internally. + */ +static void enable_loopback(struct slgt_info *info) +{ + /* SCR (serial control) BIT2=looopback enable */ + wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2)); + + if (info->params.mode != MGSL_MODE_ASYNC) { + /* CCR (clock control) + * 07..05 tx clock source (010 = BRG) + * 04..02 rx clock source (010 = BRG) + * 01 auxclk enable (0 = disable) + * 00 BRG enable (1 = enable) + * + * 0100 1001 + */ + wr_reg8(info, CCR, 0x49); + + /* set speed if available, otherwise use default */ + if (info->params.clock_speed) + set_rate(info, info->params.clock_speed); + else + set_rate(info, 3686400); + } +} + +/* + * set baud rate generator to specified rate + */ +static void set_rate(struct slgt_info *info, u32 rate) +{ + unsigned int div; + unsigned int osc = info->base_clock; + + /* div = osc/rate - 1 + * + * Round div up if osc/rate is not integer to + * force to next slowest rate. + */ + + if (rate) { + div = osc/rate; + if (!(osc % rate) && div) + div--; + wr_reg16(info, BDR, (unsigned short)div); + } +} + +static void rx_stop(struct slgt_info *info) +{ + unsigned short val; + + /* disable and reset receiver */ + val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ + wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ + wr_reg16(info, RCR, val); /* clear reset bit */ + + slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE); + + /* clear pending rx interrupts */ + wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER); + + rdma_reset(info); + + info->rx_enabled = false; + info->rx_restart = false; +} + +static void rx_start(struct slgt_info *info) +{ + unsigned short val; + + slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA); + + /* clear pending rx overrun IRQ */ + wr_reg16(info, SSR, IRQ_RXOVER); + + /* reset and disable receiver */ + val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ + wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ + wr_reg16(info, RCR, val); /* clear reset bit */ + + rdma_reset(info); + reset_rbufs(info); + + if (info->rx_pio) { + /* rx request when rx FIFO not empty */ + wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14)); + slgt_irq_on(info, IRQ_RXDATA); + if (info->params.mode == MGSL_MODE_ASYNC) { + /* enable saving of rx status */ + wr_reg32(info, RDCSR, BIT6); + } + } else { + /* rx request when rx FIFO half full */ + wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14)); + /* set 1st descriptor address */ + wr_reg32(info, RDDAR, info->rbufs[0].pdesc); + + if (info->params.mode != MGSL_MODE_ASYNC) { + /* enable rx DMA and DMA interrupt */ + wr_reg32(info, RDCSR, (BIT2 + BIT0)); + } else { + /* enable saving of rx status, rx DMA and DMA interrupt */ + wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0)); + } + } + + slgt_irq_on(info, IRQ_RXOVER); + + /* enable receiver */ + wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1)); + + info->rx_restart = false; + info->rx_enabled = true; +} + +static void tx_start(struct slgt_info *info) +{ + if (!info->tx_enabled) { + wr_reg16(info, TCR, + (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); + info->tx_enabled = true; + } + + if (desc_count(info->tbufs[info->tbuf_start])) { + info->drop_rts_on_tx_done = false; + + if (info->params.mode != MGSL_MODE_ASYNC) { + if (info->params.flags & HDLC_FLAG_AUTO_RTS) { + get_signals(info); + if (!(info->signals & SerialSignal_RTS)) { + info->signals |= SerialSignal_RTS; + set_signals(info); + info->drop_rts_on_tx_done = true; + } + } + + slgt_irq_off(info, IRQ_TXDATA); + slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); + /* clear tx idle and underrun status bits */ + wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); + } else { + slgt_irq_off(info, IRQ_TXDATA); + slgt_irq_on(info, IRQ_TXIDLE); + /* clear tx idle status bit */ + wr_reg16(info, SSR, IRQ_TXIDLE); + } + /* set 1st descriptor address and start DMA */ + wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); + wr_reg32(info, TDCSR, BIT2 + BIT0); + info->tx_active = true; + } +} + +static void tx_stop(struct slgt_info *info) +{ + unsigned short val; + + del_timer(&info->tx_timer); + + tdma_reset(info); + + /* reset and disable transmitter */ + val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ + wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ + + slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); + + /* clear tx idle and underrun status bit */ + wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); + + reset_tbufs(info); + + info->tx_enabled = false; + info->tx_active = false; +} + +static void reset_port(struct slgt_info *info) +{ + if (!info->reg_addr) + return; + + tx_stop(info); + rx_stop(info); + + info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + set_signals(info); + + slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); +} + +static void reset_adapter(struct slgt_info *info) +{ + int i; + for (i=0; i < info->port_count; ++i) { + if (info->port_array[i]) + reset_port(info->port_array[i]); + } +} + +static void async_mode(struct slgt_info *info) +{ + unsigned short val; + + slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); + tx_stop(info); + rx_stop(info); + + /* TCR (tx control) + * + * 15..13 mode, 010=async + * 12..10 encoding, 000=NRZ + * 09 parity enable + * 08 1=odd parity, 0=even parity + * 07 1=RTS driver control + * 06 1=break enable + * 05..04 character length + * 00=5 bits + * 01=6 bits + * 10=7 bits + * 11=8 bits + * 03 0=1 stop bit, 1=2 stop bits + * 02 reset + * 01 enable + * 00 auto-CTS enable + */ + val = 0x4000; + + if (info->if_mode & MGSL_INTERFACE_RTS_EN) + val |= BIT7; + + if (info->params.parity != ASYNC_PARITY_NONE) { + val |= BIT9; + if (info->params.parity == ASYNC_PARITY_ODD) + val |= BIT8; + } + + switch (info->params.data_bits) + { + case 6: val |= BIT4; break; + case 7: val |= BIT5; break; + case 8: val |= BIT5 + BIT4; break; + } + + if (info->params.stop_bits != 1) + val |= BIT3; + + if (info->params.flags & HDLC_FLAG_AUTO_CTS) + val |= BIT0; + + wr_reg16(info, TCR, val); + + /* RCR (rx control) + * + * 15..13 mode, 010=async + * 12..10 encoding, 000=NRZ + * 09 parity enable + * 08 1=odd parity, 0=even parity + * 07..06 reserved, must be 0 + * 05..04 character length + * 00=5 bits + * 01=6 bits + * 10=7 bits + * 11=8 bits + * 03 reserved, must be zero + * 02 reset + * 01 enable + * 00 auto-DCD enable + */ + val = 0x4000; + + if (info->params.parity != ASYNC_PARITY_NONE) { + val |= BIT9; + if (info->params.parity == ASYNC_PARITY_ODD) + val |= BIT8; + } + + switch (info->params.data_bits) + { + case 6: val |= BIT4; break; + case 7: val |= BIT5; break; + case 8: val |= BIT5 + BIT4; break; + } + + if (info->params.flags & HDLC_FLAG_AUTO_DCD) + val |= BIT0; + + wr_reg16(info, RCR, val); + + /* CCR (clock control) + * + * 07..05 011 = tx clock source is BRG/16 + * 04..02 010 = rx clock source is BRG + * 01 0 = auxclk disabled + * 00 1 = BRG enabled + * + * 0110 1001 + */ + wr_reg8(info, CCR, 0x69); + + msc_set_vcr(info); + + /* SCR (serial control) + * + * 15 1=tx req on FIFO half empty + * 14 1=rx req on FIFO half full + * 13 tx data IRQ enable + * 12 tx idle IRQ enable + * 11 rx break on IRQ enable + * 10 rx data IRQ enable + * 09 rx break off IRQ enable + * 08 overrun IRQ enable + * 07 DSR IRQ enable + * 06 CTS IRQ enable + * 05 DCD IRQ enable + * 04 RI IRQ enable + * 03 0=16x sampling, 1=8x sampling + * 02 1=txd->rxd internal loopback enable + * 01 reserved, must be zero + * 00 1=master IRQ enable + */ + val = BIT15 + BIT14 + BIT0; + /* JCR[8] : 1 = x8 async mode feature available */ + if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate && + ((info->base_clock < (info->params.data_rate * 16)) || + (info->base_clock % (info->params.data_rate * 16)))) { + /* use 8x sampling */ + val |= BIT3; + set_rate(info, info->params.data_rate * 8); + } else { + /* use 16x sampling */ + set_rate(info, info->params.data_rate * 16); + } + wr_reg16(info, SCR, val); + + slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER); + + if (info->params.loopback) + enable_loopback(info); +} + +static void sync_mode(struct slgt_info *info) +{ + unsigned short val; + + slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); + tx_stop(info); + rx_stop(info); + + /* TCR (tx control) + * + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous + * 12..10 encoding + * 09 CRC enable + * 08 CRC32 + * 07 1=RTS driver control + * 06 preamble enable + * 05..04 preamble length + * 03 share open/close flag + * 02 reset + * 01 enable + * 00 auto-CTS enable + */ + val = BIT2; + + switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; + case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; + case MGSL_MODE_BISYNC: val |= BIT15; break; + case MGSL_MODE_RAW: val |= BIT13; break; + } + if (info->if_mode & MGSL_INTERFACE_RTS_EN) + val |= BIT7; + + switch(info->params.encoding) + { + case HDLC_ENCODING_NRZB: val |= BIT10; break; + case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; + case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; + case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; + case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; + case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; + } + + switch (info->params.crc_type & HDLC_CRC_MASK) + { + case HDLC_CRC_16_CCITT: val |= BIT9; break; + case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; + } + + if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) + val |= BIT6; + + switch (info->params.preamble_length) + { + case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break; + case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break; + case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break; + } + + if (info->params.flags & HDLC_FLAG_AUTO_CTS) + val |= BIT0; + + wr_reg16(info, TCR, val); + + /* TPR (transmit preamble) */ + + switch (info->params.preamble) + { + case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; + case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; + case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break; + case HDLC_PREAMBLE_PATTERN_10: val = 0x55; break; + case HDLC_PREAMBLE_PATTERN_01: val = 0xaa; break; + default: val = 0x7e; break; + } + wr_reg8(info, TPR, (unsigned char)val); + + /* RCR (rx control) + * + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous + * 12..10 encoding + * 09 CRC enable + * 08 CRC32 + * 07..03 reserved, must be 0 + * 02 reset + * 01 enable + * 00 auto-DCD enable + */ + val = 0; + + switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; + case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; + case MGSL_MODE_BISYNC: val |= BIT15; break; + case MGSL_MODE_RAW: val |= BIT13; break; + } + + switch(info->params.encoding) + { + case HDLC_ENCODING_NRZB: val |= BIT10; break; + case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; + case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; + case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; + case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; + case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; + } + + switch (info->params.crc_type & HDLC_CRC_MASK) + { + case HDLC_CRC_16_CCITT: val |= BIT9; break; + case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; + } + + if (info->params.flags & HDLC_FLAG_AUTO_DCD) + val |= BIT0; + + wr_reg16(info, RCR, val); + + /* CCR (clock control) + * + * 07..05 tx clock source + * 04..02 rx clock source + * 01 auxclk enable + * 00 BRG enable + */ + val = 0; + + if (info->params.flags & HDLC_FLAG_TXC_BRG) + { + // when RxC source is DPLL, BRG generates 16X DPLL + // reference clock, so take TxC from BRG/16 to get + // transmit clock at actual data rate + if (info->params.flags & HDLC_FLAG_RXC_DPLL) + val |= BIT6 + BIT5; /* 011, txclk = BRG/16 */ + else + val |= BIT6; /* 010, txclk = BRG */ + } + else if (info->params.flags & HDLC_FLAG_TXC_DPLL) + val |= BIT7; /* 100, txclk = DPLL Input */ + else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN) + val |= BIT5; /* 001, txclk = RXC Input */ + + if (info->params.flags & HDLC_FLAG_RXC_BRG) + val |= BIT3; /* 010, rxclk = BRG */ + else if (info->params.flags & HDLC_FLAG_RXC_DPLL) + val |= BIT4; /* 100, rxclk = DPLL */ + else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN) + val |= BIT2; /* 001, rxclk = TXC Input */ + + if (info->params.clock_speed) + val |= BIT1 + BIT0; + + wr_reg8(info, CCR, (unsigned char)val); + + if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL)) + { + // program DPLL mode + switch(info->params.encoding) + { + case HDLC_ENCODING_BIPHASE_MARK: + case HDLC_ENCODING_BIPHASE_SPACE: + val = BIT7; break; + case HDLC_ENCODING_BIPHASE_LEVEL: + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: + val = BIT7 + BIT6; break; + default: val = BIT6; // NRZ encodings + } + wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val)); + + // DPLL requires a 16X reference clock from BRG + set_rate(info, info->params.clock_speed * 16); + } + else + set_rate(info, info->params.clock_speed); + + tx_set_idle(info); + + msc_set_vcr(info); + + /* SCR (serial control) + * + * 15 1=tx req on FIFO half empty + * 14 1=rx req on FIFO half full + * 13 tx data IRQ enable + * 12 tx idle IRQ enable + * 11 underrun IRQ enable + * 10 rx data IRQ enable + * 09 rx idle IRQ enable + * 08 overrun IRQ enable + * 07 DSR IRQ enable + * 06 CTS IRQ enable + * 05 DCD IRQ enable + * 04 RI IRQ enable + * 03 reserved, must be zero + * 02 1=txd->rxd internal loopback enable + * 01 reserved, must be zero + * 00 1=master IRQ enable + */ + wr_reg16(info, SCR, BIT15 + BIT14 + BIT0); + + if (info->params.loopback) + enable_loopback(info); +} + +/* + * set transmit idle mode + */ +static void tx_set_idle(struct slgt_info *info) +{ + unsigned char val; + unsigned short tcr; + + /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits + * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits + */ + tcr = rd_reg16(info, TCR); + if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) { + /* disable preamble, set idle size to 16 bits */ + tcr = (tcr & ~(BIT6 + BIT5)) | BIT4; + /* MSB of 16 bit idle specified in tx preamble register (TPR) */ + wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff)); + } else if (!(tcr & BIT6)) { + /* preamble is disabled, set idle size to 8 bits */ + tcr &= ~(BIT5 + BIT4); + } + wr_reg16(info, TCR, tcr); + + if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) { + /* LSB of custom tx idle specified in tx idle register */ + val = (unsigned char)(info->idle_mode & 0xff); + } else { + /* standard 8 bit idle patterns */ + switch(info->idle_mode) + { + case HDLC_TXIDLE_FLAGS: val = 0x7e; break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: + case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break; + case HDLC_TXIDLE_ZEROS: + case HDLC_TXIDLE_SPACE: val = 0x00; break; + default: val = 0xff; + } + } + + wr_reg8(info, TIR, val); +} + +/* + * get state of V24 status (input) signals + */ +static void get_signals(struct slgt_info *info) +{ + unsigned short status = rd_reg16(info, SSR); + + /* clear all serial signals except DTR and RTS */ + info->signals &= SerialSignal_DTR + SerialSignal_RTS; + + if (status & BIT3) + info->signals |= SerialSignal_DSR; + if (status & BIT2) + info->signals |= SerialSignal_CTS; + if (status & BIT1) + info->signals |= SerialSignal_DCD; + if (status & BIT0) + info->signals |= SerialSignal_RI; +} + +/* + * set V.24 Control Register based on current configuration + */ +static void msc_set_vcr(struct slgt_info *info) +{ + unsigned char val = 0; + + /* VCR (V.24 control) + * + * 07..04 serial IF select + * 03 DTR + * 02 RTS + * 01 LL + * 00 RL + */ + + switch(info->if_mode & MGSL_INTERFACE_MASK) + { + case MGSL_INTERFACE_RS232: + val |= BIT5; /* 0010 */ + break; + case MGSL_INTERFACE_V35: + val |= BIT7 + BIT6 + BIT5; /* 1110 */ + break; + case MGSL_INTERFACE_RS422: + val |= BIT6; /* 0100 */ + break; + } + + if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) + val |= BIT4; + if (info->signals & SerialSignal_DTR) + val |= BIT3; + if (info->signals & SerialSignal_RTS) + val |= BIT2; + if (info->if_mode & MGSL_INTERFACE_LL) + val |= BIT1; + if (info->if_mode & MGSL_INTERFACE_RL) + val |= BIT0; + wr_reg8(info, VCR, val); +} + +/* + * set state of V24 control (output) signals + */ +static void set_signals(struct slgt_info *info) +{ + unsigned char val = rd_reg8(info, VCR); + if (info->signals & SerialSignal_DTR) + val |= BIT3; + else + val &= ~BIT3; + if (info->signals & SerialSignal_RTS) + val |= BIT2; + else + val &= ~BIT2; + wr_reg8(info, VCR, val); +} + +/* + * free range of receive DMA buffers (i to last) + */ +static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last) +{ + int done = 0; + + while(!done) { + /* reset current buffer for reuse */ + info->rbufs[i].status = 0; + set_desc_count(info->rbufs[i], info->rbuf_fill_level); + if (i == last) + done = 1; + if (++i == info->rbuf_count) + i = 0; + } + info->rbuf_current = i; +} + +/* + * mark all receive DMA buffers as free + */ +static void reset_rbufs(struct slgt_info *info) +{ + free_rbufs(info, 0, info->rbuf_count - 1); + info->rbuf_fill_index = 0; + info->rbuf_fill_count = 0; +} + +/* + * pass receive HDLC frame to upper layer + * + * return true if frame available, otherwise false + */ +static bool rx_get_frame(struct slgt_info *info) +{ + unsigned int start, end; + unsigned short status; + unsigned int framesize = 0; + unsigned long flags; + struct tty_struct *tty = info->port.tty; + unsigned char addr_field = 0xff; + unsigned int crc_size = 0; + + switch (info->params.crc_type & HDLC_CRC_MASK) { + case HDLC_CRC_16_CCITT: crc_size = 2; break; + case HDLC_CRC_32_CCITT: crc_size = 4; break; + } + +check_again: + + framesize = 0; + addr_field = 0xff; + start = end = info->rbuf_current; + + for (;;) { + if (!desc_complete(info->rbufs[end])) + goto cleanup; + + if (framesize == 0 && info->params.addr_filter != 0xff) + addr_field = info->rbufs[end].buf[0]; + + framesize += desc_count(info->rbufs[end]); + + if (desc_eof(info->rbufs[end])) + break; + + if (++end == info->rbuf_count) + end = 0; + + if (end == info->rbuf_current) { + if (info->rx_enabled){ + spin_lock_irqsave(&info->lock,flags); + rx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } + goto cleanup; + } + } + + /* status + * + * 15 buffer complete + * 14..06 reserved + * 05..04 residue + * 02 eof (end of frame) + * 01 CRC error + * 00 abort + */ + status = desc_status(info->rbufs[end]); + + /* ignore CRC bit if not using CRC (bit is undefined) */ + if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE) + status &= ~BIT1; + + if (framesize == 0 || + (addr_field != 0xff && addr_field != info->params.addr_filter)) { + free_rbufs(info, start, end); + goto check_again; + } + + if (framesize < (2 + crc_size) || status & BIT0) { + info->icount.rxshort++; + framesize = 0; + } else if (status & BIT1) { + info->icount.rxcrc++; + if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) + framesize = 0; + } + +#if SYNCLINK_GENERIC_HDLC + if (framesize == 0) { + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; + } +#endif + + DBGBH(("%s rx frame status=%04X size=%d\n", + info->device_name, status, framesize)); + DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); + + if (framesize) { + if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { + framesize -= crc_size; + crc_size = 0; + } + + if (framesize > info->max_frame_size + crc_size) + info->icount.rxlong++; + else { + /* copy dma buffer(s) to contiguous temp buffer */ + int copy_count = framesize; + int i = start; + unsigned char *p = info->tmp_rbuf; + info->tmp_rbuf_count = framesize; + + info->icount.rxok++; + + while(copy_count) { + int partial_count = min_t(int, copy_count, info->rbuf_fill_level); + memcpy(p, info->rbufs[i].buf, partial_count); + p += partial_count; + copy_count -= partial_count; + if (++i == info->rbuf_count) + i = 0; + } + + if (info->params.crc_type & HDLC_CRC_RETURN_EX) { + *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK; + framesize++; + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_rx(info,info->tmp_rbuf, framesize); + else +#endif + ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize); + } + } + free_rbufs(info, start, end); + return true; + +cleanup: + return false; +} + +/* + * pass receive buffer (RAW synchronous mode) to tty layer + * return true if buffer available, otherwise false + */ +static bool rx_get_buf(struct slgt_info *info) +{ + unsigned int i = info->rbuf_current; + unsigned int count; + + if (!desc_complete(info->rbufs[i])) + return false; + count = desc_count(info->rbufs[i]); + switch(info->params.mode) { + case MGSL_MODE_MONOSYNC: + case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: + /* ignore residue in byte synchronous modes */ + if (desc_residue(info->rbufs[i])) + count--; + break; + } + DBGDATA(info, info->rbufs[i].buf, count, "rx"); + DBGINFO(("rx_get_buf size=%d\n", count)); + if (count) + ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, + info->flag_buf, count); + free_rbufs(info, i, i); + return true; +} + +static void reset_tbufs(struct slgt_info *info) +{ + unsigned int i; + info->tbuf_current = 0; + for (i=0 ; i < info->tbuf_count ; i++) { + info->tbufs[i].status = 0; + info->tbufs[i].count = 0; + } +} + +/* + * return number of free transmit DMA buffers + */ +static unsigned int free_tbuf_count(struct slgt_info *info) +{ + unsigned int count = 0; + unsigned int i = info->tbuf_current; + + do + { + if (desc_count(info->tbufs[i])) + break; /* buffer in use */ + ++count; + if (++i == info->tbuf_count) + i=0; + } while (i != info->tbuf_current); + + /* if tx DMA active, last zero count buffer is in use */ + if (count && (rd_reg32(info, TDCSR) & BIT0)) + --count; + + return count; +} + +/* + * return number of bytes in unsent transmit DMA buffers + * and the serial controller tx FIFO + */ +static unsigned int tbuf_bytes(struct slgt_info *info) +{ + unsigned int total_count = 0; + unsigned int i = info->tbuf_current; + unsigned int reg_value; + unsigned int count; + unsigned int active_buf_count = 0; + + /* + * Add descriptor counts for all tx DMA buffers. + * If count is zero (cleared by DMA controller after read), + * the buffer is complete or is actively being read from. + * + * Record buf_count of last buffer with zero count starting + * from current ring position. buf_count is mirror + * copy of count and is not cleared by serial controller. + * If DMA controller is active, that buffer is actively + * being read so add to total. + */ + do { + count = desc_count(info->tbufs[i]); + if (count) + total_count += count; + else if (!total_count) + active_buf_count = info->tbufs[i].buf_count; + if (++i == info->tbuf_count) + i = 0; + } while (i != info->tbuf_current); + + /* read tx DMA status register */ + reg_value = rd_reg32(info, TDCSR); + + /* if tx DMA active, last zero count buffer is in use */ + if (reg_value & BIT0) + total_count += active_buf_count; + + /* add tx FIFO count = reg_value[15..8] */ + total_count += (reg_value >> 8) & 0xff; + + /* if transmitter active add one byte for shift register */ + if (info->tx_active) + total_count++; + + return total_count; +} + +/* + * load data into transmit DMA buffer ring and start transmitter if needed + * return true if data accepted, otherwise false (buffers full) + */ +static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size) +{ + unsigned short count; + unsigned int i; + struct slgt_desc *d; + + /* check required buffer space */ + if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) + return false; + + DBGDATA(info, buf, size, "tx"); + + /* + * copy data to one or more DMA buffers in circular ring + * tbuf_start = first buffer for this data + * tbuf_current = next free buffer + * + * Copy all data before making data visible to DMA controller by + * setting descriptor count of the first buffer. + * This prevents an active DMA controller from reading the first DMA + * buffers of a frame and stopping before the final buffers are filled. + */ + + info->tbuf_start = i = info->tbuf_current; + + while (size) { + d = &info->tbufs[i]; + + count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); + memcpy(d->buf, buf, count); + + size -= count; + buf += count; + + /* + * set EOF bit for last buffer of HDLC frame or + * for every buffer in raw mode + */ + if ((!size && info->params.mode == MGSL_MODE_HDLC) || + info->params.mode == MGSL_MODE_RAW) + set_desc_eof(*d, 1); + else + set_desc_eof(*d, 0); + + /* set descriptor count for all but first buffer */ + if (i != info->tbuf_start) + set_desc_count(*d, count); + d->buf_count = count; + + if (++i == info->tbuf_count) + i = 0; + } + + info->tbuf_current = i; + + /* set first buffer count to make new data visible to DMA controller */ + d = &info->tbufs[info->tbuf_start]; + set_desc_count(*d, d->buf_count); + + /* start transmitter if needed and update transmit timeout */ + if (!info->tx_active) + tx_start(info); + update_tx_timer(info); + + return true; +} + +static int register_test(struct slgt_info *info) +{ + static unsigned short patterns[] = + {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696}; + static unsigned int count = ARRAY_SIZE(patterns); + unsigned int i; + int rc = 0; + + for (i=0 ; i < count ; i++) { + wr_reg16(info, TIR, patterns[i]); + wr_reg16(info, BDR, patterns[(i+1)%count]); + if ((rd_reg16(info, TIR) != patterns[i]) || + (rd_reg16(info, BDR) != patterns[(i+1)%count])) { + rc = -ENODEV; + break; + } + } + info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0; + info->init_error = rc ? 0 : DiagStatus_AddressFailure; + return rc; +} + +static int irq_test(struct slgt_info *info) +{ + unsigned long timeout; + unsigned long flags; + struct tty_struct *oldtty = info->port.tty; + u32 speed = info->params.data_rate; + + info->params.data_rate = 921600; + info->port.tty = NULL; + + spin_lock_irqsave(&info->lock, flags); + async_mode(info); + slgt_irq_on(info, IRQ_TXIDLE); + + /* enable transmitter */ + wr_reg16(info, TCR, + (unsigned short)(rd_reg16(info, TCR) | BIT1)); + + /* write one byte and wait for tx idle */ + wr_reg16(info, TDR, 0); + + /* assume failure */ + info->init_error = DiagStatus_IrqFailure; + info->irq_occurred = false; + + spin_unlock_irqrestore(&info->lock, flags); + + timeout=100; + while(timeout-- && !info->irq_occurred) + msleep_interruptible(10); + + spin_lock_irqsave(&info->lock,flags); + reset_port(info); + spin_unlock_irqrestore(&info->lock,flags); + + info->params.data_rate = speed; + info->port.tty = oldtty; + + info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure; + return info->irq_occurred ? 0 : -ENODEV; +} + +static int loopback_test_rx(struct slgt_info *info) +{ + unsigned char *src, *dest; + int count; + + if (desc_complete(info->rbufs[0])) { + count = desc_count(info->rbufs[0]); + src = info->rbufs[0].buf; + dest = info->tmp_rbuf; + + for( ; count ; count-=2, src+=2) { + /* src=data byte (src+1)=status byte */ + if (!(*(src+1) & (BIT9 + BIT8))) { + *dest = *src; + dest++; + info->tmp_rbuf_count++; + } + } + DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx"); + return 1; + } + return 0; +} + +static int loopback_test(struct slgt_info *info) +{ +#define TESTFRAMESIZE 20 + + unsigned long timeout; + u16 count = TESTFRAMESIZE; + unsigned char buf[TESTFRAMESIZE]; + int rc = -ENODEV; + unsigned long flags; + + struct tty_struct *oldtty = info->port.tty; + MGSL_PARAMS params; + + memcpy(¶ms, &info->params, sizeof(params)); + + info->params.mode = MGSL_MODE_ASYNC; + info->params.data_rate = 921600; + info->params.loopback = 1; + info->port.tty = NULL; + + /* build and send transmit frame */ + for (count = 0; count < TESTFRAMESIZE; ++count) + buf[count] = (unsigned char)count; + + info->tmp_rbuf_count = 0; + memset(info->tmp_rbuf, 0, TESTFRAMESIZE); + + /* program hardware for HDLC and enabled receiver */ + spin_lock_irqsave(&info->lock,flags); + async_mode(info); + rx_start(info); + tx_load(info, buf, count); + spin_unlock_irqrestore(&info->lock, flags); + + /* wait for receive complete */ + for (timeout = 100; timeout; --timeout) { + msleep_interruptible(10); + if (loopback_test_rx(info)) { + rc = 0; + break; + } + } + + /* verify received frame length and contents */ + if (!rc && (info->tmp_rbuf_count != count || + memcmp(buf, info->tmp_rbuf, count))) { + rc = -ENODEV; + } + + spin_lock_irqsave(&info->lock,flags); + reset_adapter(info); + spin_unlock_irqrestore(&info->lock,flags); + + memcpy(&info->params, ¶ms, sizeof(info->params)); + info->port.tty = oldtty; + + info->init_error = rc ? DiagStatus_DmaFailure : 0; + return rc; +} + +static int adapter_test(struct slgt_info *info) +{ + DBGINFO(("testing %s\n", info->device_name)); + if (register_test(info) < 0) { + printk("register test failure %s addr=%08X\n", + info->device_name, info->phys_reg_addr); + } else if (irq_test(info) < 0) { + printk("IRQ test failure %s IRQ=%d\n", + info->device_name, info->irq_level); + } else if (loopback_test(info) < 0) { + printk("loopback test failure %s\n", info->device_name); + } + return info->init_error; +} + +/* + * transmit timeout handler + */ +static void tx_timeout(unsigned long context) +{ + struct slgt_info *info = (struct slgt_info*)context; + unsigned long flags; + + DBGINFO(("%s tx_timeout\n", info->device_name)); + if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { + info->icount.txtimeout++; + } + spin_lock_irqsave(&info->lock,flags); + tx_stop(info); + spin_unlock_irqrestore(&info->lock,flags); + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + bh_transmit(info); +} + +/* + * receive buffer polling timer + */ +static void rx_timeout(unsigned long context) +{ + struct slgt_info *info = (struct slgt_info*)context; + unsigned long flags; + + DBGINFO(("%s rx_timeout\n", info->device_name)); + spin_lock_irqsave(&info->lock, flags); + info->pending_bh |= BH_RECEIVE; + spin_unlock_irqrestore(&info->lock, flags); + bh_handler(&info->task); +} + diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c new file mode 100644 index 000000000000..327343694473 --- /dev/null +++ b/drivers/tty/synclinkmp.c @@ -0,0 +1,5600 @@ +/* + * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $ + * + * Device driver for Microgate SyncLink Multiport + * high speed multiprotocol serial adapter. + * + * written by Paul Fulghum for Microgate Corporation + * paulkf@microgate.com + * + * Microgate and SyncLink are trademarks of Microgate Corporation + * + * Derived from serial.c written by Theodore Ts'o and Linus Torvalds + * This code is released under the GNU General Public License (GPL) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) +#if defined(__i386__) +# define BREAKPOINT() asm(" int $3"); +#else +# define BREAKPOINT() { } +#endif + +#define MAX_DEVICES 12 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE)) +#define SYNCLINK_GENERIC_HDLC 1 +#else +#define SYNCLINK_GENERIC_HDLC 0 +#endif + +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#include + +static MGSL_PARAMS default_params = { + MGSL_MODE_HDLC, /* unsigned long mode */ + 0, /* unsigned char loopback; */ + HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ + HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ + 0, /* unsigned long clock_speed; */ + 0xff, /* unsigned char addr_filter; */ + HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ + HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ + HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ + 9600, /* unsigned long data_rate; */ + 8, /* unsigned char data_bits; */ + 1, /* unsigned char stop_bits; */ + ASYNC_PARITY_NONE /* unsigned char parity; */ +}; + +/* size in bytes of DMA data buffers */ +#define SCABUFSIZE 1024 +#define SCA_MEM_SIZE 0x40000 +#define SCA_BASE_SIZE 512 +#define SCA_REG_SIZE 16 +#define SCA_MAX_PORTS 4 +#define SCAMAXDESC 128 + +#define BUFFERLISTSIZE 4096 + +/* SCA-I style DMA buffer descriptor */ +typedef struct _SCADESC +{ + u16 next; /* lower l6 bits of next descriptor addr */ + u16 buf_ptr; /* lower 16 bits of buffer addr */ + u8 buf_base; /* upper 8 bits of buffer addr */ + u8 pad1; + u16 length; /* length of buffer */ + u8 status; /* status of buffer */ + u8 pad2; +} SCADESC, *PSCADESC; + +typedef struct _SCADESC_EX +{ + /* device driver bookkeeping section */ + char *virt_addr; /* virtual address of data buffer */ + u16 phys_entry; /* lower 16-bits of physical address of this descriptor */ +} SCADESC_EX, *PSCADESC_EX; + +/* The queue of BH actions to be performed */ + +#define BH_RECEIVE 1 +#define BH_TRANSMIT 2 +#define BH_STATUS 4 + +#define IO_PIN_SHUTDOWN_LIMIT 100 + +struct _input_signal_events { + int ri_up; + int ri_down; + int dsr_up; + int dsr_down; + int dcd_up; + int dcd_down; + int cts_up; + int cts_down; +}; + +/* + * Device instance data structure + */ +typedef struct _synclinkmp_info { + void *if_ptr; /* General purpose pointer (used by SPPP) */ + int magic; + struct tty_port port; + int line; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + + struct mgsl_icount icount; + + int timeout; + int x_char; /* xon/xoff character */ + u16 read_status_mask1; /* break detection (SR1 indications) */ + u16 read_status_mask2; /* parity/framing/overun (SR2 indications) */ + unsigned char ignore_status_mask1; /* break detection (SR1 indications) */ + unsigned char ignore_status_mask2; /* parity/framing/overun (SR2 indications) */ + unsigned char *tx_buf; + int tx_put; + int tx_get; + int tx_count; + + wait_queue_head_t status_event_wait_q; + wait_queue_head_t event_wait_q; + struct timer_list tx_timer; /* HDLC transmit timeout timer */ + struct _synclinkmp_info *next_device; /* device list link */ + struct timer_list status_timer; /* input signal status check timer */ + + spinlock_t lock; /* spinlock for synchronizing with ISR */ + struct work_struct task; /* task structure for scheduling bh */ + + u32 max_frame_size; /* as set by device config */ + + u32 pending_bh; + + bool bh_running; /* Protection from multiple */ + int isr_overflow; + bool bh_requested; + + int dcd_chkcount; /* check counts to prevent */ + int cts_chkcount; /* too many IRQs if a signal */ + int dsr_chkcount; /* is floating */ + int ri_chkcount; + + char *buffer_list; /* virtual address of Rx & Tx buffer lists */ + unsigned long buffer_list_phys; + + unsigned int rx_buf_count; /* count of total allocated Rx buffers */ + SCADESC *rx_buf_list; /* list of receive buffer entries */ + SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */ + unsigned int current_rx_buf; + + unsigned int tx_buf_count; /* count of total allocated Tx buffers */ + SCADESC *tx_buf_list; /* list of transmit buffer entries */ + SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */ + unsigned int last_tx_buf; + + unsigned char *tmp_rx_buf; + unsigned int tmp_rx_buf_count; + + bool rx_enabled; + bool rx_overflow; + + bool tx_enabled; + bool tx_active; + u32 idle_mode; + + unsigned char ie0_value; + unsigned char ie1_value; + unsigned char ie2_value; + unsigned char ctrlreg_value; + unsigned char old_signals; + + char device_name[25]; /* device instance name */ + + int port_count; + int adapter_num; + int port_num; + + struct _synclinkmp_info *port_array[SCA_MAX_PORTS]; + + unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ + + unsigned int irq_level; /* interrupt level */ + unsigned long irq_flags; + bool irq_requested; /* true if IRQ requested */ + + MGSL_PARAMS params; /* communications parameters */ + + unsigned char serial_signals; /* current serial signal states */ + + bool irq_occurred; /* for diagnostics use */ + unsigned int init_error; /* Initialization startup error */ + + u32 last_mem_alloc; + unsigned char* memory_base; /* shared memory address (PCI only) */ + u32 phys_memory_base; + int shared_mem_requested; + + unsigned char* sca_base; /* HD64570 SCA Memory address */ + u32 phys_sca_base; + u32 sca_offset; + bool sca_base_requested; + + unsigned char* lcr_base; /* local config registers (PCI only) */ + u32 phys_lcr_base; + u32 lcr_offset; + int lcr_mem_requested; + + unsigned char* statctrl_base; /* status/control register memory */ + u32 phys_statctrl_base; + u32 statctrl_offset; + bool sca_statctrl_requested; + + u32 misc_ctrl_value; + char flag_buf[MAX_ASYNC_BUFFER_SIZE]; + char char_buf[MAX_ASYNC_BUFFER_SIZE]; + bool drop_rts_on_tx_done; + + struct _input_signal_events input_signal_events; + + /* SPPP/Cisco HDLC device parts */ + int netcount; + spinlock_t netlock; + +#if SYNCLINK_GENERIC_HDLC + struct net_device *netdev; +#endif + +} SLMP_INFO; + +#define MGSL_MAGIC 0x5401 + +/* + * define serial signal status change macros + */ +#define MISCSTATUS_DCD_LATCHED (SerialSignal_DCD<<8) /* indicates change in DCD */ +#define MISCSTATUS_RI_LATCHED (SerialSignal_RI<<8) /* indicates change in RI */ +#define MISCSTATUS_CTS_LATCHED (SerialSignal_CTS<<8) /* indicates change in CTS */ +#define MISCSTATUS_DSR_LATCHED (SerialSignal_DSR<<8) /* change in DSR */ + +/* Common Register macros */ +#define LPR 0x00 +#define PABR0 0x02 +#define PABR1 0x03 +#define WCRL 0x04 +#define WCRM 0x05 +#define WCRH 0x06 +#define DPCR 0x08 +#define DMER 0x09 +#define ISR0 0x10 +#define ISR1 0x11 +#define ISR2 0x12 +#define IER0 0x14 +#define IER1 0x15 +#define IER2 0x16 +#define ITCR 0x18 +#define INTVR 0x1a +#define IMVR 0x1c + +/* MSCI Register macros */ +#define TRB 0x20 +#define TRBL 0x20 +#define TRBH 0x21 +#define SR0 0x22 +#define SR1 0x23 +#define SR2 0x24 +#define SR3 0x25 +#define FST 0x26 +#define IE0 0x28 +#define IE1 0x29 +#define IE2 0x2a +#define FIE 0x2b +#define CMD 0x2c +#define MD0 0x2e +#define MD1 0x2f +#define MD2 0x30 +#define CTL 0x31 +#define SA0 0x32 +#define SA1 0x33 +#define IDL 0x34 +#define TMC 0x35 +#define RXS 0x36 +#define TXS 0x37 +#define TRC0 0x38 +#define TRC1 0x39 +#define RRC 0x3a +#define CST0 0x3c +#define CST1 0x3d + +/* Timer Register Macros */ +#define TCNT 0x60 +#define TCNTL 0x60 +#define TCNTH 0x61 +#define TCONR 0x62 +#define TCONRL 0x62 +#define TCONRH 0x63 +#define TMCS 0x64 +#define TEPR 0x65 + +/* DMA Controller Register macros */ +#define DARL 0x80 +#define DARH 0x81 +#define DARB 0x82 +#define BAR 0x80 +#define BARL 0x80 +#define BARH 0x81 +#define BARB 0x82 +#define SAR 0x84 +#define SARL 0x84 +#define SARH 0x85 +#define SARB 0x86 +#define CPB 0x86 +#define CDA 0x88 +#define CDAL 0x88 +#define CDAH 0x89 +#define EDA 0x8a +#define EDAL 0x8a +#define EDAH 0x8b +#define BFL 0x8c +#define BFLL 0x8c +#define BFLH 0x8d +#define BCR 0x8e +#define BCRL 0x8e +#define BCRH 0x8f +#define DSR 0x90 +#define DMR 0x91 +#define FCT 0x93 +#define DIR 0x94 +#define DCMD 0x95 + +/* combine with timer or DMA register address */ +#define TIMER0 0x00 +#define TIMER1 0x08 +#define TIMER2 0x10 +#define TIMER3 0x18 +#define RXDMA 0x00 +#define TXDMA 0x20 + +/* SCA Command Codes */ +#define NOOP 0x00 +#define TXRESET 0x01 +#define TXENABLE 0x02 +#define TXDISABLE 0x03 +#define TXCRCINIT 0x04 +#define TXCRCEXCL 0x05 +#define TXEOM 0x06 +#define TXABORT 0x07 +#define MPON 0x08 +#define TXBUFCLR 0x09 +#define RXRESET 0x11 +#define RXENABLE 0x12 +#define RXDISABLE 0x13 +#define RXCRCINIT 0x14 +#define RXREJECT 0x15 +#define SEARCHMP 0x16 +#define RXCRCEXCL 0x17 +#define RXCRCCALC 0x18 +#define CHRESET 0x21 +#define HUNT 0x31 + +/* DMA command codes */ +#define SWABORT 0x01 +#define FEICLEAR 0x02 + +/* IE0 */ +#define TXINTE BIT7 +#define RXINTE BIT6 +#define TXRDYE BIT1 +#define RXRDYE BIT0 + +/* IE1 & SR1 */ +#define UDRN BIT7 +#define IDLE BIT6 +#define SYNCD BIT4 +#define FLGD BIT4 +#define CCTS BIT3 +#define CDCD BIT2 +#define BRKD BIT1 +#define ABTD BIT1 +#define GAPD BIT1 +#define BRKE BIT0 +#define IDLD BIT0 + +/* IE2 & SR2 */ +#define EOM BIT7 +#define PMP BIT6 +#define SHRT BIT6 +#define PE BIT5 +#define ABT BIT5 +#define FRME BIT4 +#define RBIT BIT4 +#define OVRN BIT3 +#define CRCE BIT2 + + +/* + * Global linked list of SyncLink devices + */ +static SLMP_INFO *synclinkmp_device_list = NULL; +static int synclinkmp_adapter_count = -1; +static int synclinkmp_device_count = 0; + +/* + * Set this param to non-zero to load eax with the + * .text section address and breakpoint on module load. + * This is useful for use with gdb and add-symbol-file command. + */ +static int break_on_load = 0; + +/* + * Driver major number, defaults to zero to get auto + * assigned major number. May be forced as module parameter. + */ +static int ttymajor = 0; + +/* + * Array of user specified options for ISA adapters. + */ +static int debug_level = 0; +static int maxframe[MAX_DEVICES] = {0,}; + +module_param(break_on_load, bool, 0); +module_param(ttymajor, int, 0); +module_param(debug_level, int, 0); +module_param_array(maxframe, int, NULL, 0); + +static char *driver_name = "SyncLink MultiPort driver"; +static char *driver_version = "$Revision: 4.38 $"; + +static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent); +static void synclinkmp_remove_one(struct pci_dev *dev); + +static struct pci_device_id synclinkmp_pci_tbl[] = { + { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl); + +MODULE_LICENSE("GPL"); + +static struct pci_driver synclinkmp_pci_driver = { + .name = "synclinkmp", + .id_table = synclinkmp_pci_tbl, + .probe = synclinkmp_init_one, + .remove = __devexit_p(synclinkmp_remove_one), +}; + + +static struct tty_driver *serial_driver; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + + +/* tty callbacks */ + +static int open(struct tty_struct *tty, struct file * filp); +static void close(struct tty_struct *tty, struct file * filp); +static void hangup(struct tty_struct *tty); +static void set_termios(struct tty_struct *tty, struct ktermios *old_termios); + +static int write(struct tty_struct *tty, const unsigned char *buf, int count); +static int put_char(struct tty_struct *tty, unsigned char ch); +static void send_xchar(struct tty_struct *tty, char ch); +static void wait_until_sent(struct tty_struct *tty, int timeout); +static int write_room(struct tty_struct *tty); +static void flush_chars(struct tty_struct *tty); +static void flush_buffer(struct tty_struct *tty); +static void tx_hold(struct tty_struct *tty); +static void tx_release(struct tty_struct *tty); + +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static int chars_in_buffer(struct tty_struct *tty); +static void throttle(struct tty_struct * tty); +static void unthrottle(struct tty_struct * tty); +static int set_break(struct tty_struct *tty, int break_state); + +#if SYNCLINK_GENERIC_HDLC +#define dev_to_port(D) (dev_to_hdlc(D)->priv) +static void hdlcdev_tx_done(SLMP_INFO *info); +static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size); +static int hdlcdev_init(SLMP_INFO *info); +static void hdlcdev_exit(SLMP_INFO *info); +#endif + +/* ioctl handlers */ + +static int get_stats(SLMP_INFO *info, struct mgsl_icount __user *user_icount); +static int get_params(SLMP_INFO *info, MGSL_PARAMS __user *params); +static int set_params(SLMP_INFO *info, MGSL_PARAMS __user *params); +static int get_txidle(SLMP_INFO *info, int __user *idle_mode); +static int set_txidle(SLMP_INFO *info, int idle_mode); +static int tx_enable(SLMP_INFO *info, int enable); +static int tx_abort(SLMP_INFO *info); +static int rx_enable(SLMP_INFO *info, int enable); +static int modem_input_wait(SLMP_INFO *info,int arg); +static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); +static int tiocmget(struct tty_struct *tty); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static int set_break(struct tty_struct *tty, int break_state); + +static void add_device(SLMP_INFO *info); +static void device_init(int adapter_num, struct pci_dev *pdev); +static int claim_resources(SLMP_INFO *info); +static void release_resources(SLMP_INFO *info); + +static int startup(SLMP_INFO *info); +static int block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info); +static int carrier_raised(struct tty_port *port); +static void shutdown(SLMP_INFO *info); +static void program_hw(SLMP_INFO *info); +static void change_params(SLMP_INFO *info); + +static bool init_adapter(SLMP_INFO *info); +static bool register_test(SLMP_INFO *info); +static bool irq_test(SLMP_INFO *info); +static bool loopback_test(SLMP_INFO *info); +static int adapter_test(SLMP_INFO *info); +static bool memory_test(SLMP_INFO *info); + +static void reset_adapter(SLMP_INFO *info); +static void reset_port(SLMP_INFO *info); +static void async_mode(SLMP_INFO *info); +static void hdlc_mode(SLMP_INFO *info); + +static void rx_stop(SLMP_INFO *info); +static void rx_start(SLMP_INFO *info); +static void rx_reset_buffers(SLMP_INFO *info); +static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last); +static bool rx_get_frame(SLMP_INFO *info); + +static void tx_start(SLMP_INFO *info); +static void tx_stop(SLMP_INFO *info); +static void tx_load_fifo(SLMP_INFO *info); +static void tx_set_idle(SLMP_INFO *info); +static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count); + +static void get_signals(SLMP_INFO *info); +static void set_signals(SLMP_INFO *info); +static void enable_loopback(SLMP_INFO *info, int enable); +static void set_rate(SLMP_INFO *info, u32 data_rate); + +static int bh_action(SLMP_INFO *info); +static void bh_handler(struct work_struct *work); +static void bh_receive(SLMP_INFO *info); +static void bh_transmit(SLMP_INFO *info); +static void bh_status(SLMP_INFO *info); +static void isr_timer(SLMP_INFO *info); +static void isr_rxint(SLMP_INFO *info); +static void isr_rxrdy(SLMP_INFO *info); +static void isr_txint(SLMP_INFO *info); +static void isr_txrdy(SLMP_INFO *info); +static void isr_rxdmaok(SLMP_INFO *info); +static void isr_rxdmaerror(SLMP_INFO *info); +static void isr_txdmaok(SLMP_INFO *info); +static void isr_txdmaerror(SLMP_INFO *info); +static void isr_io_pin(SLMP_INFO *info, u16 status); + +static int alloc_dma_bufs(SLMP_INFO *info); +static void free_dma_bufs(SLMP_INFO *info); +static int alloc_buf_list(SLMP_INFO *info); +static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count); +static int alloc_tmp_rx_buf(SLMP_INFO *info); +static void free_tmp_rx_buf(SLMP_INFO *info); + +static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count); +static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit); +static void tx_timeout(unsigned long context); +static void status_timeout(unsigned long context); + +static unsigned char read_reg(SLMP_INFO *info, unsigned char addr); +static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val); +static u16 read_reg16(SLMP_INFO *info, unsigned char addr); +static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val); +static unsigned char read_status_reg(SLMP_INFO * info); +static void write_control_reg(SLMP_INFO * info); + + +static unsigned char rx_active_fifo_level = 16; // rx request FIFO activation level in bytes +static unsigned char tx_active_fifo_level = 16; // tx request FIFO activation level in bytes +static unsigned char tx_negate_fifo_level = 32; // tx request FIFO negation level in bytes + +static u32 misc_ctrl_value = 0x007e4040; +static u32 lcr1_brdr_value = 0x00800028; + +static u32 read_ahead_count = 8; + +/* DPCR, DMA Priority Control + * + * 07..05 Not used, must be 0 + * 04 BRC, bus release condition: 0=all transfers complete + * 1=release after 1 xfer on all channels + * 03 CCC, channel change condition: 0=every cycle + * 1=after each channel completes all xfers + * 02..00 PR<2..0>, priority 100=round robin + * + * 00000100 = 0x00 + */ +static unsigned char dma_priority = 0x04; + +// Number of bytes that can be written to shared RAM +// in a single write operation +static u32 sca_pci_load_interval = 64; + +/* + * 1st function defined in .text section. Calling this function in + * init_module() followed by a breakpoint allows a remote debugger + * (gdb) to get the .text address for the add-symbol-file command. + * This allows remote debugging of dynamically loadable modules. + */ +static void* synclinkmp_get_text_ptr(void); +static void* synclinkmp_get_text_ptr(void) {return synclinkmp_get_text_ptr;} + +static inline int sanity_check(SLMP_INFO *info, + char *name, const char *routine) +{ +#ifdef SANITY_CHECK + static const char *badmagic = + "Warning: bad magic number for synclinkmp_struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null synclinkmp_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, name, routine); + return 1; + } + if (info->magic != MGSL_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#else + if (!info) + return 1; +#endif + return 0; +} + +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + +/* tty callbacks */ + +/* Called when a port is opened. Init and enable port. + */ +static int open(struct tty_struct *tty, struct file *filp) +{ + SLMP_INFO *info; + int retval, line; + unsigned long flags; + + line = tty->index; + if ((line < 0) || (line >= synclinkmp_device_count)) { + printk("%s(%d): open with invalid line #%d.\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + info = synclinkmp_device_list; + while(info && info->line != line) + info = info->next_device; + if (sanity_check(info, tty->name, "open")) + return -ENODEV; + if ( info->init_error ) { + printk("%s(%d):%s device is not allocated, init error=%d\n", + __FILE__,__LINE__,info->device_name,info->init_error); + return -ENODEV; + } + + tty->driver_data = info; + info->port.tty = tty; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s open(), old ref count = %d\n", + __FILE__,__LINE__,tty->driver->name, info->port.count); + + /* If port is closing, signal caller to try again */ + if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ + if (info->port.flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->port.close_wait); + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); + goto cleanup; + } + + info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + spin_lock_irqsave(&info->netlock, flags); + if (info->netcount) { + retval = -EBUSY; + spin_unlock_irqrestore(&info->netlock, flags); + goto cleanup; + } + info->port.count++; + spin_unlock_irqrestore(&info->netlock, flags); + + if (info->port.count == 1) { + /* 1st open on this device, init hardware */ + retval = startup(info); + if (retval < 0) + goto cleanup; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s block_til_ready() returned %d\n", + __FILE__,__LINE__, info->device_name, retval); + goto cleanup; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s open() success\n", + __FILE__,__LINE__, info->device_name); + retval = 0; + +cleanup: + if (retval) { + if (tty->count == 1) + info->port.tty = NULL; /* tty layer will release tty struct */ + if(info->port.count) + info->port.count--; + } + + return retval; +} + +/* Called when port is closed. Wait for remaining data to be + * sent. Disable port and free resources. + */ +static void close(struct tty_struct *tty, struct file *filp) +{ + SLMP_INFO * info = tty->driver_data; + + if (sanity_check(info, tty->name, "close")) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s close() entry, count=%d\n", + __FILE__,__LINE__, info->device_name, info->port.count); + + if (tty_port_close_start(&info->port, tty, filp) == 0) + goto cleanup; + + mutex_lock(&info->port.mutex); + if (info->port.flags & ASYNC_INITIALIZED) + wait_until_sent(tty, info->timeout); + + flush_buffer(tty); + tty_ldisc_flush(tty); + shutdown(info); + mutex_unlock(&info->port.mutex); + + tty_port_close_end(&info->port, tty); + info->port.tty = NULL; +cleanup: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__, + tty->driver->name, info->port.count); +} + +/* Called by tty_hangup() when a hangup is signaled. + * This is the same as closing all open descriptors for the port. + */ +static void hangup(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s hangup()\n", + __FILE__,__LINE__, info->device_name ); + + if (sanity_check(info, tty->name, "hangup")) + return; + + mutex_lock(&info->port.mutex); + flush_buffer(tty); + shutdown(info); + + spin_lock_irqsave(&info->port.lock, flags); + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + spin_unlock_irqrestore(&info->port.lock, flags); + mutex_unlock(&info->port.mutex); + + wake_up_interruptible(&info->port.open_wait); +} + +/* Set new termios settings + */ +static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__, + tty->driver->name ); + + change_params(info); + + /* Handle transition to B0 status */ + if (old_termios->c_cflag & CBAUD && + !(tty->termios->c_cflag & CBAUD)) { + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + tty->termios->c_cflag & CBAUD) { + info->serial_signals |= SerialSignal_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->serial_signals |= SerialSignal_RTS; + } + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } + + /* Handle turning off CRTSCTS */ + if (old_termios->c_cflag & CRTSCTS && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + tx_release(tty); + } +} + +/* Send a block of data + * + * Arguments: + * + * tty pointer to tty information structure + * buf pointer to buffer containing send data + * count size of send data in bytes + * + * Return Value: number of characters written + */ +static int write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int c, ret = 0; + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s write() count=%d\n", + __FILE__,__LINE__,info->device_name,count); + + if (sanity_check(info, tty->name, "write")) + goto cleanup; + + if (!info->tx_buf) + goto cleanup; + + if (info->params.mode == MGSL_MODE_HDLC) { + if (count > info->max_frame_size) { + ret = -EIO; + goto cleanup; + } + if (info->tx_active) + goto cleanup; + if (info->tx_count) { + /* send accumulated data from send_char() calls */ + /* as frame and wait before accepting more data. */ + tx_load_dma_buffer(info, info->tx_buf, info->tx_count); + goto start; + } + ret = info->tx_count = count; + tx_load_dma_buffer(info, buf, count); + goto start; + } + + for (;;) { + c = min_t(int, count, + min(info->max_frame_size - info->tx_count - 1, + info->max_frame_size - info->tx_put)); + if (c <= 0) + break; + + memcpy(info->tx_buf + info->tx_put, buf, c); + + spin_lock_irqsave(&info->lock,flags); + info->tx_put += c; + if (info->tx_put >= info->max_frame_size) + info->tx_put -= info->max_frame_size; + info->tx_count += c; + spin_unlock_irqrestore(&info->lock,flags); + + buf += c; + count -= c; + ret += c; + } + + if (info->params.mode == MGSL_MODE_HDLC) { + if (count) { + ret = info->tx_count = 0; + goto cleanup; + } + tx_load_dma_buffer(info, info->tx_buf, info->tx_count); + } +start: + if (info->tx_count && !tty->stopped && !tty->hw_stopped) { + spin_lock_irqsave(&info->lock,flags); + if (!info->tx_active) + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } + +cleanup: + if (debug_level >= DEBUG_LEVEL_INFO) + printk( "%s(%d):%s write() returning=%d\n", + __FILE__,__LINE__,info->device_name,ret); + return ret; +} + +/* Add a character to the transmit buffer. + */ +static int put_char(struct tty_struct *tty, unsigned char ch) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + int ret = 0; + + if ( debug_level >= DEBUG_LEVEL_INFO ) { + printk( "%s(%d):%s put_char(%d)\n", + __FILE__,__LINE__,info->device_name,ch); + } + + if (sanity_check(info, tty->name, "put_char")) + return 0; + + if (!info->tx_buf) + return 0; + + spin_lock_irqsave(&info->lock,flags); + + if ( (info->params.mode != MGSL_MODE_HDLC) || + !info->tx_active ) { + + if (info->tx_count < info->max_frame_size - 1) { + info->tx_buf[info->tx_put++] = ch; + if (info->tx_put >= info->max_frame_size) + info->tx_put -= info->max_frame_size; + info->tx_count++; + ret = 1; + } + } + + spin_unlock_irqrestore(&info->lock,flags); + return ret; +} + +/* Send a high-priority XON/XOFF character + */ +static void send_xchar(struct tty_struct *tty, char ch) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s send_xchar(%d)\n", + __FILE__,__LINE__, info->device_name, ch ); + + if (sanity_check(info, tty->name, "send_xchar")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + spin_lock_irqsave(&info->lock,flags); + if (!info->tx_enabled) + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +/* Wait until the transmitter is empty. + */ +static void wait_until_sent(struct tty_struct *tty, int timeout) +{ + SLMP_INFO * info = tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (!info ) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s wait_until_sent() entry\n", + __FILE__,__LINE__, info->device_name ); + + if (sanity_check(info, tty->name, "wait_until_sent")) + return; + + if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags)) + goto exit; + + orig_jiffies = jiffies; + + /* Set check interval to 1/5 of estimated time to + * send a character, and make it at least 1. The check + * interval should also be less than the timeout. + * Note: use tight timings here to satisfy the NIST-PCTS. + */ + + if ( info->params.data_rate ) { + char_time = info->timeout/(32 * 5); + if (!char_time) + char_time++; + } else + char_time = 1; + + if (timeout) + char_time = min_t(unsigned long, char_time, timeout); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + while (info->tx_active) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + } else { + /* + * TODO: determine if there is something similar to USC16C32 + * TXSTATUS_ALL_SENT status + */ + while ( info->tx_active && info->tx_enabled) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + } + +exit: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s wait_until_sent() exit\n", + __FILE__,__LINE__, info->device_name ); +} + +/* Return the count of free bytes in transmit buffer + */ +static int write_room(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + int ret; + + if (sanity_check(info, tty->name, "write_room")) + return 0; + + if (info->params.mode == MGSL_MODE_HDLC) { + ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; + } else { + ret = info->max_frame_size - info->tx_count - 1; + if (ret < 0) + ret = 0; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s write_room()=%d\n", + __FILE__, __LINE__, info->device_name, ret); + + return ret; +} + +/* enable transmitter and send remaining buffered characters + */ +static void flush_chars(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s flush_chars() entry tx_count=%d\n", + __FILE__,__LINE__,info->device_name,info->tx_count); + + if (sanity_check(info, tty->name, "flush_chars")) + return; + + if (info->tx_count <= 0 || tty->stopped || tty->hw_stopped || + !info->tx_buf) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s flush_chars() entry, starting transmitter\n", + __FILE__,__LINE__,info->device_name ); + + spin_lock_irqsave(&info->lock,flags); + + if (!info->tx_active) { + if ( (info->params.mode == MGSL_MODE_HDLC) && + info->tx_count ) { + /* operating in synchronous (frame oriented) mode */ + /* copy data from circular tx_buf to */ + /* transmit DMA buffer. */ + tx_load_dma_buffer(info, + info->tx_buf,info->tx_count); + } + tx_start(info); + } + + spin_unlock_irqrestore(&info->lock,flags); +} + +/* Discard all data in the send buffer + */ +static void flush_buffer(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s flush_buffer() entry\n", + __FILE__,__LINE__, info->device_name ); + + if (sanity_check(info, tty->name, "flush_buffer")) + return; + + spin_lock_irqsave(&info->lock,flags); + info->tx_count = info->tx_put = info->tx_get = 0; + del_timer(&info->tx_timer); + spin_unlock_irqrestore(&info->lock,flags); + + tty_wakeup(tty); +} + +/* throttle (stop) transmitter + */ +static void tx_hold(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "tx_hold")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):%s tx_hold()\n", + __FILE__,__LINE__,info->device_name); + + spin_lock_irqsave(&info->lock,flags); + if (info->tx_enabled) + tx_stop(info); + spin_unlock_irqrestore(&info->lock,flags); +} + +/* release (start) transmitter + */ +static void tx_release(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (sanity_check(info, tty->name, "tx_release")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):%s tx_release()\n", + __FILE__,__LINE__,info->device_name); + + spin_lock_irqsave(&info->lock,flags); + if (!info->tx_enabled) + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); +} + +/* Service an IOCTL request + * + * Arguments: + * + * tty pointer to tty instance data + * cmd IOCTL command code + * arg command argument/context + * + * Return Value: 0 if success, otherwise error code + */ +static int ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + SLMP_INFO *info = tty->driver_data; + void __user *argp = (void __user *)arg; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s ioctl() cmd=%08X\n", __FILE__,__LINE__, + info->device_name, cmd ); + + if (sanity_check(info, tty->name, "ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case MGSL_IOCGPARAMS: + return get_params(info, argp); + case MGSL_IOCSPARAMS: + return set_params(info, argp); + case MGSL_IOCGTXIDLE: + return get_txidle(info, argp); + case MGSL_IOCSTXIDLE: + return set_txidle(info, (int)arg); + case MGSL_IOCTXENABLE: + return tx_enable(info, (int)arg); + case MGSL_IOCRXENABLE: + return rx_enable(info, (int)arg); + case MGSL_IOCTXABORT: + return tx_abort(info); + case MGSL_IOCGSTATS: + return get_stats(info, argp); + case MGSL_IOCWAITEVENT: + return wait_mgsl_event(info, argp); + case MGSL_IOCLOOPTXDONE: + return 0; // TODO: Not supported, need to document + /* Wait for modem input (DCD,RI,DSR,CTS) change + * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS) + */ + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + SLMP_INFO *info = tty->driver_data; + struct mgsl_icount cnow; /* kernel counter temps */ + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->lock,flags); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline void line_info(struct seq_file *m, SLMP_INFO *info) +{ + char stat_buf[30]; + unsigned long flags; + + seq_printf(m, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n" + "\tIRQ=%d MaxFrameSize=%u\n", + info->device_name, + info->phys_sca_base, + info->phys_memory_base, + info->phys_statctrl_base, + info->phys_lcr_base, + info->irq_level, + info->max_frame_size ); + + /* output current serial signal states */ + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (info->serial_signals & SerialSignal_RTS) + strcat(stat_buf, "|RTS"); + if (info->serial_signals & SerialSignal_CTS) + strcat(stat_buf, "|CTS"); + if (info->serial_signals & SerialSignal_DTR) + strcat(stat_buf, "|DTR"); + if (info->serial_signals & SerialSignal_DSR) + strcat(stat_buf, "|DSR"); + if (info->serial_signals & SerialSignal_DCD) + strcat(stat_buf, "|CD"); + if (info->serial_signals & SerialSignal_RI) + strcat(stat_buf, "|RI"); + + if (info->params.mode == MGSL_MODE_HDLC) { + seq_printf(m, "\tHDLC txok:%d rxok:%d", + info->icount.txok, info->icount.rxok); + if (info->icount.txunder) + seq_printf(m, " txunder:%d", info->icount.txunder); + if (info->icount.txabort) + seq_printf(m, " txabort:%d", info->icount.txabort); + if (info->icount.rxshort) + seq_printf(m, " rxshort:%d", info->icount.rxshort); + if (info->icount.rxlong) + seq_printf(m, " rxlong:%d", info->icount.rxlong); + if (info->icount.rxover) + seq_printf(m, " rxover:%d", info->icount.rxover); + if (info->icount.rxcrc) + seq_printf(m, " rxlong:%d", info->icount.rxcrc); + } else { + seq_printf(m, "\tASYNC tx:%d rx:%d", + info->icount.tx, info->icount.rx); + if (info->icount.frame) + seq_printf(m, " fe:%d", info->icount.frame); + if (info->icount.parity) + seq_printf(m, " pe:%d", info->icount.parity); + if (info->icount.brk) + seq_printf(m, " brk:%d", info->icount.brk); + if (info->icount.overrun) + seq_printf(m, " oe:%d", info->icount.overrun); + } + + /* Append serial signal status to end */ + seq_printf(m, " %s\n", stat_buf+1); + + seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", + info->tx_active,info->bh_requested,info->bh_running, + info->pending_bh); +} + +/* Called to print information about devices + */ +static int synclinkmp_proc_show(struct seq_file *m, void *v) +{ + SLMP_INFO *info; + + seq_printf(m, "synclinkmp driver:%s\n", driver_version); + + info = synclinkmp_device_list; + while( info ) { + line_info(m, info); + info = info->next_device; + } + return 0; +} + +static int synclinkmp_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, synclinkmp_proc_show, NULL); +} + +static const struct file_operations synclinkmp_proc_fops = { + .owner = THIS_MODULE, + .open = synclinkmp_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Return the count of bytes in transmit buffer + */ +static int chars_in_buffer(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + + if (sanity_check(info, tty->name, "chars_in_buffer")) + return 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s chars_in_buffer()=%d\n", + __FILE__, __LINE__, info->device_name, info->tx_count); + + return info->tx_count; +} + +/* Signal remote device to throttle send data (our receive data) + */ +static void throttle(struct tty_struct * tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s throttle() entry\n", + __FILE__,__LINE__, info->device_name ); + + if (sanity_check(info, tty->name, "throttle")) + return; + + if (I_IXOFF(tty)) + send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock,flags); + info->serial_signals &= ~SerialSignal_RTS; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +/* Signal remote device to stop throttling send data (our receive data) + */ +static void unthrottle(struct tty_struct * tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s unthrottle() entry\n", + __FILE__,__LINE__, info->device_name ); + + if (sanity_check(info, tty->name, "unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock,flags); + info->serial_signals |= SerialSignal_RTS; + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + } +} + +/* set or clear transmit break condition + * break_state -1=set break condition, 0=clear + */ +static int set_break(struct tty_struct *tty, int break_state) +{ + unsigned char RegValue; + SLMP_INFO * info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s set_break(%d)\n", + __FILE__,__LINE__, info->device_name, break_state); + + if (sanity_check(info, tty->name, "set_break")) + return -EINVAL; + + spin_lock_irqsave(&info->lock,flags); + RegValue = read_reg(info, CTL); + if (break_state == -1) + RegValue |= BIT3; + else + RegValue &= ~BIT3; + write_reg(info, CTL, RegValue); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +#if SYNCLINK_GENERIC_HDLC + +/** + * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) + * set encoding and frame check sequence (FCS) options + * + * dev pointer to network device structure + * encoding serial encoding setting + * parity FCS setting + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ + SLMP_INFO *info = dev_to_port(dev); + unsigned char new_encoding; + unsigned short new_crctype; + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + switch (encoding) + { + case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; + case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; + case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; + case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; + case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; + default: return -EINVAL; + } + + switch (parity) + { + case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; + case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; + case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; + default: return -EINVAL; + } + + info->params.encoding = new_encoding; + info->params.crc_type = new_crctype; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + program_hw(info); + + return 0; +} + +/** + * called by generic HDLC layer to send frame + * + * skb socket buffer containing HDLC frame + * dev pointer to network device structure + */ +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + SLMP_INFO *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name); + + /* stop sending until this frame completes */ + netif_stop_queue(dev); + + /* copy data to device buffers */ + info->tx_count = skb->len; + tx_load_dma_buffer(info, skb->data, skb->len); + + /* update network statistics */ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* done with socket buffer, so free it */ + dev_kfree_skb(skb); + + /* save start time for transmit timeout detection */ + dev->trans_start = jiffies; + + /* start hardware transmitter if necessary */ + spin_lock_irqsave(&info->lock,flags); + if (!info->tx_active) + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + + return NETDEV_TX_OK; +} + +/** + * called by network layer when interface enabled + * claim resources and initialize hardware + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_open(struct net_device *dev) +{ + SLMP_INFO *info = dev_to_port(dev); + int rc; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); + + /* generic HDLC layer open processing */ + if ((rc = hdlc_open(dev))) + return rc; + + /* arbitrate between network and tty opens */ + spin_lock_irqsave(&info->netlock, flags); + if (info->port.count != 0 || info->netcount != 0) { + printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); + spin_unlock_irqrestore(&info->netlock, flags); + return -EBUSY; + } + info->netcount=1; + spin_unlock_irqrestore(&info->netlock, flags); + + /* claim resources and init adapter */ + if ((rc = startup(info)) != 0) { + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + return rc; + } + + /* assert DTR and RTS, apply hardware settings */ + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + program_hw(info); + + /* enable network layer transmit */ + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* inform generic HDLC layer of current DCD status */ + spin_lock_irqsave(&info->lock, flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock, flags); + if (info->serial_signals & SerialSignal_DCD) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + +/** + * called by network layer when interface is disabled + * shutdown hardware and release resources + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_close(struct net_device *dev) +{ + SLMP_INFO *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name); + + netif_stop_queue(dev); + + /* shutdown adapter and release resources */ + shutdown(info); + + hdlc_close(dev); + + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + + return 0; +} + +/** + * called by network layer to process IOCTL call to network device + * + * dev pointer to network device structure + * ifr pointer to network interface request structure + * cmd IOCTL command code + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(sync_serial_settings); + sync_serial_settings new_line; + sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync; + SLMP_INFO *info = dev_to_port(dev); + unsigned int flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name); + + /* return error if TTY interface open */ + if (info->port.count) + return -EBUSY; + + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: /* return current sync_serial_settings */ + + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + + flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + + switch (flags){ + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; + case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; + default: new_line.clock_type = CLOCK_DEFAULT; + } + + new_line.clock_rate = info->params.clock_speed; + new_line.loopback = info->params.loopback ? 1:0; + + if (copy_to_user(line, &new_line, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + switch (new_line.clock_type) + { + case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; + case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; + case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; + case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; + case CLOCK_DEFAULT: flags = info->params.flags & + (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; + default: return -EINVAL; + } + + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; + + info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + info->params.flags |= flags; + + info->params.loopback = new_line.loopback; + + if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) + info->params.clock_speed = new_line.clock_rate; + else + info->params.clock_speed = 0; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + program_hw(info); + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +/** + * called by network layer when transmit timeout is detected + * + * dev pointer to network device structure + */ +static void hdlcdev_tx_timeout(struct net_device *dev) +{ + SLMP_INFO *info = dev_to_port(dev); + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("hdlcdev_tx_timeout(%s)\n",dev->name); + + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + + spin_lock_irqsave(&info->lock,flags); + tx_stop(info); + spin_unlock_irqrestore(&info->lock,flags); + + netif_wake_queue(dev); +} + +/** + * called by device driver when transmit completes + * reenable network layer transmit if stopped + * + * info pointer to device instance information + */ +static void hdlcdev_tx_done(SLMP_INFO *info) +{ + if (netif_queue_stopped(info->netdev)) + netif_wake_queue(info->netdev); +} + +/** + * called by device driver when frame received + * pass frame to network layer + * + * info pointer to device instance information + * buf pointer to buffer contianing frame data + * size count of data bytes in buf + */ +static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) +{ + struct sk_buff *skb = dev_alloc_skb(size); + struct net_device *dev = info->netdev; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("hdlcdev_rx(%s)\n",dev->name); + + if (skb == NULL) { + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; + return; + } + + memcpy(skb_put(skb, size), buf, size); + + skb->protocol = hdlc_type_trans(skb, dev); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; + + netif_rx(skb); +} + +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + +/** + * called by device driver when adding device instance + * do generic HDLC initialization + * + * info pointer to device instance information + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_init(SLMP_INFO *info) +{ + int rc; + struct net_device *dev; + hdlc_device *hdlc; + + /* allocate and initialize network and HDLC layer objects */ + + if (!(dev = alloc_hdlcdev(info))) { + printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); + return -ENOMEM; + } + + /* for network layer reporting purposes only */ + dev->mem_start = info->phys_sca_base; + dev->mem_end = info->phys_sca_base + SCA_BASE_SIZE - 1; + dev->irq = info->irq_level; + + /* network layer callbacks and settings */ + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; + dev->tx_queue_len = 50; + + /* generic HDLC layer callbacks and settings */ + hdlc = dev_to_hdlc(dev); + hdlc->attach = hdlcdev_attach; + hdlc->xmit = hdlcdev_xmit; + + /* register objects with HDLC layer */ + if ((rc = register_hdlc_device(dev))) { + printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); + free_netdev(dev); + return rc; + } + + info->netdev = dev; + return 0; +} + +/** + * called by device driver when removing device instance + * do generic HDLC cleanup + * + * info pointer to device instance information + */ +static void hdlcdev_exit(SLMP_INFO *info) +{ + unregister_hdlc_device(info->netdev); + free_netdev(info->netdev); + info->netdev = NULL; +} + +#endif /* CONFIG_HDLC */ + + +/* Return next bottom half action to perform. + * Return Value: BH action code or 0 if nothing to do. + */ +static int bh_action(SLMP_INFO *info) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&info->lock,flags); + + if (info->pending_bh & BH_RECEIVE) { + info->pending_bh &= ~BH_RECEIVE; + rc = BH_RECEIVE; + } else if (info->pending_bh & BH_TRANSMIT) { + info->pending_bh &= ~BH_TRANSMIT; + rc = BH_TRANSMIT; + } else if (info->pending_bh & BH_STATUS) { + info->pending_bh &= ~BH_STATUS; + rc = BH_STATUS; + } + + if (!rc) { + /* Mark BH routine as complete */ + info->bh_running = false; + info->bh_requested = false; + } + + spin_unlock_irqrestore(&info->lock,flags); + + return rc; +} + +/* Perform bottom half processing of work items queued by ISR. + */ +static void bh_handler(struct work_struct *work) +{ + SLMP_INFO *info = container_of(work, SLMP_INFO, task); + int action; + + if (!info) + return; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_handler() entry\n", + __FILE__,__LINE__,info->device_name); + + info->bh_running = true; + + while((action = bh_action(info)) != 0) { + + /* Process work item */ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_handler() work item action=%d\n", + __FILE__,__LINE__,info->device_name, action); + + switch (action) { + + case BH_RECEIVE: + bh_receive(info); + break; + case BH_TRANSMIT: + bh_transmit(info); + break; + case BH_STATUS: + bh_status(info); + break; + default: + /* unknown work item ID */ + printk("%s(%d):%s Unknown work item ID=%08X!\n", + __FILE__,__LINE__,info->device_name,action); + break; + } + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_handler() exit\n", + __FILE__,__LINE__,info->device_name); +} + +static void bh_receive(SLMP_INFO *info) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_receive()\n", + __FILE__,__LINE__,info->device_name); + + while( rx_get_frame(info) ); +} + +static void bh_transmit(SLMP_INFO *info) +{ + struct tty_struct *tty = info->port.tty; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_transmit() entry\n", + __FILE__,__LINE__,info->device_name); + + if (tty) + tty_wakeup(tty); +} + +static void bh_status(SLMP_INFO *info) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):%s bh_status() entry\n", + __FILE__,__LINE__,info->device_name); + + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + info->dcd_chkcount = 0; + info->cts_chkcount = 0; +} + +static void isr_timer(SLMP_INFO * info) +{ + unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0; + + /* IER2<7..4> = timer<3..0> interrupt enables (0=disabled) */ + write_reg(info, IER2, 0); + + /* TMCS, Timer Control/Status Register + * + * 07 CMF, Compare match flag (read only) 1=match + * 06 ECMI, CMF Interrupt Enable: 0=disabled + * 05 Reserved, must be 0 + * 04 TME, Timer Enable + * 03..00 Reserved, must be 0 + * + * 0000 0000 + */ + write_reg(info, (unsigned char)(timer + TMCS), 0); + + info->irq_occurred = true; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_timer()\n", + __FILE__,__LINE__,info->device_name); +} + +static void isr_rxint(SLMP_INFO * info) +{ + struct tty_struct *tty = info->port.tty; + struct mgsl_icount *icount = &info->icount; + unsigned char status = read_reg(info, SR1) & info->ie1_value & (FLGD + IDLD + CDCD + BRKD); + unsigned char status2 = read_reg(info, SR2) & info->ie2_value & OVRN; + + /* clear status bits */ + if (status) + write_reg(info, SR1, status); + + if (status2) + write_reg(info, SR2, status2); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_rxint status=%02X %02x\n", + __FILE__,__LINE__,info->device_name,status,status2); + + if (info->params.mode == MGSL_MODE_ASYNC) { + if (status & BRKD) { + icount->brk++; + + /* process break detection if tty control + * is not set to ignore it + */ + if ( tty ) { + if (!(status & info->ignore_status_mask1)) { + if (info->read_status_mask1 & BRKD) { + tty_insert_flip_char(tty, 0, TTY_BREAK); + if (info->port.flags & ASYNC_SAK) + do_SAK(tty); + } + } + } + } + } + else { + if (status & (FLGD|IDLD)) { + if (status & FLGD) + info->icount.exithunt++; + else if (status & IDLD) + info->icount.rxidle++; + wake_up_interruptible(&info->event_wait_q); + } + } + + if (status & CDCD) { + /* simulate a common modem status change interrupt + * for our handler + */ + get_signals( info ); + isr_io_pin(info, + MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD)); + } +} + +/* + * handle async rx data interrupts + */ +static void isr_rxrdy(SLMP_INFO * info) +{ + u16 status; + unsigned char DataByte; + struct tty_struct *tty = info->port.tty; + struct mgsl_icount *icount = &info->icount; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_rxrdy\n", + __FILE__,__LINE__,info->device_name); + + while((status = read_reg(info,CST0)) & BIT0) + { + int flag = 0; + bool over = false; + DataByte = read_reg(info,TRB); + + icount->rx++; + + if ( status & (PE + FRME + OVRN) ) { + printk("%s(%d):%s rxerr=%04X\n", + __FILE__,__LINE__,info->device_name,status); + + /* update error statistics */ + if (status & PE) + icount->parity++; + else if (status & FRME) + icount->frame++; + else if (status & OVRN) + icount->overrun++; + + /* discard char if tty control flags say so */ + if (status & info->ignore_status_mask2) + continue; + + status &= info->read_status_mask2; + + if ( tty ) { + if (status & PE) + flag = TTY_PARITY; + else if (status & FRME) + flag = TTY_FRAME; + if (status & OVRN) { + /* Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + over = true; + } + } + } /* end of if (error) */ + + if ( tty ) { + tty_insert_flip_char(tty, DataByte, flag); + if (over) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + } + + if ( debug_level >= DEBUG_LEVEL_ISR ) { + printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n", + __FILE__,__LINE__,info->device_name, + icount->rx,icount->brk,icount->parity, + icount->frame,icount->overrun); + } + + if ( tty ) + tty_flip_buffer_push(tty); +} + +static void isr_txeom(SLMP_INFO * info, unsigned char status) +{ + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_txeom status=%02x\n", + __FILE__,__LINE__,info->device_name,status); + + write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */ + write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */ + write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ + + if (status & UDRN) { + write_reg(info, CMD, TXRESET); + write_reg(info, CMD, TXENABLE); + } else + write_reg(info, CMD, TXBUFCLR); + + /* disable and clear tx interrupts */ + info->ie0_value &= ~TXRDYE; + info->ie1_value &= ~(IDLE + UDRN); + write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value)); + write_reg(info, SR1, (unsigned char)(UDRN + IDLE)); + + if ( info->tx_active ) { + if (info->params.mode != MGSL_MODE_ASYNC) { + if (status & UDRN) + info->icount.txunder++; + else if (status & IDLE) + info->icount.txok++; + } + + info->tx_active = false; + info->tx_count = info->tx_put = info->tx_get = 0; + + del_timer(&info->tx_timer); + + if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done ) { + info->serial_signals &= ~SerialSignal_RTS; + info->drop_rts_on_tx_done = false; + set_signals(info); + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + { + if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { + tx_stop(info); + return; + } + info->pending_bh |= BH_TRANSMIT; + } + } +} + + +/* + * handle tx status interrupts + */ +static void isr_txint(SLMP_INFO * info) +{ + unsigned char status = read_reg(info, SR1) & info->ie1_value & (UDRN + IDLE + CCTS); + + /* clear status bits */ + write_reg(info, SR1, status); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_txint status=%02x\n", + __FILE__,__LINE__,info->device_name,status); + + if (status & (UDRN + IDLE)) + isr_txeom(info, status); + + if (status & CCTS) { + /* simulate a common modem status change interrupt + * for our handler + */ + get_signals( info ); + isr_io_pin(info, + MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS)); + + } +} + +/* + * handle async tx data interrupts + */ +static void isr_txrdy(SLMP_INFO * info) +{ + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_txrdy() tx_count=%d\n", + __FILE__,__LINE__,info->device_name,info->tx_count); + + if (info->params.mode != MGSL_MODE_ASYNC) { + /* disable TXRDY IRQ, enable IDLE IRQ */ + info->ie0_value &= ~TXRDYE; + info->ie1_value |= IDLE; + write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value)); + return; + } + + if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) { + tx_stop(info); + return; + } + + if ( info->tx_count ) + tx_load_fifo( info ); + else { + info->tx_active = false; + info->ie0_value &= ~TXRDYE; + write_reg(info, IE0, info->ie0_value); + } + + if (info->tx_count < WAKEUP_CHARS) + info->pending_bh |= BH_TRANSMIT; +} + +static void isr_rxdmaok(SLMP_INFO * info) +{ + /* BIT7 = EOT (end of transfer) + * BIT6 = EOM (end of message/frame) + */ + unsigned char status = read_reg(info,RXDMA + DSR) & 0xc0; + + /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ + write_reg(info, RXDMA + DSR, (unsigned char)(status | 1)); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_rxdmaok(), status=%02x\n", + __FILE__,__LINE__,info->device_name,status); + + info->pending_bh |= BH_RECEIVE; +} + +static void isr_rxdmaerror(SLMP_INFO * info) +{ + /* BIT5 = BOF (buffer overflow) + * BIT4 = COF (counter overflow) + */ + unsigned char status = read_reg(info,RXDMA + DSR) & 0x30; + + /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ + write_reg(info, RXDMA + DSR, (unsigned char)(status | 1)); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_rxdmaerror(), status=%02x\n", + __FILE__,__LINE__,info->device_name,status); + + info->rx_overflow = true; + info->pending_bh |= BH_RECEIVE; +} + +static void isr_txdmaok(SLMP_INFO * info) +{ + unsigned char status_reg1 = read_reg(info, SR1); + + write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */ + write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */ + write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_txdmaok(), status=%02x\n", + __FILE__,__LINE__,info->device_name,status_reg1); + + /* program TXRDY as FIFO empty flag, enable TXRDY IRQ */ + write_reg16(info, TRC0, 0); + info->ie0_value |= TXRDYE; + write_reg(info, IE0, info->ie0_value); +} + +static void isr_txdmaerror(SLMP_INFO * info) +{ + /* BIT5 = BOF (buffer overflow) + * BIT4 = COF (counter overflow) + */ + unsigned char status = read_reg(info,TXDMA + DSR) & 0x30; + + /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */ + write_reg(info, TXDMA + DSR, (unsigned char)(status | 1)); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s isr_txdmaerror(), status=%02x\n", + __FILE__,__LINE__,info->device_name,status); +} + +/* handle input serial signal changes + */ +static void isr_io_pin( SLMP_INFO *info, u16 status ) +{ + struct mgsl_icount *icount; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):isr_io_pin status=%04X\n", + __FILE__,__LINE__,status); + + if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | + MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { + icount = &info->icount; + /* update input line counters */ + if (status & MISCSTATUS_RI_LATCHED) { + icount->rng++; + if ( status & SerialSignal_RI ) + info->input_signal_events.ri_up++; + else + info->input_signal_events.ri_down++; + } + if (status & MISCSTATUS_DSR_LATCHED) { + icount->dsr++; + if ( status & SerialSignal_DSR ) + info->input_signal_events.dsr_up++; + else + info->input_signal_events.dsr_down++; + } + if (status & MISCSTATUS_DCD_LATCHED) { + if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) { + info->ie1_value &= ~CDCD; + write_reg(info, IE1, info->ie1_value); + } + icount->dcd++; + if (status & SerialSignal_DCD) { + info->input_signal_events.dcd_up++; + } else + info->input_signal_events.dcd_down++; +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) { + if (status & SerialSignal_DCD) + netif_carrier_on(info->netdev); + else + netif_carrier_off(info->netdev); + } +#endif + } + if (status & MISCSTATUS_CTS_LATCHED) + { + if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) { + info->ie1_value &= ~CCTS; + write_reg(info, IE1, info->ie1_value); + } + icount->cts++; + if ( status & SerialSignal_CTS ) + info->input_signal_events.cts_up++; + else + info->input_signal_events.cts_down++; + } + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if ( (info->port.flags & ASYNC_CHECK_CD) && + (status & MISCSTATUS_DCD_LATCHED) ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s CD now %s...", info->device_name, + (status & SerialSignal_DCD) ? "on" : "off"); + if (status & SerialSignal_DCD) + wake_up_interruptible(&info->port.open_wait); + else { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("doing serial hangup..."); + if (info->port.tty) + tty_hangup(info->port.tty); + } + } + + if ( (info->port.flags & ASYNC_CTS_FLOW) && + (status & MISCSTATUS_CTS_LATCHED) ) { + if ( info->port.tty ) { + if (info->port.tty->hw_stopped) { + if (status & SerialSignal_CTS) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx start..."); + info->port.tty->hw_stopped = 0; + tx_start(info); + info->pending_bh |= BH_TRANSMIT; + return; + } + } else { + if (!(status & SerialSignal_CTS)) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx stop..."); + info->port.tty->hw_stopped = 1; + tx_stop(info); + } + } + } + } + } + + info->pending_bh |= BH_STATUS; +} + +/* Interrupt service routine entry point. + * + * Arguments: + * irq interrupt number that caused interrupt + * dev_id device ID supplied during interrupt registration + * regs interrupted processor context + */ +static irqreturn_t synclinkmp_interrupt(int dummy, void *dev_id) +{ + SLMP_INFO *info = dev_id; + unsigned char status, status0, status1=0; + unsigned char dmastatus, dmastatus0, dmastatus1=0; + unsigned char timerstatus0, timerstatus1=0; + unsigned char shift; + unsigned int i; + unsigned short tmp; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk(KERN_DEBUG "%s(%d): synclinkmp_interrupt(%d)entry.\n", + __FILE__, __LINE__, info->irq_level); + + spin_lock(&info->lock); + + for(;;) { + + /* get status for SCA0 (ports 0-1) */ + tmp = read_reg16(info, ISR0); /* get ISR0 and ISR1 in one read */ + status0 = (unsigned char)tmp; + dmastatus0 = (unsigned char)(tmp>>8); + timerstatus0 = read_reg(info, ISR2); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk(KERN_DEBUG "%s(%d):%s status0=%02x, dmastatus0=%02x, timerstatus0=%02x\n", + __FILE__, __LINE__, info->device_name, + status0, dmastatus0, timerstatus0); + + if (info->port_count == 4) { + /* get status for SCA1 (ports 2-3) */ + tmp = read_reg16(info->port_array[2], ISR0); + status1 = (unsigned char)tmp; + dmastatus1 = (unsigned char)(tmp>>8); + timerstatus1 = read_reg(info->port_array[2], ISR2); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s status1=%02x, dmastatus1=%02x, timerstatus1=%02x\n", + __FILE__,__LINE__,info->device_name, + status1,dmastatus1,timerstatus1); + } + + if (!status0 && !dmastatus0 && !timerstatus0 && + !status1 && !dmastatus1 && !timerstatus1) + break; + + for(i=0; i < info->port_count ; i++) { + if (info->port_array[i] == NULL) + continue; + if (i < 2) { + status = status0; + dmastatus = dmastatus0; + } else { + status = status1; + dmastatus = dmastatus1; + } + + shift = i & 1 ? 4 :0; + + if (status & BIT0 << shift) + isr_rxrdy(info->port_array[i]); + if (status & BIT1 << shift) + isr_txrdy(info->port_array[i]); + if (status & BIT2 << shift) + isr_rxint(info->port_array[i]); + if (status & BIT3 << shift) + isr_txint(info->port_array[i]); + + if (dmastatus & BIT0 << shift) + isr_rxdmaerror(info->port_array[i]); + if (dmastatus & BIT1 << shift) + isr_rxdmaok(info->port_array[i]); + if (dmastatus & BIT2 << shift) + isr_txdmaerror(info->port_array[i]); + if (dmastatus & BIT3 << shift) + isr_txdmaok(info->port_array[i]); + } + + if (timerstatus0 & (BIT5 | BIT4)) + isr_timer(info->port_array[0]); + if (timerstatus0 & (BIT7 | BIT6)) + isr_timer(info->port_array[1]); + if (timerstatus1 & (BIT5 | BIT4)) + isr_timer(info->port_array[2]); + if (timerstatus1 & (BIT7 | BIT6)) + isr_timer(info->port_array[3]); + } + + for(i=0; i < info->port_count ; i++) { + SLMP_INFO * port = info->port_array[i]; + + /* Request bottom half processing if there's something + * for it to do and the bh is not already running. + * + * Note: startup adapter diags require interrupts. + * do not request bottom half processing if the + * device is not open in a normal mode. + */ + if ( port && (port->port.count || port->netcount) && + port->pending_bh && !port->bh_running && + !port->bh_requested ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s queueing bh task.\n", + __FILE__,__LINE__,port->device_name); + schedule_work(&port->task); + port->bh_requested = true; + } + } + + spin_unlock(&info->lock); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk(KERN_DEBUG "%s(%d):synclinkmp_interrupt(%d)exit.\n", + __FILE__, __LINE__, info->irq_level); + return IRQ_HANDLED; +} + +/* Initialize and start device. + */ +static int startup(SLMP_INFO * info) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name); + + if (info->port.flags & ASYNC_INITIALIZED) + return 0; + + if (!info->tx_buf) { + info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); + if (!info->tx_buf) { + printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", + __FILE__,__LINE__,info->device_name); + return -ENOMEM; + } + } + + info->pending_bh = 0; + + memset(&info->icount, 0, sizeof(info->icount)); + + /* program hardware for current parameters */ + reset_port(info); + + change_params(info); + + mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10)); + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags |= ASYNC_INITIALIZED; + + return 0; +} + +/* Called by close() and hangup() to shutdown hardware + */ +static void shutdown(SLMP_INFO * info) +{ + unsigned long flags; + + if (!(info->port.flags & ASYNC_INITIALIZED)) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s synclinkmp_shutdown()\n", + __FILE__,__LINE__, info->device_name ); + + /* clear status wait queue because status changes */ + /* can't happen after shutting down the hardware */ + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + del_timer(&info->tx_timer); + del_timer(&info->status_timer); + + kfree(info->tx_buf); + info->tx_buf = NULL; + + spin_lock_irqsave(&info->lock,flags); + + reset_port(info); + + if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) { + info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + set_signals(info); + } + + spin_unlock_irqrestore(&info->lock,flags); + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->port.flags &= ~ASYNC_INITIALIZED; +} + +static void program_hw(SLMP_INFO *info) +{ + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + + rx_stop(info); + tx_stop(info); + + info->tx_count = info->tx_put = info->tx_get = 0; + + if (info->params.mode == MGSL_MODE_HDLC || info->netcount) + hdlc_mode(info); + else + async_mode(info); + + set_signals(info); + + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + info->ri_chkcount = 0; + info->dsr_chkcount = 0; + + info->ie1_value |= (CDCD|CCTS); + write_reg(info, IE1, info->ie1_value); + + get_signals(info); + + if (info->netcount || (info->port.tty && info->port.tty->termios->c_cflag & CREAD) ) + rx_start(info); + + spin_unlock_irqrestore(&info->lock,flags); +} + +/* Reconfigure adapter based on new parameters + */ +static void change_params(SLMP_INFO *info) +{ + unsigned cflag; + int bits_per_char; + + if (!info->port.tty || !info->port.tty->termios) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s change_params()\n", + __FILE__,__LINE__, info->device_name ); + + cflag = info->port.tty->termios->c_cflag; + + /* if B0 rate (hangup) specified then negate DTR and RTS */ + /* otherwise assert DTR and RTS */ + if (cflag & CBAUD) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + + /* byte size and parity */ + + switch (cflag & CSIZE) { + case CS5: info->params.data_bits = 5; break; + case CS6: info->params.data_bits = 6; break; + case CS7: info->params.data_bits = 7; break; + case CS8: info->params.data_bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: info->params.data_bits = 7; break; + } + + if (cflag & CSTOPB) + info->params.stop_bits = 2; + else + info->params.stop_bits = 1; + + info->params.parity = ASYNC_PARITY_NONE; + if (cflag & PARENB) { + if (cflag & PARODD) + info->params.parity = ASYNC_PARITY_ODD; + else + info->params.parity = ASYNC_PARITY_EVEN; +#ifdef CMSPAR + if (cflag & CMSPAR) + info->params.parity = ASYNC_PARITY_SPACE; +#endif + } + + /* calculate number of jiffies to transmit a full + * FIFO (32 bytes) at specified data rate + */ + bits_per_char = info->params.data_bits + + info->params.stop_bits + 1; + + /* if port data rate is set to 460800 or less then + * allow tty settings to override, otherwise keep the + * current data rate. + */ + if (info->params.data_rate <= 460800) { + info->params.data_rate = tty_get_baud_rate(info->port.tty); + } + + if ( info->params.data_rate ) { + info->timeout = (32*HZ*bits_per_char) / + info->params.data_rate; + } + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->port.flags |= ASYNC_CTS_FLOW; + else + info->port.flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->port.flags &= ~ASYNC_CHECK_CD; + else + info->port.flags |= ASYNC_CHECK_CD; + + /* process tty input control flags */ + + info->read_status_mask2 = OVRN; + if (I_INPCK(info->port.tty)) + info->read_status_mask2 |= PE | FRME; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask1 |= BRKD; + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask2 |= PE | FRME; + if (I_IGNBRK(info->port.tty)) { + info->ignore_status_mask1 |= BRKD; + /* If ignoring parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask2 |= OVRN; + } + + program_hw(info); +} + +static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s get_params()\n", + __FILE__,__LINE__, info->device_name); + + if (!user_icount) { + memset(&info->icount, 0, sizeof(info->icount)); + } else { + mutex_lock(&info->port.mutex); + COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount)); + mutex_unlock(&info->port.mutex); + if (err) + return -EFAULT; + } + + return 0; +} + +static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params) +{ + int err; + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s get_params()\n", + __FILE__,__LINE__, info->device_name); + + mutex_lock(&info->port.mutex); + COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); + mutex_unlock(&info->port.mutex); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s get_params() user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; +} + +static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params) +{ + unsigned long flags; + MGSL_PARAMS tmp_params; + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s set_params\n", + __FILE__,__LINE__,info->device_name ); + COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s set_params() user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + mutex_lock(&info->port.mutex); + spin_lock_irqsave(&info->lock,flags); + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + spin_unlock_irqrestore(&info->lock,flags); + + change_params(info); + mutex_unlock(&info->port.mutex); + + return 0; +} + +static int get_txidle(SLMP_INFO * info, int __user *idle_mode) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s get_txidle()=%d\n", + __FILE__,__LINE__, info->device_name, info->idle_mode); + + COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s get_txidle() user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; +} + +static int set_txidle(SLMP_INFO * info, int idle_mode) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s set_txidle(%d)\n", + __FILE__,__LINE__,info->device_name, idle_mode ); + + spin_lock_irqsave(&info->lock,flags); + info->idle_mode = idle_mode; + tx_set_idle( info ); + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int tx_enable(SLMP_INFO * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tx_enable(%d)\n", + __FILE__,__LINE__,info->device_name, enable); + + spin_lock_irqsave(&info->lock,flags); + if ( enable ) { + if ( !info->tx_enabled ) { + tx_start(info); + } + } else { + if ( info->tx_enabled ) + tx_stop(info); + } + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +/* abort send HDLC frame + */ +static int tx_abort(SLMP_INFO * info) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tx_abort()\n", + __FILE__,__LINE__,info->device_name); + + spin_lock_irqsave(&info->lock,flags); + if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) { + info->ie1_value &= ~UDRN; + info->ie1_value |= IDLE; + write_reg(info, IE1, info->ie1_value); /* disable tx status interrupts */ + write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); /* clear pending */ + + write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ + write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ + + write_reg(info, CMD, TXABORT); + } + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +static int rx_enable(SLMP_INFO * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s rx_enable(%d)\n", + __FILE__,__LINE__,info->device_name,enable); + + spin_lock_irqsave(&info->lock,flags); + if ( enable ) { + if ( !info->rx_enabled ) + rx_start(info); + } else { + if ( info->rx_enabled ) + rx_stop(info); + } + spin_unlock_irqrestore(&info->lock,flags); + return 0; +} + +/* wait for specified event to occur + */ +static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr) +{ + unsigned long flags; + int s; + int rc=0; + struct mgsl_icount cprev, cnow; + int events; + int mask; + struct _input_signal_events oldsigs, newsigs; + DECLARE_WAITQUEUE(wait, current); + + COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); + if (rc) { + return -EFAULT; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s wait_mgsl_event(%d)\n", + __FILE__,__LINE__,info->device_name,mask); + + spin_lock_irqsave(&info->lock,flags); + + /* return immediately if state matches requested events */ + get_signals(info); + s = info->serial_signals; + + events = mask & + ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + + ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + + ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + + ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); + if (events) { + spin_unlock_irqrestore(&info->lock,flags); + goto exit; + } + + /* save current irq counts */ + cprev = info->icount; + oldsigs = info->input_signal_events; + + /* enable hunt and idle irqs if needed */ + if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { + unsigned char oldval = info->ie1_value; + unsigned char newval = oldval + + (mask & MgslEvent_ExitHuntMode ? FLGD:0) + + (mask & MgslEvent_IdleReceived ? IDLD:0); + if ( oldval != newval ) { + info->ie1_value = newval; + write_reg(info, IE1, info->ie1_value); + } + } + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&info->event_wait_q, &wait); + + spin_unlock_irqrestore(&info->lock,flags); + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get current irq counts */ + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + newsigs = info->input_signal_events; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + /* if no change, wait aborted for some reason */ + if (newsigs.dsr_up == oldsigs.dsr_up && + newsigs.dsr_down == oldsigs.dsr_down && + newsigs.dcd_up == oldsigs.dcd_up && + newsigs.dcd_down == oldsigs.dcd_down && + newsigs.cts_up == oldsigs.cts_up && + newsigs.cts_down == oldsigs.cts_down && + newsigs.ri_up == oldsigs.ri_up && + newsigs.ri_down == oldsigs.ri_down && + cnow.exithunt == cprev.exithunt && + cnow.rxidle == cprev.rxidle) { + rc = -EIO; + break; + } + + events = mask & + ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + + (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + + (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + + (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + + (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + + (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + + (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + + (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + + (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + + (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); + if (events) + break; + + cprev = cnow; + oldsigs = newsigs; + } + + remove_wait_queue(&info->event_wait_q, &wait); + set_current_state(TASK_RUNNING); + + + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + spin_lock_irqsave(&info->lock,flags); + if (!waitqueue_active(&info->event_wait_q)) { + /* disable enable exit hunt mode/idle rcvd IRQs */ + info->ie1_value &= ~(FLGD|IDLD); + write_reg(info, IE1, info->ie1_value); + } + spin_unlock_irqrestore(&info->lock,flags); + } +exit: + if ( rc == 0 ) + PUT_USER(rc, events, mask_ptr); + + return rc; +} + +static int modem_input_wait(SLMP_INFO *info,int arg) +{ + unsigned long flags; + int rc; + struct mgsl_icount cprev, cnow; + DECLARE_WAITQUEUE(wait, current); + + /* save current irq counts */ + spin_lock_irqsave(&info->lock,flags); + cprev = info->icount; + add_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + for(;;) { + schedule(); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + /* get new irq counts */ + spin_lock_irqsave(&info->lock,flags); + cnow = info->icount; + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&info->lock,flags); + + /* if no change, wait aborted for some reason */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; + break; + } + + /* check for change in caller specified modem input */ + if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || + (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || + (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || + (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { + rc = 0; + break; + } + + cprev = cnow; + } + remove_wait_queue(&info->status_event_wait_q, &wait); + set_current_state(TASK_RUNNING); + return rc; +} + +/* return the state of the serial control and status signals + */ +static int tiocmget(struct tty_struct *tty) +{ + SLMP_INFO *info = tty->driver_data; + unsigned int result; + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + + ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + + ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + + ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + + ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + + ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tiocmget() value=%08X\n", + __FILE__,__LINE__, info->device_name, result ); + return result; +} + +/* set modem control signals (DTR/RTS) + */ +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + SLMP_INFO *info = tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s tiocmset(%x,%x)\n", + __FILE__,__LINE__,info->device_name, set, clear); + + if (set & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + if (set & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + if (clear & TIOCM_RTS) + info->serial_signals &= ~SerialSignal_RTS; + if (clear & TIOCM_DTR) + info->serial_signals &= ~SerialSignal_DTR; + + spin_lock_irqsave(&info->lock,flags); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + return 0; +} + +static int carrier_raised(struct tty_port *port) +{ + SLMP_INFO *info = container_of(port, SLMP_INFO, port); + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; +} + +static void dtr_rts(struct tty_port *port, int on) +{ + SLMP_INFO *info = container_of(port, SLMP_INFO, port); + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + if (on) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + set_signals(info); + spin_unlock_irqrestore(&info->lock,flags); +} + +/* Block the current process until the specified port is ready to open. + */ +static int block_til_ready(struct tty_struct *tty, struct file *filp, + SLMP_INFO *info) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + bool do_clocal = false; + bool extra_count = false; + unsigned long flags; + int cd; + struct tty_port *port = &info->port; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s block_til_ready()\n", + __FILE__,__LINE__, tty->driver->name ); + + if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + /* nonblock mode is set or port is not enabled */ + /* just verify that callout device is not active */ + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = true; + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s block_til_ready() before block, count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + spin_lock_irqsave(&info->lock, flags); + if (!tty_hung_up_p(filp)) { + extra_count = true; + port->count--; + } + spin_unlock_irqrestore(&info->lock, flags); + port->blocked_open++; + + while (1) { + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); + + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){ + retval = (port->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + cd = tty_port_carrier_raised(port); + + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s block_til_ready() count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + tty_unlock(); + schedule(); + tty_lock(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + + if (extra_count) + port->count++; + port->blocked_open--; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):%s block_til_ready() after, count=%d\n", + __FILE__,__LINE__, tty->driver->name, port->count ); + + if (!retval) + port->flags |= ASYNC_NORMAL_ACTIVE; + + return retval; +} + +static int alloc_dma_bufs(SLMP_INFO *info) +{ + unsigned short BuffersPerFrame; + unsigned short BufferCount; + + // Force allocation to start at 64K boundary for each port. + // This is necessary because *all* buffer descriptors for a port + // *must* be in the same 64K block. All descriptors on a port + // share a common 'base' address (upper 8 bits of 24 bits) programmed + // into the CBP register. + info->port_array[0]->last_mem_alloc = (SCA_MEM_SIZE/4) * info->port_num; + + /* Calculate the number of DMA buffers necessary to hold the */ + /* largest allowable frame size. Note: If the max frame size is */ + /* not an even multiple of the DMA buffer size then we need to */ + /* round the buffer count per frame up one. */ + + BuffersPerFrame = (unsigned short)(info->max_frame_size/SCABUFSIZE); + if ( info->max_frame_size % SCABUFSIZE ) + BuffersPerFrame++; + + /* calculate total number of data buffers (SCABUFSIZE) possible + * in one ports memory (SCA_MEM_SIZE/4) after allocating memory + * for the descriptor list (BUFFERLISTSIZE). + */ + BufferCount = (SCA_MEM_SIZE/4 - BUFFERLISTSIZE)/SCABUFSIZE; + + /* limit number of buffers to maximum amount of descriptors */ + if (BufferCount > BUFFERLISTSIZE/sizeof(SCADESC)) + BufferCount = BUFFERLISTSIZE/sizeof(SCADESC); + + /* use enough buffers to transmit one max size frame */ + info->tx_buf_count = BuffersPerFrame + 1; + + /* never use more than half the available buffers for transmit */ + if (info->tx_buf_count > (BufferCount/2)) + info->tx_buf_count = BufferCount/2; + + if (info->tx_buf_count > SCAMAXDESC) + info->tx_buf_count = SCAMAXDESC; + + /* use remaining buffers for receive */ + info->rx_buf_count = BufferCount - info->tx_buf_count; + + if (info->rx_buf_count > SCAMAXDESC) + info->rx_buf_count = SCAMAXDESC; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):%s Allocating %d TX and %d RX DMA buffers.\n", + __FILE__,__LINE__, info->device_name, + info->tx_buf_count,info->rx_buf_count); + + if ( alloc_buf_list( info ) < 0 || + alloc_frame_bufs(info, + info->rx_buf_list, + info->rx_buf_list_ex, + info->rx_buf_count) < 0 || + alloc_frame_bufs(info, + info->tx_buf_list, + info->tx_buf_list_ex, + info->tx_buf_count) < 0 || + alloc_tmp_rx_buf(info) < 0 ) { + printk("%s(%d):%s Can't allocate DMA buffer memory\n", + __FILE__,__LINE__, info->device_name); + return -ENOMEM; + } + + rx_reset_buffers( info ); + + return 0; +} + +/* Allocate DMA buffers for the transmit and receive descriptor lists. + */ +static int alloc_buf_list(SLMP_INFO *info) +{ + unsigned int i; + + /* build list in adapter shared memory */ + info->buffer_list = info->memory_base + info->port_array[0]->last_mem_alloc; + info->buffer_list_phys = info->port_array[0]->last_mem_alloc; + info->port_array[0]->last_mem_alloc += BUFFERLISTSIZE; + + memset(info->buffer_list, 0, BUFFERLISTSIZE); + + /* Save virtual address pointers to the receive and */ + /* transmit buffer lists. (Receive 1st). These pointers will */ + /* be used by the processor to access the lists. */ + info->rx_buf_list = (SCADESC *)info->buffer_list; + + info->tx_buf_list = (SCADESC *)info->buffer_list; + info->tx_buf_list += info->rx_buf_count; + + /* Build links for circular buffer entry lists (tx and rx) + * + * Note: links are physical addresses read by the SCA device + * to determine the next buffer entry to use. + */ + + for ( i = 0; i < info->rx_buf_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->rx_buf_list_ex[i].phys_entry = + info->buffer_list_phys + (i * sizeof(SCABUFSIZE)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + info->rx_buf_list[i].next = info->buffer_list_phys; + if ( i < info->rx_buf_count - 1 ) + info->rx_buf_list[i].next += (i + 1) * sizeof(SCADESC); + + info->rx_buf_list[i].length = SCABUFSIZE; + } + + for ( i = 0; i < info->tx_buf_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->tx_buf_list_ex[i].phys_entry = info->buffer_list_phys + + ((info->rx_buf_count + i) * sizeof(SCADESC)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->tx_buf_list[i].next = info->buffer_list_phys + + info->rx_buf_count * sizeof(SCADESC); + + if ( i < info->tx_buf_count - 1 ) + info->tx_buf_list[i].next += (i + 1) * sizeof(SCADESC); + } + + return 0; +} + +/* Allocate the frame DMA buffers used by the specified buffer list. + */ +static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *buf_list,SCADESC_EX *buf_list_ex,int count) +{ + int i; + unsigned long phys_addr; + + for ( i = 0; i < count; i++ ) { + buf_list_ex[i].virt_addr = info->memory_base + info->port_array[0]->last_mem_alloc; + phys_addr = info->port_array[0]->last_mem_alloc; + info->port_array[0]->last_mem_alloc += SCABUFSIZE; + + buf_list[i].buf_ptr = (unsigned short)phys_addr; + buf_list[i].buf_base = (unsigned char)(phys_addr >> 16); + } + + return 0; +} + +static void free_dma_bufs(SLMP_INFO *info) +{ + info->buffer_list = NULL; + info->rx_buf_list = NULL; + info->tx_buf_list = NULL; +} + +/* allocate buffer large enough to hold max_frame_size. + * This buffer is used to pass an assembled frame to the line discipline. + */ +static int alloc_tmp_rx_buf(SLMP_INFO *info) +{ + info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); + if (info->tmp_rx_buf == NULL) + return -ENOMEM; + return 0; +} + +static void free_tmp_rx_buf(SLMP_INFO *info) +{ + kfree(info->tmp_rx_buf); + info->tmp_rx_buf = NULL; +} + +static int claim_resources(SLMP_INFO *info) +{ + if (request_mem_region(info->phys_memory_base,SCA_MEM_SIZE,"synclinkmp") == NULL) { + printk( "%s(%d):%s mem addr conflict, Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base); + info->init_error = DiagStatus_AddressConflict; + goto errout; + } + else + info->shared_mem_requested = true; + + if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) { + printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base); + info->init_error = DiagStatus_AddressConflict; + goto errout; + } + else + info->lcr_mem_requested = true; + + if (request_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE,"synclinkmp") == NULL) { + printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_sca_base); + info->init_error = DiagStatus_AddressConflict; + goto errout; + } + else + info->sca_base_requested = true; + + if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE,"synclinkmp") == NULL) { + printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_statctrl_base); + info->init_error = DiagStatus_AddressConflict; + goto errout; + } + else + info->sca_statctrl_requested = true; + + info->memory_base = ioremap_nocache(info->phys_memory_base, + SCA_MEM_SIZE); + if (!info->memory_base) { + printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + info->init_error = DiagStatus_CantAssignPciResources; + goto errout; + } + + info->lcr_base = ioremap_nocache(info->phys_lcr_base, PAGE_SIZE); + if (!info->lcr_base) { + printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); + info->init_error = DiagStatus_CantAssignPciResources; + goto errout; + } + info->lcr_base += info->lcr_offset; + + info->sca_base = ioremap_nocache(info->phys_sca_base, PAGE_SIZE); + if (!info->sca_base) { + printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_sca_base ); + info->init_error = DiagStatus_CantAssignPciResources; + goto errout; + } + info->sca_base += info->sca_offset; + + info->statctrl_base = ioremap_nocache(info->phys_statctrl_base, + PAGE_SIZE); + if (!info->statctrl_base) { + printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_statctrl_base ); + info->init_error = DiagStatus_CantAssignPciResources; + goto errout; + } + info->statctrl_base += info->statctrl_offset; + + if ( !memory_test(info) ) { + printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + info->init_error = DiagStatus_MemoryError; + goto errout; + } + + return 0; + +errout: + release_resources( info ); + return -ENODEV; +} + +static void release_resources(SLMP_INFO *info) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s release_resources() entry\n", + __FILE__,__LINE__,info->device_name ); + + if ( info->irq_requested ) { + free_irq(info->irq_level, info); + info->irq_requested = false; + } + + if ( info->shared_mem_requested ) { + release_mem_region(info->phys_memory_base,SCA_MEM_SIZE); + info->shared_mem_requested = false; + } + if ( info->lcr_mem_requested ) { + release_mem_region(info->phys_lcr_base + info->lcr_offset,128); + info->lcr_mem_requested = false; + } + if ( info->sca_base_requested ) { + release_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE); + info->sca_base_requested = false; + } + if ( info->sca_statctrl_requested ) { + release_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE); + info->sca_statctrl_requested = false; + } + + if (info->memory_base){ + iounmap(info->memory_base); + info->memory_base = NULL; + } + + if (info->sca_base) { + iounmap(info->sca_base - info->sca_offset); + info->sca_base=NULL; + } + + if (info->statctrl_base) { + iounmap(info->statctrl_base - info->statctrl_offset); + info->statctrl_base=NULL; + } + + if (info->lcr_base){ + iounmap(info->lcr_base - info->lcr_offset); + info->lcr_base = NULL; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s release_resources() exit\n", + __FILE__,__LINE__,info->device_name ); +} + +/* Add the specified device instance data structure to the + * global linked list of devices and increment the device count. + */ +static void add_device(SLMP_INFO *info) +{ + info->next_device = NULL; + info->line = synclinkmp_device_count; + sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num); + + if (info->line < MAX_DEVICES) { + if (maxframe[info->line]) + info->max_frame_size = maxframe[info->line]; + } + + synclinkmp_device_count++; + + if ( !synclinkmp_device_list ) + synclinkmp_device_list = info; + else { + SLMP_INFO *current_dev = synclinkmp_device_list; + while( current_dev->next_device ) + current_dev = current_dev->next_device; + current_dev->next_device = info; + } + + if ( info->max_frame_size < 4096 ) + info->max_frame_size = 4096; + else if ( info->max_frame_size > 65535 ) + info->max_frame_size = 65535; + + printk( "SyncLink MultiPort %s: " + "Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n", + info->device_name, + info->phys_sca_base, + info->phys_memory_base, + info->phys_statctrl_base, + info->phys_lcr_base, + info->irq_level, + info->max_frame_size ); + +#if SYNCLINK_GENERIC_HDLC + hdlcdev_init(info); +#endif +} + +static const struct tty_port_operations port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, +}; + +/* Allocate and initialize a device instance structure + * + * Return Value: pointer to SLMP_INFO if success, otherwise NULL + */ +static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) +{ + SLMP_INFO *info; + + info = kzalloc(sizeof(SLMP_INFO), + GFP_KERNEL); + + if (!info) { + printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n", + __FILE__,__LINE__, adapter_num, port_num); + } else { + tty_port_init(&info->port); + info->port.ops = &port_ops; + info->magic = MGSL_MAGIC; + INIT_WORK(&info->task, bh_handler); + info->max_frame_size = 4096; + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; + init_waitqueue_head(&info->status_event_wait_q); + init_waitqueue_head(&info->event_wait_q); + spin_lock_init(&info->netlock); + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + info->idle_mode = HDLC_TXIDLE_FLAGS; + info->adapter_num = adapter_num; + info->port_num = port_num; + + /* Copy configuration info to device instance data */ + info->irq_level = pdev->irq; + info->phys_lcr_base = pci_resource_start(pdev,0); + info->phys_sca_base = pci_resource_start(pdev,2); + info->phys_memory_base = pci_resource_start(pdev,3); + info->phys_statctrl_base = pci_resource_start(pdev,4); + + /* Because veremap only works on page boundaries we must map + * a larger area than is actually implemented for the LCR + * memory range. We map a full page starting at the page boundary. + */ + info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); + info->phys_lcr_base &= ~(PAGE_SIZE-1); + + info->sca_offset = info->phys_sca_base & (PAGE_SIZE-1); + info->phys_sca_base &= ~(PAGE_SIZE-1); + + info->statctrl_offset = info->phys_statctrl_base & (PAGE_SIZE-1); + info->phys_statctrl_base &= ~(PAGE_SIZE-1); + + info->bus_type = MGSL_BUS_TYPE_PCI; + info->irq_flags = IRQF_SHARED; + + setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info); + setup_timer(&info->status_timer, status_timeout, + (unsigned long)info); + + /* Store the PCI9050 misc control register value because a flaw + * in the PCI9050 prevents LCR registers from being read if + * BIOS assigns an LCR base address with bit 7 set. + * + * Only the misc control register is accessed for which only + * write access is needed, so set an initial value and change + * bits to the device instance data as we write the value + * to the actual misc control register. + */ + info->misc_ctrl_value = 0x087e4546; + + /* initial port state is unknown - if startup errors + * occur, init_error will be set to indicate the + * problem. Once the port is fully initialized, + * this value will be set to 0 to indicate the + * port is available. + */ + info->init_error = -1; + } + + return info; +} + +static void device_init(int adapter_num, struct pci_dev *pdev) +{ + SLMP_INFO *port_array[SCA_MAX_PORTS]; + int port; + + /* allocate device instances for up to SCA_MAX_PORTS devices */ + for ( port = 0; port < SCA_MAX_PORTS; ++port ) { + port_array[port] = alloc_dev(adapter_num,port,pdev); + if( port_array[port] == NULL ) { + for ( --port; port >= 0; --port ) + kfree(port_array[port]); + return; + } + } + + /* give copy of port_array to all ports and add to device list */ + for ( port = 0; port < SCA_MAX_PORTS; ++port ) { + memcpy(port_array[port]->port_array,port_array,sizeof(port_array)); + add_device( port_array[port] ); + spin_lock_init(&port_array[port]->lock); + } + + /* Allocate and claim adapter resources */ + if ( !claim_resources(port_array[0]) ) { + + alloc_dma_bufs(port_array[0]); + + /* copy resource information from first port to others */ + for ( port = 1; port < SCA_MAX_PORTS; ++port ) { + port_array[port]->lock = port_array[0]->lock; + port_array[port]->irq_level = port_array[0]->irq_level; + port_array[port]->memory_base = port_array[0]->memory_base; + port_array[port]->sca_base = port_array[0]->sca_base; + port_array[port]->statctrl_base = port_array[0]->statctrl_base; + port_array[port]->lcr_base = port_array[0]->lcr_base; + alloc_dma_bufs(port_array[port]); + } + + if ( request_irq(port_array[0]->irq_level, + synclinkmp_interrupt, + port_array[0]->irq_flags, + port_array[0]->device_name, + port_array[0]) < 0 ) { + printk( "%s(%d):%s Cant request interrupt, IRQ=%d\n", + __FILE__,__LINE__, + port_array[0]->device_name, + port_array[0]->irq_level ); + } + else { + port_array[0]->irq_requested = true; + adapter_test(port_array[0]); + } + } +} + +static const struct tty_operations ops = { + .open = open, + .close = close, + .write = write, + .put_char = put_char, + .flush_chars = flush_chars, + .write_room = write_room, + .chars_in_buffer = chars_in_buffer, + .flush_buffer = flush_buffer, + .ioctl = ioctl, + .throttle = throttle, + .unthrottle = unthrottle, + .send_xchar = send_xchar, + .break_ctl = set_break, + .wait_until_sent = wait_until_sent, + .set_termios = set_termios, + .stop = tx_hold, + .start = tx_release, + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, + .get_icount = get_icount, + .proc_fops = &synclinkmp_proc_fops, +}; + + +static void synclinkmp_cleanup(void) +{ + int rc; + SLMP_INFO *info; + SLMP_INFO *tmp; + + printk("Unloading %s %s\n", driver_name, driver_version); + + if (serial_driver) { + if ((rc = tty_unregister_driver(serial_driver))) + printk("%s(%d) failed to unregister tty driver err=%d\n", + __FILE__,__LINE__,rc); + put_tty_driver(serial_driver); + } + + /* reset devices */ + info = synclinkmp_device_list; + while(info) { + reset_port(info); + info = info->next_device; + } + + /* release devices */ + info = synclinkmp_device_list; + while(info) { +#if SYNCLINK_GENERIC_HDLC + hdlcdev_exit(info); +#endif + free_dma_bufs(info); + free_tmp_rx_buf(info); + if ( info->port_num == 0 ) { + if (info->sca_base) + write_reg(info, LPR, 1); /* set low power mode */ + release_resources(info); + } + tmp = info; + info = info->next_device; + kfree(tmp); + } + + pci_unregister_driver(&synclinkmp_pci_driver); +} + +/* Driver initialization entry point. + */ + +static int __init synclinkmp_init(void) +{ + int rc; + + if (break_on_load) { + synclinkmp_get_text_ptr(); + BREAKPOINT(); + } + + printk("%s %s\n", driver_name, driver_version); + + if ((rc = pci_register_driver(&synclinkmp_pci_driver)) < 0) { + printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc); + return rc; + } + + serial_driver = alloc_tty_driver(128); + if (!serial_driver) { + rc = -ENOMEM; + goto error; + } + + /* Initialize the tty_driver structure */ + + serial_driver->owner = THIS_MODULE; + serial_driver->driver_name = "synclinkmp"; + serial_driver->name = "ttySLM"; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + 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->init_termios.c_ispeed = 9600; + serial_driver->init_termios.c_ospeed = 9600; + serial_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(serial_driver, &ops); + if ((rc = tty_register_driver(serial_driver)) < 0) { + printk("%s(%d):Couldn't register serial driver\n", + __FILE__,__LINE__); + put_tty_driver(serial_driver); + serial_driver = NULL; + goto error; + } + + printk("%s %s, tty major#%d\n", + driver_name, driver_version, + serial_driver->major); + + return 0; + +error: + synclinkmp_cleanup(); + return rc; +} + +static void __exit synclinkmp_exit(void) +{ + synclinkmp_cleanup(); +} + +module_init(synclinkmp_init); +module_exit(synclinkmp_exit); + +/* Set the port for internal loopback mode. + * The TxCLK and RxCLK signals are generated from the BRG and + * the TxD is looped back to the RxD internally. + */ +static void enable_loopback(SLMP_INFO *info, int enable) +{ + if (enable) { + /* MD2 (Mode Register 2) + * 01..00 CNCT<1..0> Channel Connection 11=Local Loopback + */ + write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) | (BIT1 + BIT0))); + + /* degate external TxC clock source */ + info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); + write_control_reg(info); + + /* RXS/TXS (Rx/Tx clock source) + * 07 Reserved, must be 0 + * 06..04 Clock Source, 100=BRG + * 03..00 Clock Divisor, 0000=1 + */ + write_reg(info, RXS, 0x40); + write_reg(info, TXS, 0x40); + + } else { + /* MD2 (Mode Register 2) + * 01..00 CNCT<1..0> Channel connection, 0=normal + */ + write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) & ~(BIT1 + BIT0))); + + /* RXS/TXS (Rx/Tx clock source) + * 07 Reserved, must be 0 + * 06..04 Clock Source, 000=RxC/TxC Pin + * 03..00 Clock Divisor, 0000=1 + */ + write_reg(info, RXS, 0x00); + write_reg(info, TXS, 0x00); + } + + /* set LinkSpeed if available, otherwise default to 2Mbps */ + if (info->params.clock_speed) + set_rate(info, info->params.clock_speed); + else + set_rate(info, 3686400); +} + +/* Set the baud rate register to the desired speed + * + * data_rate data rate of clock in bits per second + * A data rate of 0 disables the AUX clock. + */ +static void set_rate( SLMP_INFO *info, u32 data_rate ) +{ + u32 TMCValue; + unsigned char BRValue; + u32 Divisor=0; + + /* fBRG = fCLK/(TMC * 2^BR) + */ + if (data_rate != 0) { + Divisor = 14745600/data_rate; + if (!Divisor) + Divisor = 1; + + TMCValue = Divisor; + + BRValue = 0; + if (TMCValue != 1 && TMCValue != 2) { + /* BRValue of 0 provides 50/50 duty cycle *only* when + * TMCValue is 1 or 2. BRValue of 1 to 9 always provides + * 50/50 duty cycle. + */ + BRValue = 1; + TMCValue >>= 1; + } + + /* while TMCValue is too big for TMC register, divide + * by 2 and increment BR exponent. + */ + for(; TMCValue > 256 && BRValue < 10; BRValue++) + TMCValue >>= 1; + + write_reg(info, TXS, + (unsigned char)((read_reg(info, TXS) & 0xf0) | BRValue)); + write_reg(info, RXS, + (unsigned char)((read_reg(info, RXS) & 0xf0) | BRValue)); + write_reg(info, TMC, (unsigned char)TMCValue); + } + else { + write_reg(info, TXS,0); + write_reg(info, RXS,0); + write_reg(info, TMC, 0); + } +} + +/* Disable receiver + */ +static void rx_stop(SLMP_INFO *info) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):%s rx_stop()\n", + __FILE__,__LINE__, info->device_name ); + + write_reg(info, CMD, RXRESET); + + info->ie0_value &= ~RXRDYE; + write_reg(info, IE0, info->ie0_value); /* disable Rx data interrupts */ + + write_reg(info, RXDMA + DSR, 0); /* disable Rx DMA */ + write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */ + write_reg(info, RXDMA + DIR, 0); /* disable Rx DMA interrupts */ + + info->rx_enabled = false; + info->rx_overflow = false; +} + +/* enable the receiver + */ +static void rx_start(SLMP_INFO *info) +{ + int i; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):%s rx_start()\n", + __FILE__,__LINE__, info->device_name ); + + write_reg(info, CMD, RXRESET); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* HDLC, disabe IRQ on rxdata */ + info->ie0_value &= ~RXRDYE; + write_reg(info, IE0, info->ie0_value); + + /* Reset all Rx DMA buffers and program rx dma */ + write_reg(info, RXDMA + DSR, 0); /* disable Rx DMA */ + write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */ + + for (i = 0; i < info->rx_buf_count; i++) { + info->rx_buf_list[i].status = 0xff; + + // throttle to 4 shared memory writes at a time to prevent + // hogging local bus (keep latency time for DMA requests low). + if (!(i % 4)) + read_status_reg(info); + } + info->current_rx_buf = 0; + + /* set current/1st descriptor address */ + write_reg16(info, RXDMA + CDA, + info->rx_buf_list_ex[0].phys_entry); + + /* set new last rx descriptor address */ + write_reg16(info, RXDMA + EDA, + info->rx_buf_list_ex[info->rx_buf_count - 1].phys_entry); + + /* set buffer length (shared by all rx dma data buffers) */ + write_reg16(info, RXDMA + BFL, SCABUFSIZE); + + write_reg(info, RXDMA + DIR, 0x60); /* enable Rx DMA interrupts (EOM/BOF) */ + write_reg(info, RXDMA + DSR, 0xf2); /* clear Rx DMA IRQs, enable Rx DMA */ + } else { + /* async, enable IRQ on rxdata */ + info->ie0_value |= RXRDYE; + write_reg(info, IE0, info->ie0_value); + } + + write_reg(info, CMD, RXENABLE); + + info->rx_overflow = false; + info->rx_enabled = true; +} + +/* Enable the transmitter and send a transmit frame if + * one is loaded in the DMA buffers. + */ +static void tx_start(SLMP_INFO *info) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):%s tx_start() tx_count=%d\n", + __FILE__,__LINE__, info->device_name,info->tx_count ); + + if (!info->tx_enabled ) { + write_reg(info, CMD, TXRESET); + write_reg(info, CMD, TXENABLE); + info->tx_enabled = true; + } + + if ( info->tx_count ) { + + /* If auto RTS enabled and RTS is inactive, then assert */ + /* RTS and set a flag indicating that the driver should */ + /* negate RTS when the transmission completes. */ + + info->drop_rts_on_tx_done = false; + + if (info->params.mode != MGSL_MODE_ASYNC) { + + if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { + get_signals( info ); + if ( !(info->serial_signals & SerialSignal_RTS) ) { + info->serial_signals |= SerialSignal_RTS; + set_signals( info ); + info->drop_rts_on_tx_done = true; + } + } + + write_reg16(info, TRC0, + (unsigned short)(((tx_negate_fifo_level-1)<<8) + tx_active_fifo_level)); + + write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ + write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ + + /* set TX CDA (current descriptor address) */ + write_reg16(info, TXDMA + CDA, + info->tx_buf_list_ex[0].phys_entry); + + /* set TX EDA (last descriptor address) */ + write_reg16(info, TXDMA + EDA, + info->tx_buf_list_ex[info->last_tx_buf].phys_entry); + + /* enable underrun IRQ */ + info->ie1_value &= ~IDLE; + info->ie1_value |= UDRN; + write_reg(info, IE1, info->ie1_value); + write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); + + write_reg(info, TXDMA + DIR, 0x40); /* enable Tx DMA interrupts (EOM) */ + write_reg(info, TXDMA + DSR, 0xf2); /* clear Tx DMA IRQs, enable Tx DMA */ + + mod_timer(&info->tx_timer, jiffies + + msecs_to_jiffies(5000)); + } + else { + tx_load_fifo(info); + /* async, enable IRQ on txdata */ + info->ie0_value |= TXRDYE; + write_reg(info, IE0, info->ie0_value); + } + + info->tx_active = true; + } +} + +/* stop the transmitter and DMA + */ +static void tx_stop( SLMP_INFO *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):%s tx_stop()\n", + __FILE__,__LINE__, info->device_name ); + + del_timer(&info->tx_timer); + + write_reg(info, TXDMA + DSR, 0); /* disable DMA channel */ + write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */ + + write_reg(info, CMD, TXRESET); + + info->ie1_value &= ~(UDRN + IDLE); + write_reg(info, IE1, info->ie1_value); /* disable tx status interrupts */ + write_reg(info, SR1, (unsigned char)(IDLE + UDRN)); /* clear pending */ + + info->ie0_value &= ~TXRDYE; + write_reg(info, IE0, info->ie0_value); /* disable tx data interrupts */ + + info->tx_enabled = false; + info->tx_active = false; +} + +/* Fill the transmit FIFO until the FIFO is full or + * there is no more data to load. + */ +static void tx_load_fifo(SLMP_INFO *info) +{ + u8 TwoBytes[2]; + + /* do nothing is now tx data available and no XON/XOFF pending */ + + if ( !info->tx_count && !info->x_char ) + return; + + /* load the Transmit FIFO until FIFOs full or all data sent */ + + while( info->tx_count && (read_reg(info,SR0) & BIT1) ) { + + /* there is more space in the transmit FIFO and */ + /* there is more data in transmit buffer */ + + if ( (info->tx_count > 1) && !info->x_char ) { + /* write 16-bits */ + TwoBytes[0] = info->tx_buf[info->tx_get++]; + if (info->tx_get >= info->max_frame_size) + info->tx_get -= info->max_frame_size; + TwoBytes[1] = info->tx_buf[info->tx_get++]; + if (info->tx_get >= info->max_frame_size) + info->tx_get -= info->max_frame_size; + + write_reg16(info, TRB, *((u16 *)TwoBytes)); + + info->tx_count -= 2; + info->icount.tx += 2; + } else { + /* only 1 byte left to transmit or 1 FIFO slot left */ + + if (info->x_char) { + /* transmit pending high priority char */ + write_reg(info, TRB, info->x_char); + info->x_char = 0; + } else { + write_reg(info, TRB, info->tx_buf[info->tx_get++]); + if (info->tx_get >= info->max_frame_size) + info->tx_get -= info->max_frame_size; + info->tx_count--; + } + info->icount.tx++; + } + } +} + +/* Reset a port to a known state + */ +static void reset_port(SLMP_INFO *info) +{ + if (info->sca_base) { + + tx_stop(info); + rx_stop(info); + + info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + set_signals(info); + + /* disable all port interrupts */ + info->ie0_value = 0; + info->ie1_value = 0; + info->ie2_value = 0; + write_reg(info, IE0, info->ie0_value); + write_reg(info, IE1, info->ie1_value); + write_reg(info, IE2, info->ie2_value); + + write_reg(info, CMD, CHRESET); + } +} + +/* Reset all the ports to a known state. + */ +static void reset_adapter(SLMP_INFO *info) +{ + int i; + + for ( i=0; i < SCA_MAX_PORTS; ++i) { + if (info->port_array[i]) + reset_port(info->port_array[i]); + } +} + +/* Program port for asynchronous communications. + */ +static void async_mode(SLMP_INFO *info) +{ + + unsigned char RegValue; + + tx_stop(info); + rx_stop(info); + + /* MD0, Mode Register 0 + * + * 07..05 PRCTL<2..0>, Protocol Mode, 000=async + * 04 AUTO, Auto-enable (RTS/CTS/DCD) + * 03 Reserved, must be 0 + * 02 CRCCC, CRC Calculation, 0=disabled + * 01..00 STOP<1..0> Stop bits (00=1,10=2) + * + * 0000 0000 + */ + RegValue = 0x00; + if (info->params.stop_bits != 1) + RegValue |= BIT1; + write_reg(info, MD0, RegValue); + + /* MD1, Mode Register 1 + * + * 07..06 BRATE<1..0>, bit rate, 00=1/1 01=1/16 10=1/32 11=1/64 + * 05..04 TXCHR<1..0>, tx char size, 00=8 bits,01=7,10=6,11=5 + * 03..02 RXCHR<1..0>, rx char size + * 01..00 PMPM<1..0>, Parity mode, 00=none 10=even 11=odd + * + * 0100 0000 + */ + RegValue = 0x40; + switch (info->params.data_bits) { + case 7: RegValue |= BIT4 + BIT2; break; + case 6: RegValue |= BIT5 + BIT3; break; + case 5: RegValue |= BIT5 + BIT4 + BIT3 + BIT2; break; + } + if (info->params.parity != ASYNC_PARITY_NONE) { + RegValue |= BIT1; + if (info->params.parity == ASYNC_PARITY_ODD) + RegValue |= BIT0; + } + write_reg(info, MD1, RegValue); + + /* MD2, Mode Register 2 + * + * 07..02 Reserved, must be 0 + * 01..00 CNCT<1..0> Channel connection, 00=normal 11=local loopback + * + * 0000 0000 + */ + RegValue = 0x00; + if (info->params.loopback) + RegValue |= (BIT1 + BIT0); + write_reg(info, MD2, RegValue); + + /* RXS, Receive clock source + * + * 07 Reserved, must be 0 + * 06..04 RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL + * 03..00 RXBR<3..0>, rate divisor, 0000=1 + */ + RegValue=BIT6; + write_reg(info, RXS, RegValue); + + /* TXS, Transmit clock source + * + * 07 Reserved, must be 0 + * 06..04 RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock + * 03..00 RXBR<3..0>, rate divisor, 0000=1 + */ + RegValue=BIT6; + write_reg(info, TXS, RegValue); + + /* Control Register + * + * 6,4,2,0 CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out + */ + info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); + write_control_reg(info); + + tx_set_idle(info); + + /* RRC Receive Ready Control 0 + * + * 07..05 Reserved, must be 0 + * 04..00 RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte + */ + write_reg(info, RRC, 0x00); + + /* TRC0 Transmit Ready Control 0 + * + * 07..05 Reserved, must be 0 + * 04..00 TRC<4..0> Tx FIFO trigger active 0x10 = 16 bytes + */ + write_reg(info, TRC0, 0x10); + + /* TRC1 Transmit Ready Control 1 + * + * 07..05 Reserved, must be 0 + * 04..00 TRC<4..0> Tx FIFO trigger inactive 0x1e = 31 bytes (full-1) + */ + write_reg(info, TRC1, 0x1e); + + /* CTL, MSCI control register + * + * 07..06 Reserved, set to 0 + * 05 UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC) + * 04 IDLC, idle control, 0=mark 1=idle register + * 03 BRK, break, 0=off 1 =on (async) + * 02 SYNCLD, sync char load enable (BSC) 1=enabled + * 01 GOP, go active on poll (LOOP mode) 1=enabled + * 00 RTS, RTS output control, 0=active 1=inactive + * + * 0001 0001 + */ + RegValue = 0x10; + if (!(info->serial_signals & SerialSignal_RTS)) + RegValue |= 0x01; + write_reg(info, CTL, RegValue); + + /* enable status interrupts */ + info->ie0_value |= TXINTE + RXINTE; + write_reg(info, IE0, info->ie0_value); + + /* enable break detect interrupt */ + info->ie1_value = BRKD; + write_reg(info, IE1, info->ie1_value); + + /* enable rx overrun interrupt */ + info->ie2_value = OVRN; + write_reg(info, IE2, info->ie2_value); + + set_rate( info, info->params.data_rate * 16 ); +} + +/* Program the SCA for HDLC communications. + */ +static void hdlc_mode(SLMP_INFO *info) +{ + unsigned char RegValue; + u32 DpllDivisor; + + // Can't use DPLL because SCA outputs recovered clock on RxC when + // DPLL mode selected. This causes output contention with RxC receiver. + // Use of DPLL would require external hardware to disable RxC receiver + // when DPLL mode selected. + info->params.flags &= ~(HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL); + + /* disable DMA interrupts */ + write_reg(info, TXDMA + DIR, 0); + write_reg(info, RXDMA + DIR, 0); + + /* MD0, Mode Register 0 + * + * 07..05 PRCTL<2..0>, Protocol Mode, 100=HDLC + * 04 AUTO, Auto-enable (RTS/CTS/DCD) + * 03 Reserved, must be 0 + * 02 CRCCC, CRC Calculation, 1=enabled + * 01 CRC1, CRC selection, 0=CRC-16,1=CRC-CCITT-16 + * 00 CRC0, CRC initial value, 1 = all 1s + * + * 1000 0001 + */ + RegValue = 0x81; + if (info->params.flags & HDLC_FLAG_AUTO_CTS) + RegValue |= BIT4; + if (info->params.flags & HDLC_FLAG_AUTO_DCD) + RegValue |= BIT4; + if (info->params.crc_type == HDLC_CRC_16_CCITT) + RegValue |= BIT2 + BIT1; + write_reg(info, MD0, RegValue); + + /* MD1, Mode Register 1 + * + * 07..06 ADDRS<1..0>, Address detect, 00=no addr check + * 05..04 TXCHR<1..0>, tx char size, 00=8 bits + * 03..02 RXCHR<1..0>, rx char size, 00=8 bits + * 01..00 PMPM<1..0>, Parity mode, 00=no parity + * + * 0000 0000 + */ + RegValue = 0x00; + write_reg(info, MD1, RegValue); + + /* MD2, Mode Register 2 + * + * 07 NRZFM, 0=NRZ, 1=FM + * 06..05 CODE<1..0> Encoding, 00=NRZ + * 04..03 DRATE<1..0> DPLL Divisor, 00=8 + * 02 Reserved, must be 0 + * 01..00 CNCT<1..0> Channel connection, 0=normal + * + * 0000 0000 + */ + RegValue = 0x00; + switch(info->params.encoding) { + case HDLC_ENCODING_NRZI: RegValue |= BIT5; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT7 + BIT5; break; /* aka FM1 */ + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT7 + BIT6; break; /* aka FM0 */ + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT7; break; /* aka Manchester */ +#if 0 + case HDLC_ENCODING_NRZB: /* not supported */ + case HDLC_ENCODING_NRZI_MARK: /* not supported */ + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: /* not supported */ +#endif + } + if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { + DpllDivisor = 16; + RegValue |= BIT3; + } else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { + DpllDivisor = 8; + } else { + DpllDivisor = 32; + RegValue |= BIT4; + } + write_reg(info, MD2, RegValue); + + + /* RXS, Receive clock source + * + * 07 Reserved, must be 0 + * 06..04 RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL + * 03..00 RXBR<3..0>, rate divisor, 0000=1 + */ + RegValue=0; + if (info->params.flags & HDLC_FLAG_RXC_BRG) + RegValue |= BIT6; + if (info->params.flags & HDLC_FLAG_RXC_DPLL) + RegValue |= BIT6 + BIT5; + write_reg(info, RXS, RegValue); + + /* TXS, Transmit clock source + * + * 07 Reserved, must be 0 + * 06..04 RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock + * 03..00 RXBR<3..0>, rate divisor, 0000=1 + */ + RegValue=0; + if (info->params.flags & HDLC_FLAG_TXC_BRG) + RegValue |= BIT6; + if (info->params.flags & HDLC_FLAG_TXC_DPLL) + RegValue |= BIT6 + BIT5; + write_reg(info, TXS, RegValue); + + if (info->params.flags & HDLC_FLAG_RXC_DPLL) + set_rate(info, info->params.clock_speed * DpllDivisor); + else + set_rate(info, info->params.clock_speed); + + /* GPDATA (General Purpose I/O Data Register) + * + * 6,4,2,0 CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out + */ + if (info->params.flags & HDLC_FLAG_TXC_BRG) + info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2)); + else + info->port_array[0]->ctrlreg_value &= ~(BIT0 << (info->port_num * 2)); + write_control_reg(info); + + /* RRC Receive Ready Control 0 + * + * 07..05 Reserved, must be 0 + * 04..00 RRC<4..0> Rx FIFO trigger active + */ + write_reg(info, RRC, rx_active_fifo_level); + + /* TRC0 Transmit Ready Control 0 + * + * 07..05 Reserved, must be 0 + * 04..00 TRC<4..0> Tx FIFO trigger active + */ + write_reg(info, TRC0, tx_active_fifo_level); + + /* TRC1 Transmit Ready Control 1 + * + * 07..05 Reserved, must be 0 + * 04..00 TRC<4..0> Tx FIFO trigger inactive 0x1f = 32 bytes (full) + */ + write_reg(info, TRC1, (unsigned char)(tx_negate_fifo_level - 1)); + + /* DMR, DMA Mode Register + * + * 07..05 Reserved, must be 0 + * 04 TMOD, Transfer Mode: 1=chained-block + * 03 Reserved, must be 0 + * 02 NF, Number of Frames: 1=multi-frame + * 01 CNTE, Frame End IRQ Counter enable: 0=disabled + * 00 Reserved, must be 0 + * + * 0001 0100 + */ + write_reg(info, TXDMA + DMR, 0x14); + write_reg(info, RXDMA + DMR, 0x14); + + /* Set chain pointer base (upper 8 bits of 24 bit addr) */ + write_reg(info, RXDMA + CPB, + (unsigned char)(info->buffer_list_phys >> 16)); + + /* Set chain pointer base (upper 8 bits of 24 bit addr) */ + write_reg(info, TXDMA + CPB, + (unsigned char)(info->buffer_list_phys >> 16)); + + /* enable status interrupts. other code enables/disables + * the individual sources for these two interrupt classes. + */ + info->ie0_value |= TXINTE + RXINTE; + write_reg(info, IE0, info->ie0_value); + + /* CTL, MSCI control register + * + * 07..06 Reserved, set to 0 + * 05 UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC) + * 04 IDLC, idle control, 0=mark 1=idle register + * 03 BRK, break, 0=off 1 =on (async) + * 02 SYNCLD, sync char load enable (BSC) 1=enabled + * 01 GOP, go active on poll (LOOP mode) 1=enabled + * 00 RTS, RTS output control, 0=active 1=inactive + * + * 0001 0001 + */ + RegValue = 0x10; + if (!(info->serial_signals & SerialSignal_RTS)) + RegValue |= 0x01; + write_reg(info, CTL, RegValue); + + /* preamble not supported ! */ + + tx_set_idle(info); + tx_stop(info); + rx_stop(info); + + set_rate(info, info->params.clock_speed); + + if (info->params.loopback) + enable_loopback(info,1); +} + +/* Set the transmit HDLC idle mode + */ +static void tx_set_idle(SLMP_INFO *info) +{ + unsigned char RegValue = 0xff; + + /* Map API idle mode to SCA register bits */ + switch(info->idle_mode) { + case HDLC_TXIDLE_FLAGS: RegValue = 0x7e; break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: RegValue = 0xaa; break; + case HDLC_TXIDLE_ZEROS: RegValue = 0x00; break; + case HDLC_TXIDLE_ONES: RegValue = 0xff; break; + case HDLC_TXIDLE_ALT_MARK_SPACE: RegValue = 0xaa; break; + case HDLC_TXIDLE_SPACE: RegValue = 0x00; break; + case HDLC_TXIDLE_MARK: RegValue = 0xff; break; + } + + write_reg(info, IDL, RegValue); +} + +/* Query the adapter for the state of the V24 status (input) signals. + */ +static void get_signals(SLMP_INFO *info) +{ + u16 status = read_reg(info, SR3); + u16 gpstatus = read_status_reg(info); + u16 testbit; + + /* clear all serial signals except DTR and RTS */ + info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + + /* set serial signal bits to reflect MISR */ + + if (!(status & BIT3)) + info->serial_signals |= SerialSignal_CTS; + + if ( !(status & BIT2)) + info->serial_signals |= SerialSignal_DCD; + + testbit = BIT1 << (info->port_num * 2); // Port 0..3 RI is GPDATA<1,3,5,7> + if (!(gpstatus & testbit)) + info->serial_signals |= SerialSignal_RI; + + testbit = BIT0 << (info->port_num * 2); // Port 0..3 DSR is GPDATA<0,2,4,6> + if (!(gpstatus & testbit)) + info->serial_signals |= SerialSignal_DSR; +} + +/* Set the state of DTR and RTS based on contents of + * serial_signals member of device context. + */ +static void set_signals(SLMP_INFO *info) +{ + unsigned char RegValue; + u16 EnableBit; + + RegValue = read_reg(info, CTL); + if (info->serial_signals & SerialSignal_RTS) + RegValue &= ~BIT0; + else + RegValue |= BIT0; + write_reg(info, CTL, RegValue); + + // Port 0..3 DTR is ctrl reg <1,3,5,7> + EnableBit = BIT1 << (info->port_num*2); + if (info->serial_signals & SerialSignal_DTR) + info->port_array[0]->ctrlreg_value &= ~EnableBit; + else + info->port_array[0]->ctrlreg_value |= EnableBit; + write_control_reg(info); +} + +/*******************/ +/* DMA Buffer Code */ +/*******************/ + +/* Set the count for all receive buffers to SCABUFSIZE + * and set the current buffer to the first buffer. This effectively + * makes all buffers free and discards any data in buffers. + */ +static void rx_reset_buffers(SLMP_INFO *info) +{ + rx_free_frame_buffers(info, 0, info->rx_buf_count - 1); +} + +/* Free the buffers used by a received frame + * + * info pointer to device instance data + * first index of 1st receive buffer of frame + * last index of last receive buffer of frame + */ +static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last) +{ + bool done = false; + + while(!done) { + /* reset current buffer for reuse */ + info->rx_buf_list[first].status = 0xff; + + if (first == last) { + done = true; + /* set new last rx descriptor address */ + write_reg16(info, RXDMA + EDA, info->rx_buf_list_ex[first].phys_entry); + } + + first++; + if (first == info->rx_buf_count) + first = 0; + } + + /* set current buffer to next buffer after last buffer of frame */ + info->current_rx_buf = first; +} + +/* Return a received frame from the receive DMA buffers. + * Only frames received without errors are returned. + * + * Return Value: true if frame returned, otherwise false + */ +static bool rx_get_frame(SLMP_INFO *info) +{ + unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ + unsigned short status; + unsigned int framesize = 0; + bool ReturnCode = false; + unsigned long flags; + struct tty_struct *tty = info->port.tty; + unsigned char addr_field = 0xff; + SCADESC *desc; + SCADESC_EX *desc_ex; + +CheckAgain: + /* assume no frame returned, set zero length */ + framesize = 0; + addr_field = 0xff; + + /* + * current_rx_buf points to the 1st buffer of the next available + * receive frame. To find the last buffer of the frame look for + * a non-zero status field in the buffer entries. (The status + * field is set by the 16C32 after completing a receive frame. + */ + StartIndex = EndIndex = info->current_rx_buf; + + for ( ;; ) { + desc = &info->rx_buf_list[EndIndex]; + desc_ex = &info->rx_buf_list_ex[EndIndex]; + + if (desc->status == 0xff) + goto Cleanup; /* current desc still in use, no frames available */ + + if (framesize == 0 && info->params.addr_filter != 0xff) + addr_field = desc_ex->virt_addr[0]; + + framesize += desc->length; + + /* Status != 0 means last buffer of frame */ + if (desc->status) + break; + + EndIndex++; + if (EndIndex == info->rx_buf_count) + EndIndex = 0; + + if (EndIndex == info->current_rx_buf) { + /* all buffers have been 'used' but none mark */ + /* the end of a frame. Reset buffers and receiver. */ + if ( info->rx_enabled ){ + spin_lock_irqsave(&info->lock,flags); + rx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } + goto Cleanup; + } + + } + + /* check status of receive frame */ + + /* frame status is byte stored after frame data + * + * 7 EOM (end of msg), 1 = last buffer of frame + * 6 Short Frame, 1 = short frame + * 5 Abort, 1 = frame aborted + * 4 Residue, 1 = last byte is partial + * 3 Overrun, 1 = overrun occurred during frame reception + * 2 CRC, 1 = CRC error detected + * + */ + status = desc->status; + + /* ignore CRC bit if not using CRC (bit is undefined) */ + /* Note:CRC is not save to data buffer */ + if (info->params.crc_type == HDLC_CRC_NONE) + status &= ~BIT2; + + if (framesize == 0 || + (addr_field != 0xff && addr_field != info->params.addr_filter)) { + /* discard 0 byte frames, this seems to occur sometime + * when remote is idling flags. + */ + rx_free_frame_buffers(info, StartIndex, EndIndex); + goto CheckAgain; + } + + if (framesize < 2) + status |= BIT6; + + if (status & (BIT6+BIT5+BIT3+BIT2)) { + /* received frame has errors, + * update counts and mark frame size as 0 + */ + if (status & BIT6) + info->icount.rxshort++; + else if (status & BIT5) + info->icount.rxabort++; + else if (status & BIT3) + info->icount.rxover++; + else + info->icount.rxcrc++; + + framesize = 0; +#if SYNCLINK_GENERIC_HDLC + { + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; + } +#endif + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk("%s(%d):%s rx_get_frame() status=%04X size=%d\n", + __FILE__,__LINE__,info->device_name,status,framesize); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr, + min_t(int, framesize,SCABUFSIZE),0); + + if (framesize) { + if (framesize > info->max_frame_size) + info->icount.rxlong++; + else { + /* copy dma buffer(s) to contiguous intermediate buffer */ + int copy_count = framesize; + int index = StartIndex; + unsigned char *ptmp = info->tmp_rx_buf; + info->tmp_rx_buf_count = framesize; + + info->icount.rxok++; + + while(copy_count) { + int partial_count = min(copy_count,SCABUFSIZE); + memcpy( ptmp, + info->rx_buf_list_ex[index].virt_addr, + partial_count ); + ptmp += partial_count; + copy_count -= partial_count; + + if ( ++index == info->rx_buf_count ) + index = 0; + } + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_rx(info,info->tmp_rx_buf,framesize); + else +#endif + ldisc_receive_buf(tty,info->tmp_rx_buf, + info->flag_buf, framesize); + } + } + /* Free the buffers used by this frame. */ + rx_free_frame_buffers( info, StartIndex, EndIndex ); + + ReturnCode = true; + +Cleanup: + if ( info->rx_enabled && info->rx_overflow ) { + /* Receiver is enabled, but needs to restarted due to + * rx buffer overflow. If buffers are empty, restart receiver. + */ + if (info->rx_buf_list[EndIndex].status == 0xff) { + spin_lock_irqsave(&info->lock,flags); + rx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + } + } + + return ReturnCode; +} + +/* load the transmit DMA buffer with data + */ +static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count) +{ + unsigned short copy_count; + unsigned int i = 0; + SCADESC *desc; + SCADESC_EX *desc_ex; + + if ( debug_level >= DEBUG_LEVEL_DATA ) + trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1); + + /* Copy source buffer to one or more DMA buffers, starting with + * the first transmit dma buffer. + */ + for(i=0;;) + { + copy_count = min_t(unsigned short,count,SCABUFSIZE); + + desc = &info->tx_buf_list[i]; + desc_ex = &info->tx_buf_list_ex[i]; + + load_pci_memory(info, desc_ex->virt_addr,buf,copy_count); + + desc->length = copy_count; + desc->status = 0; + + buf += copy_count; + count -= copy_count; + + if (!count) + break; + + i++; + if (i >= info->tx_buf_count) + i = 0; + } + + info->tx_buf_list[i].status = 0x81; /* set EOM and EOT status */ + info->last_tx_buf = ++i; +} + +static bool register_test(SLMP_INFO *info) +{ + static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96}; + static unsigned int count = ARRAY_SIZE(testval); + unsigned int i; + bool rc = true; + unsigned long flags; + + spin_lock_irqsave(&info->lock,flags); + reset_port(info); + + /* assume failure */ + info->init_error = DiagStatus_AddressFailure; + + /* Write bit patterns to various registers but do it out of */ + /* sync, then read back and verify values. */ + + for (i = 0 ; i < count ; i++) { + write_reg(info, TMC, testval[i]); + write_reg(info, IDL, testval[(i+1)%count]); + write_reg(info, SA0, testval[(i+2)%count]); + write_reg(info, SA1, testval[(i+3)%count]); + + if ( (read_reg(info, TMC) != testval[i]) || + (read_reg(info, IDL) != testval[(i+1)%count]) || + (read_reg(info, SA0) != testval[(i+2)%count]) || + (read_reg(info, SA1) != testval[(i+3)%count]) ) + { + rc = false; + break; + } + } + + reset_port(info); + spin_unlock_irqrestore(&info->lock,flags); + + return rc; +} + +static bool irq_test(SLMP_INFO *info) +{ + unsigned long timeout; + unsigned long flags; + + unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0; + + spin_lock_irqsave(&info->lock,flags); + reset_port(info); + + /* assume failure */ + info->init_error = DiagStatus_IrqFailure; + info->irq_occurred = false; + + /* setup timer0 on SCA0 to interrupt */ + + /* IER2<7..4> = timer<3..0> interrupt enables (1=enabled) */ + write_reg(info, IER2, (unsigned char)((info->port_num & 1) ? BIT6 : BIT4)); + + write_reg(info, (unsigned char)(timer + TEPR), 0); /* timer expand prescale */ + write_reg16(info, (unsigned char)(timer + TCONR), 1); /* timer constant */ + + + /* TMCS, Timer Control/Status Register + * + * 07 CMF, Compare match flag (read only) 1=match + * 06 ECMI, CMF Interrupt Enable: 1=enabled + * 05 Reserved, must be 0 + * 04 TME, Timer Enable + * 03..00 Reserved, must be 0 + * + * 0101 0000 + */ + write_reg(info, (unsigned char)(timer + TMCS), 0x50); + + spin_unlock_irqrestore(&info->lock,flags); + + timeout=100; + while( timeout-- && !info->irq_occurred ) { + msleep_interruptible(10); + } + + spin_lock_irqsave(&info->lock,flags); + reset_port(info); + spin_unlock_irqrestore(&info->lock,flags); + + return info->irq_occurred; +} + +/* initialize individual SCA device (2 ports) + */ +static bool sca_init(SLMP_INFO *info) +{ + /* set wait controller to single mem partition (low), no wait states */ + write_reg(info, PABR0, 0); /* wait controller addr boundary 0 */ + write_reg(info, PABR1, 0); /* wait controller addr boundary 1 */ + write_reg(info, WCRL, 0); /* wait controller low range */ + write_reg(info, WCRM, 0); /* wait controller mid range */ + write_reg(info, WCRH, 0); /* wait controller high range */ + + /* DPCR, DMA Priority Control + * + * 07..05 Not used, must be 0 + * 04 BRC, bus release condition: 0=all transfers complete + * 03 CCC, channel change condition: 0=every cycle + * 02..00 PR<2..0>, priority 100=round robin + * + * 00000100 = 0x04 + */ + write_reg(info, DPCR, dma_priority); + + /* DMA Master Enable, BIT7: 1=enable all channels */ + write_reg(info, DMER, 0x80); + + /* enable all interrupt classes */ + write_reg(info, IER0, 0xff); /* TxRDY,RxRDY,TxINT,RxINT (ports 0-1) */ + write_reg(info, IER1, 0xff); /* DMIB,DMIA (channels 0-3) */ + write_reg(info, IER2, 0xf0); /* TIRQ (timers 0-3) */ + + /* ITCR, interrupt control register + * 07 IPC, interrupt priority, 0=MSCI->DMA + * 06..05 IAK<1..0>, Acknowledge cycle, 00=non-ack cycle + * 04 VOS, Vector Output, 0=unmodified vector + * 03..00 Reserved, must be 0 + */ + write_reg(info, ITCR, 0); + + return true; +} + +/* initialize adapter hardware + */ +static bool init_adapter(SLMP_INFO *info) +{ + int i; + + /* Set BIT30 of Local Control Reg 0x50 to reset SCA */ + volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); + u32 readval; + + info->misc_ctrl_value |= BIT30; + *MiscCtrl = info->misc_ctrl_value; + + /* + * Force at least 170ns delay before clearing + * reset bit. Each read from LCR takes at least + * 30ns so 10 times for 300ns to be safe. + */ + for(i=0;i<10;i++) + readval = *MiscCtrl; + + info->misc_ctrl_value &= ~BIT30; + *MiscCtrl = info->misc_ctrl_value; + + /* init control reg (all DTRs off, all clksel=input) */ + info->ctrlreg_value = 0xaa; + write_control_reg(info); + + { + volatile u32 *LCR1BRDR = (u32 *)(info->lcr_base + 0x2c); + lcr1_brdr_value &= ~(BIT5 + BIT4 + BIT3); + + switch(read_ahead_count) + { + case 16: + lcr1_brdr_value |= BIT5 + BIT4 + BIT3; + break; + case 8: + lcr1_brdr_value |= BIT5 + BIT4; + break; + case 4: + lcr1_brdr_value |= BIT5 + BIT3; + break; + case 0: + lcr1_brdr_value |= BIT5; + break; + } + + *LCR1BRDR = lcr1_brdr_value; + *MiscCtrl = misc_ctrl_value; + } + + sca_init(info->port_array[0]); + sca_init(info->port_array[2]); + + return true; +} + +/* Loopback an HDLC frame to test the hardware + * interrupt and DMA functions. + */ +static bool loopback_test(SLMP_INFO *info) +{ +#define TESTFRAMESIZE 20 + + unsigned long timeout; + u16 count = TESTFRAMESIZE; + unsigned char buf[TESTFRAMESIZE]; + bool rc = false; + unsigned long flags; + + struct tty_struct *oldtty = info->port.tty; + u32 speed = info->params.clock_speed; + + info->params.clock_speed = 3686400; + info->port.tty = NULL; + + /* assume failure */ + info->init_error = DiagStatus_DmaFailure; + + /* build and send transmit frame */ + for (count = 0; count < TESTFRAMESIZE;++count) + buf[count] = (unsigned char)count; + + memset(info->tmp_rx_buf,0,TESTFRAMESIZE); + + /* program hardware for HDLC and enabled receiver */ + spin_lock_irqsave(&info->lock,flags); + hdlc_mode(info); + enable_loopback(info,1); + rx_start(info); + info->tx_count = count; + tx_load_dma_buffer(info,buf,count); + tx_start(info); + spin_unlock_irqrestore(&info->lock,flags); + + /* wait for receive complete */ + /* Set a timeout for waiting for interrupt. */ + for ( timeout = 100; timeout; --timeout ) { + msleep_interruptible(10); + + if (rx_get_frame(info)) { + rc = true; + break; + } + } + + /* verify received frame length and contents */ + if (rc && + ( info->tmp_rx_buf_count != count || + memcmp(buf, info->tmp_rx_buf,count))) { + rc = false; + } + + spin_lock_irqsave(&info->lock,flags); + reset_adapter(info); + spin_unlock_irqrestore(&info->lock,flags); + + info->params.clock_speed = speed; + info->port.tty = oldtty; + + return rc; +} + +/* Perform diagnostics on hardware + */ +static int adapter_test( SLMP_INFO *info ) +{ + unsigned long flags; + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):Testing device %s\n", + __FILE__,__LINE__,info->device_name ); + + spin_lock_irqsave(&info->lock,flags); + init_adapter(info); + spin_unlock_irqrestore(&info->lock,flags); + + info->port_array[0]->port_count = 0; + + if ( register_test(info->port_array[0]) && + register_test(info->port_array[1])) { + + info->port_array[0]->port_count = 2; + + if ( register_test(info->port_array[2]) && + register_test(info->port_array[3]) ) + info->port_array[0]->port_count += 2; + } + else { + printk( "%s(%d):Register test failure for device %s Addr=%08lX\n", + __FILE__,__LINE__,info->device_name, (unsigned long)(info->phys_sca_base)); + return -ENODEV; + } + + if ( !irq_test(info->port_array[0]) || + !irq_test(info->port_array[1]) || + (info->port_count == 4 && !irq_test(info->port_array[2])) || + (info->port_count == 4 && !irq_test(info->port_array[3]))) { + printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); + return -ENODEV; + } + + if (!loopback_test(info->port_array[0]) || + !loopback_test(info->port_array[1]) || + (info->port_count == 4 && !loopback_test(info->port_array[2])) || + (info->port_count == 4 && !loopback_test(info->port_array[3]))) { + printk( "%s(%d):DMA test failure for device %s\n", + __FILE__,__LINE__,info->device_name); + return -ENODEV; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):device %s passed diagnostics\n", + __FILE__,__LINE__,info->device_name ); + + info->port_array[0]->init_error = 0; + info->port_array[1]->init_error = 0; + if ( info->port_count > 2 ) { + info->port_array[2]->init_error = 0; + info->port_array[3]->init_error = 0; + } + + return 0; +} + +/* Test the shared memory on a PCI adapter. + */ +static bool memory_test(SLMP_INFO *info) +{ + static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa, + 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; + unsigned long count = ARRAY_SIZE(testval); + unsigned long i; + unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long); + unsigned long * addr = (unsigned long *)info->memory_base; + + /* Test data lines with test pattern at one location. */ + + for ( i = 0 ; i < count ; i++ ) { + *addr = testval[i]; + if ( *addr != testval[i] ) + return false; + } + + /* Test address lines with incrementing pattern over */ + /* entire address range. */ + + for ( i = 0 ; i < limit ; i++ ) { + *addr = i * 4; + addr++; + } + + addr = (unsigned long *)info->memory_base; + + for ( i = 0 ; i < limit ; i++ ) { + if ( *addr != i * 4 ) + return false; + addr++; + } + + memset( info->memory_base, 0, SCA_MEM_SIZE ); + return true; +} + +/* Load data into PCI adapter shared memory. + * + * The PCI9050 releases control of the local bus + * after completing the current read or write operation. + * + * While the PCI9050 write FIFO not empty, the + * PCI9050 treats all of the writes as a single transaction + * and does not release the bus. This causes DMA latency problems + * at high speeds when copying large data blocks to the shared memory. + * + * This function breaks a write into multiple transations by + * interleaving a read which flushes the write FIFO and 'completes' + * the write transation. This allows any pending DMA request to gain control + * of the local bus in a timely fasion. + */ +static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count) +{ + /* A load interval of 16 allows for 4 32-bit writes at */ + /* 136ns each for a maximum latency of 542ns on the local bus.*/ + + unsigned short interval = count / sca_pci_load_interval; + unsigned short i; + + for ( i = 0 ; i < interval ; i++ ) + { + memcpy(dest, src, sca_pci_load_interval); + read_status_reg(info); + dest += sca_pci_load_interval; + src += sca_pci_load_interval; + } + + memcpy(dest, src, count % sca_pci_load_interval); +} + +static void trace_block(SLMP_INFO *info,const char* data, int count, int xmit) +{ + int i; + int linecount; + if (xmit) + printk("%s tx data:\n",info->device_name); + else + printk("%s rx data:\n",info->device_name); + + while(count) { + if (count > 16) + linecount = 16; + else + linecount = count; + + for(i=0;i=040 && data[i]<=0176) + printk("%c",data[i]); + else + printk("."); + } + printk("\n"); + + data += linecount; + count -= linecount; + } +} /* end of trace_block() */ + +/* called when HDLC frame times out + * update stats and do tx completion processing + */ +static void tx_timeout(unsigned long context) +{ + SLMP_INFO *info = (SLMP_INFO*)context; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):%s tx_timeout()\n", + __FILE__,__LINE__,info->device_name); + if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { + info->icount.txtimeout++; + } + spin_lock_irqsave(&info->lock,flags); + info->tx_active = false; + info->tx_count = info->tx_put = info->tx_get = 0; + + spin_unlock_irqrestore(&info->lock,flags); + +#if SYNCLINK_GENERIC_HDLC + if (info->netcount) + hdlcdev_tx_done(info); + else +#endif + bh_transmit(info); +} + +/* called to periodically check the DSR/RI modem signal input status + */ +static void status_timeout(unsigned long context) +{ + u16 status = 0; + SLMP_INFO *info = (SLMP_INFO*)context; + unsigned long flags; + unsigned char delta; + + + spin_lock_irqsave(&info->lock,flags); + get_signals(info); + spin_unlock_irqrestore(&info->lock,flags); + + /* check for DSR/RI state change */ + + delta = info->old_signals ^ info->serial_signals; + info->old_signals = info->serial_signals; + + if (delta & SerialSignal_DSR) + status |= MISCSTATUS_DSR_LATCHED|(info->serial_signals&SerialSignal_DSR); + + if (delta & SerialSignal_RI) + status |= MISCSTATUS_RI_LATCHED|(info->serial_signals&SerialSignal_RI); + + if (delta & SerialSignal_DCD) + status |= MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD); + + if (delta & SerialSignal_CTS) + status |= MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS); + + if (status) + isr_io_pin(info,status); + + mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10)); +} + + +/* Register Access Routines - + * All registers are memory mapped + */ +#define CALC_REGADDR() \ + unsigned char * RegAddr = (unsigned char*)(info->sca_base + Addr); \ + if (info->port_num > 1) \ + RegAddr += 256; /* port 0-1 SCA0, 2-3 SCA1 */ \ + if ( info->port_num & 1) { \ + if (Addr > 0x7f) \ + RegAddr += 0x40; /* DMA access */ \ + else if (Addr > 0x1f && Addr < 0x60) \ + RegAddr += 0x20; /* MSCI access */ \ + } + + +static unsigned char read_reg(SLMP_INFO * info, unsigned char Addr) +{ + CALC_REGADDR(); + return *RegAddr; +} +static void write_reg(SLMP_INFO * info, unsigned char Addr, unsigned char Value) +{ + CALC_REGADDR(); + *RegAddr = Value; +} + +static u16 read_reg16(SLMP_INFO * info, unsigned char Addr) +{ + CALC_REGADDR(); + return *((u16 *)RegAddr); +} + +static void write_reg16(SLMP_INFO * info, unsigned char Addr, u16 Value) +{ + CALC_REGADDR(); + *((u16 *)RegAddr) = Value; +} + +static unsigned char read_status_reg(SLMP_INFO * info) +{ + unsigned char *RegAddr = (unsigned char *)info->statctrl_base; + return *RegAddr; +} + +static void write_control_reg(SLMP_INFO * info) +{ + unsigned char *RegAddr = (unsigned char *)info->statctrl_base; + *RegAddr = info->port_array[0]->ctrlreg_value; +} + + +static int __devinit synclinkmp_init_one (struct pci_dev *dev, + const struct pci_device_id *ent) +{ + if (pci_enable_device(dev)) { + printk("error enabling pci device %p\n", dev); + return -EIO; + } + device_init( ++synclinkmp_adapter_count, dev ); + return 0; +} + +static void __devexit synclinkmp_remove_one (struct pci_dev *dev) +{ +} -- cgit v1.2.3