patch-2.1.29 linux/drivers/char/stallion.c
Next file: linux/drivers/char/tty_ioctl.c
Previous file: linux/drivers/char/serial.c
Back to the patch index
Back to the overall index
- Lines: 4747
- Date:
Tue Mar 4 13:33:21 1997
- Orig file:
v2.1.28/linux/drivers/char/stallion.c
- Orig date:
Sat Nov 30 00:48:34 1996
diff -u --recursive --new-file v2.1.28/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
@@ -3,6 +3,7 @@
/*
* stallion.c -- stallion multiport serial driver.
*
+ * Copyright (C) 1996-1997 Stallion Technologies (support@stallion.oz.au).
* Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
*
* This code is loosely based on the Linux serial driver, written by
@@ -37,12 +38,13 @@
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/cd1400.h>
+#include <linux/sc26198.h>
#include <linux/comstats.h>
#include <linux/stallion.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
-#include <linux/config.h> /* for CONFIG_PCI */
+#include <linux/config.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -55,11 +57,9 @@
/*****************************************************************************/
/*
- * Define different board types. At the moment I have only declared
- * those boards that this driver supports. But I will use the standard
- * "assigned" board numbers. In the future this driver will support
- * some of the other Stallion boards. Currently supported boards are
- * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32.
+ * Define different board types. Use the standard Stallion "assigned"
+ * board numbers. Boards supported in this driver are abbreviated as
+ * EIO = EasyIO and ECH = EasyConnection 8/32.
*/
#define BRD_EASYIO 20
#define BRD_ECH 21
@@ -101,7 +101,8 @@
} stlconf_t;
static stlconf_t stl_brdconf[] = {
- { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+ /*{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },*/
+ { BRD_ECH, 0x2a0, 0x280, 0, 15, 0 },
};
static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
@@ -126,12 +127,11 @@
#define STL_DRVTYPCALLOUT 2
/*
- * I haven't really decided (or measured) what TX buffer size gives
- * a good balance between performance and memory usage. These seem
- * to work pretty well...
+ * Set the TX buffer size. Bigger is better, but we don't want
+ * to chew too much memory with buffers!
*/
-#define STL_TXBUFLOW 256
-#define STL_TXBUFSIZE 2048
+#define STL_TXBUFLOW 512
+#define STL_TXBUFSIZE 4096
/*****************************************************************************/
@@ -140,7 +140,7 @@
* all the local structures required by a serial tty driver.
*/
static char *stl_drvname = "Stallion Multiport Serial Driver";
-static char *stl_drvversion = "1.1.4";
+static char *stl_drvversion = "5.3.2";
static char *stl_serialname = "ttyE";
static char *stl_calloutname = "cue";
@@ -185,6 +185,11 @@
static stlport_t stl_dummyport;
/*
+ * Define global place to put buffer overflow characters.
+ */
+static char stl_unwanted[SC26198_RXFIFOSIZE];
+
+/*
* Keep track of what interrupts we have requested for us.
* We don't need to request an interrupt twice if it is being
* shared with another Stallion board.
@@ -198,7 +203,7 @@
/*
* Per board state flags. Used with the state field of the board struct.
- * Not really much here yet!
+ * Not really much here!
*/
#define BRD_FOUND 0x1
@@ -210,6 +215,7 @@
#define ASYI_TXBUSY 1
#define ASYI_TXLOW 2
#define ASYI_DCDCHANGE 3
+#define ASYI_TXFLOWED 4
/*
* Define an array of board names as printable strings. Handy for
@@ -249,14 +255,21 @@
/*
* Hardware ID bits for the EasyIO and ECH boards. These defines apply
- * to the directly accessible io ports of these boards (not the cd1400
- * uarts - they are in cd1400.h).
+ * to the directly accessible io ports of these boards (not the uarts -
+ * they are in cd1400.h and sc26198.h).
*/
#define EIO_8PORTRS 0x04
#define EIO_4PORTRS 0x05
#define EIO_8PORTDI 0x00
#define EIO_8PORTM 0x06
+#define EIO_MK3 0x03
#define EIO_IDBITMASK 0x07
+
+#define EIO_BRDMASK 0xf0
+#define ID_BRD4 0x10
+#define ID_BRD8 0x20
+#define ID_BRD16 0x30
+
#define EIO_INTRPEND 0x08
#define EIO_INTEDGE 0x00
#define EIO_INTLEVEL 0x08
@@ -278,24 +291,16 @@
#define ECH_PNLSTATUS 2
#define ECH_PNL16PORT 0x20
#define ECH_PNLIDMASK 0x07
+#define ECH_PNLXPID 0x40
#define ECH_PNLINTRPEND 0x80
-#define ECH_ADDR2MASK 0x1e0
-#define EIO_CLK 25000000
-#define EIO_CLK8M 20000000
-#define ECH_CLK EIO_CLK
+#define ECH_ADDR2MASK 0x1e0
/*
- * Define the offsets within the register bank for all io registers.
- * These io address offsets are common to both the EIO and ECH.
+ * Define real Stallion PCI vemdor and device ID.
*/
-#define EREG_ADDR 0
-#define EREG_DATA 4
-#define EREG_RXACK 5
-#define EREG_TXACK 6
-#define EREG_MDACK 7
-
-#define EREG_BANKSIZE 8
+#define PCI_VENDOR_ID_STALLION 0x124d
+#define PCI_DEVICE_ID_ECHPCI832 0x0000
/*
* Define the vector mapping bits for the programmable interrupt board
@@ -326,16 +331,7 @@
outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \
stl_brds[(brdnr)]->ioctrl);
-/*
- * Define the cd1400 baud rate clocks. These are used when calculating
- * what clock and divisor to use for the required baud rate. Also
- * define the maximum baud rate allowed, and the default base baud.
- */
-static int stl_cd1400clkdivs[] = {
- CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
-};
-
-#define STL_MAXBAUD 230400
+#define STL_MAXBAUD 921600
#define STL_BAUDBASE 115200
#define STL_CLOSEDELAY 50
@@ -349,16 +345,14 @@
/*
* Define a baud rate table that converts termios baud rate selector
- * into the actual baud rate value. All baud rate calculates are based
- * on the actual baud rate required.
+ * into the actual baud rate value. All baud rate calculations are
+ * based on the actual baud rate required.
*/
static unsigned int stl_baudrates[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
};
-/*****************************************************************************/
-
/*
* Define some handy local macros...
*/
@@ -408,20 +402,13 @@
static int stl_clrportstats(stlport_t *portp, comstats_t *cp);
static int stl_getportstruct(unsigned long arg);
static int stl_getbrdstruct(unsigned long arg);
-static void stl_setreg(stlport_t *portp, int regnr, int value);
-static int stl_getreg(stlport_t *portp, int regnr);
-static int stl_updatereg(stlport_t *portp, int regnr, int value);
-static void stl_setport(stlport_t *portp, struct termios *tiosp);
-static int stl_getsignals(stlport_t *portp);
-static void stl_setsignals(stlport_t *portp, int dtr, int rts);
-static void stl_ccrwait(stlport_t *portp);
-static void stl_enablerxtx(stlport_t *portp, int rx, int tx);
-static void stl_startrxtx(stlport_t *portp, int rx, int tx);
-static void stl_disableintrs(stlport_t *portp);
-static void stl_sendbreak(stlport_t *portp, long len);
static int stl_waitcarrier(stlport_t *portp, struct file *filp);
static void stl_delay(int len);
static void stl_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void stl_eiointr(stlbrd_t *brdp);
+static void stl_echatintr(stlbrd_t *brdp);
+static void stl_echmcaintr(stlbrd_t *brdp);
+static void stl_echpciintr(stlbrd_t *brdp);
static void stl_offintr(void *private);
static void *stl_memalloc(int len);
static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
@@ -430,6 +417,177 @@
static int stl_findpcibrds(void);
#endif
+/*
+ * CD1400 uart specific handling functions.
+ */
+static void stl_cd1400setreg(stlport_t *portp, int regnr, int value);
+static int stl_cd1400getreg(stlport_t *portp, int regnr);
+static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value);
+static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp);
+static int stl_cd1400getsignals(stlport_t *portp);
+static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts);
+static void stl_cd1400ccrwait(stlport_t *portp);
+static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx);
+static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx);
+static void stl_cd1400disableintrs(stlport_t *portp);
+static void stl_cd1400sendbreak(stlport_t *portp, long len);
+static void stl_cd1400flowctrl(stlport_t *portp, int state);
+static void stl_cd1400flush(stlport_t *portp);
+static void stl_cd1400intr(stlpanel_t *panelp, unsigned int iobase);
+static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr);
+static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr);
+static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr);
+
+/*
+ * SC26198 uart specific handling functions.
+ */
+static void stl_sc26198setreg(stlport_t *portp, int regnr, int value);
+static int stl_sc26198getreg(stlport_t *portp, int regnr);
+static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value);
+static int stl_sc26198getglobreg(stlport_t *portp, int regnr);
+static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp);
+static int stl_sc26198getsignals(stlport_t *portp);
+static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts);
+static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx);
+static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx);
+static void stl_sc26198disableintrs(stlport_t *portp);
+static void stl_sc26198sendbreak(stlport_t *portp, long len);
+static void stl_sc26198flowctrl(stlport_t *portp, int state);
+static void stl_sc26198flush(stlport_t *portp);
+static void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty);
+static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase);
+static void stl_sc26198txisr(stlport_t *port);
+static void stl_sc26198rxisr(stlport_t *port, unsigned int iack);
+static void stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch);
+static void stl_sc26198rxbadchars(stlport_t *portp);
+static void stl_sc26198otherisr(stlport_t *port, unsigned int iack);
+
+/*****************************************************************************/
+
+/*
+ * Generic UART support structure.
+ */
+typedef struct uart {
+ int (*panelinit)(stlbrd_t *brdp, stlpanel_t *panelp);
+ void (*portinit)(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+ void (*setport)(stlport_t *portp, struct termios *tiosp);
+ int (*getsignals)(stlport_t *portp);
+ void (*setsignals)(stlport_t *portp, int dtr, int rts);
+ void (*enablerxtx)(stlport_t *portp, int rx, int tx);
+ void (*startrxtx)(stlport_t *portp, int rx, int tx);
+ void (*disableintrs)(stlport_t *portp);
+ void (*sendbreak)(stlport_t *portp, long len);
+ void (*flowctrl)(stlport_t *portp, int state);
+ void (*flush)(stlport_t *portp);
+ void (*intr)(stlpanel_t *panelp, unsigned int iobase);
+} uart_t;
+
+/*
+ * Define some macros to make calling these functions nice and clean.
+ */
+#define stl_panelinit (* ((uart_t *) panelp->uartp)->panelinit)
+#define stl_portinit (* ((uart_t *) portp->uartp)->portinit)
+#define stl_setport (* ((uart_t *) portp->uartp)->setport)
+#define stl_getsignals (* ((uart_t *) portp->uartp)->getsignals)
+#define stl_setsignals (* ((uart_t *) portp->uartp)->setsignals)
+#define stl_enablerxtx (* ((uart_t *) portp->uartp)->enablerxtx)
+#define stl_startrxtx (* ((uart_t *) portp->uartp)->startrxtx)
+#define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs)
+#define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak)
+#define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl)
+#define stl_flush (* ((uart_t *) portp->uartp)->flush)
+
+/*****************************************************************************/
+
+/*
+ * CD1400 UART specific data initialization.
+ */
+static uart_t stl_cd1400uart = {
+ stl_cd1400panelinit,
+ stl_cd1400portinit,
+ stl_cd1400setport,
+ stl_cd1400getsignals,
+ stl_cd1400setsignals,
+ stl_cd1400enablerxtx,
+ stl_cd1400startrxtx,
+ stl_cd1400disableintrs,
+ stl_cd1400sendbreak,
+ stl_cd1400flowctrl,
+ stl_cd1400flush,
+ stl_cd1400intr
+};
+
+/*
+ * Define the offsets within the register bank of a cd1400 based panel.
+ * These io address offsets are common to the EasyIO board as well.
+ */
+#define EREG_ADDR 0
+#define EREG_DATA 4
+#define EREG_RXACK 5
+#define EREG_TXACK 6
+#define EREG_MDACK 7
+
+#define EREG_BANKSIZE 8
+
+#define CD1400_CLK 25000000
+#define CD1400_CLK8M 20000000
+
+/*
+ * Define the cd1400 baud rate clocks. These are used when calculating
+ * what clock and divisor to use for the required baud rate. Also
+ * define the maximum baud rate allowed, and the default base baud.
+ */
+static int stl_cd1400clkdivs[] = {
+ CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+/*****************************************************************************/
+
+/*
+ * SC26198 UART specific data initization.
+ */
+static uart_t stl_sc26198uart = {
+ stl_sc26198panelinit,
+ stl_sc26198portinit,
+ stl_sc26198setport,
+ stl_sc26198getsignals,
+ stl_sc26198setsignals,
+ stl_sc26198enablerxtx,
+ stl_sc26198startrxtx,
+ stl_sc26198disableintrs,
+ stl_sc26198sendbreak,
+ stl_sc26198flowctrl,
+ stl_sc26198flush,
+ stl_sc26198intr
+};
+
+/*
+ * Define the offsets within the register bank of a sc26198 based panel.
+ */
+#define XP_DATA 0
+#define XP_ADDR 1
+#define XP_MODID 2
+#define XP_STATUS 2
+#define XP_IACK 3
+
+#define XP_BANKSIZE 4
+
+/*
+ * Define the sc26198 baud rate table. Offsets within the table
+ * represent the actual baud rate selector of sc26198 registers.
+ */
+static unsigned int sc26198_baudtable[] = {
+ 50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600,
+ 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
+ 230400, 460800, 921600
+};
+
+#define SC26198_NRBAUDS (sizeof(sc26198_baudtable) / sizeof(unsigned int))
+
/*****************************************************************************/
/*
@@ -1066,7 +1224,6 @@
#endif
memset(&sio, 0, sizeof(struct serial_struct));
- sio.type = PORT_CIRRUS;
sio.line = portp->portnr;
sio.port = portp->ioaddr;
sio.flags = portp->flags;
@@ -1074,8 +1231,14 @@
sio.close_delay = portp->close_delay;
sio.closing_wait = portp->closing_wait;
sio.custom_divisor = portp->custom_divisor;
- sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
sio.hub6 = 0;
+ if (portp->uartp == &stl_cd1400uart) {
+ sio.type = PORT_CIRRUS;
+ sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+ } else {
+ sio.type = PORT_UNKNOWN;
+ sio.xmit_fifo_size = SC26198_TXFIFOSIZE;
+ }
brdp = stl_brds[portp->brdnr];
if (brdp != (stlbrd_t *) NULL)
@@ -1122,6 +1285,7 @@
static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
stlport_t *portp;
+ unsigned int ival;
int rc;
#if DEBUG
@@ -1134,6 +1298,12 @@
if (portp == (stlport_t *) NULL)
return(-ENODEV);
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return(-EIO);
+ }
+
rc = 0;
switch (cmd) {
@@ -1156,37 +1326,32 @@
break;
case TIOCSSOFTCAR:
if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
- unsigned int val;
- get_user(val, (unsigned int *) arg);
- tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (val ? CLOCAL : 0);
+ get_user(ival, (unsigned int *) arg);
+ tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
}
break;
case TIOCMGET:
if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
- unsigned int val;
- val = stl_getsignals(portp);
- put_user(val, (unsigned int *) arg);
+ ival = stl_getsignals(portp);
+ put_user(ival, (unsigned int *) arg);
}
break;
case TIOCMBIS:
if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
- unsigned int val;
- get_user(val, (unsigned int *) arg);
- stl_setsignals(portp, ((val & TIOCM_DTR) ? 1 : -1), ((val & TIOCM_RTS) ? 1 : -1));
+ get_user(ival, (unsigned int *) arg);
+ stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1));
}
break;
case TIOCMBIC:
if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
- unsigned int val;
- get_user(val, (unsigned int *) arg);
- stl_setsignals(portp, ((val & TIOCM_DTR) ? 0 : -1), ((val & TIOCM_RTS) ? 0 : -1));
+ get_user(ival, (unsigned int *) arg);
+ stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1));
}
break;
case TIOCMSET:
if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
- unsigned int val;
- get_user(val, (unsigned int *) arg);
- stl_setsignals(portp, ((val & TIOCM_DTR) ? 1 : 0), ((val & TIOCM_RTS) ? 1 : 0));
+ get_user(ival, (unsigned int *) arg);
+ stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0));
}
break;
case TIOCGSERIAL:
@@ -1261,7 +1426,6 @@
static void stl_throttle(struct tty_struct *tty)
{
stlport_t *portp;
- unsigned long flags;
#if DEBUG
printk("stl_throttle(tty=%x)\n", (int) tty);
@@ -1272,24 +1436,7 @@
portp = tty->driver_data;
if (portp == (stlport_t *) NULL)
return;
-
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- if (tty->termios->c_iflag & IXOFF) {
- stl_ccrwait(portp);
- stl_setreg(portp, CCR, CCR_SENDSCHR2);
- portp->stats.rxxoff++;
- stl_ccrwait(portp);
- }
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0));
- stl_setreg(portp, MSVR2, 0);
- portp->stats.rxrtsoff++;
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ stl_flowctrl(portp, 0);
}
/*****************************************************************************/
@@ -1301,7 +1448,6 @@
static void stl_unthrottle(struct tty_struct *tty)
{
stlport_t *portp;
- unsigned long flags;
#if DEBUG
printk("stl_unthrottle(tty=%x)\n", (int) tty);
@@ -1312,30 +1458,7 @@
portp = tty->driver_data;
if (portp == (stlport_t *) NULL)
return;
-
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- if (tty->termios->c_iflag & IXOFF) {
- stl_ccrwait(portp);
- stl_setreg(portp, CCR, CCR_SENDSCHR1);
- portp->stats.rxxon++;
- stl_ccrwait(portp);
- }
-/*
- * Question: should we return RTS to what it was before? It may have
- * been set by an ioctl... Suppose not, since if you have hardware
- * flow control set then it is pretty silly to go and set the RTS line
- * by hand.
- */
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
- stl_setreg(portp, MSVR2, MSVR2_RTS);
- portp->stats.rxrtson++;
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ stl_flowctrl(portp, 1);
}
/*****************************************************************************/
@@ -1358,7 +1481,6 @@
portp = tty->driver_data;
if (portp == (stlport_t *) NULL)
return;
-
stl_startrxtx(portp, -1, 0);
}
@@ -1381,7 +1503,6 @@
portp = tty->driver_data;
if (portp == (stlport_t *) NULL)
return;
-
stl_startrxtx(portp, -1, 1);
}
@@ -1433,7 +1554,6 @@
static void stl_flushbuffer(struct tty_struct *tty)
{
stlport_t *portp;
- unsigned long flags;
#if DEBUG
printk("stl_flushbuffer(tty=%x)\n", (int) tty);
@@ -1445,17 +1565,7 @@
if (portp == (stlport_t *) NULL)
return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- stl_ccrwait(portp);
- stl_setreg(portp, CCR, CCR_TXFLUSHFIFO);
- stl_ccrwait(portp);
- portp->tx.tail = portp->tx.head;
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
-
+ stl_flush(portp);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
@@ -1464,646 +1574,1308 @@
/*****************************************************************************/
/*
- * These functions get/set/update the registers of the cd1400 UARTs.
- * Access to the cd1400 registers is via an address/data io port pair.
- * (Maybe should make this inline...)
+ * All board interrupts are vectored through here first. This code then
+ * calls off to the approrpriate board interrupt handlers.
*/
-static int stl_getreg(stlport_t *portp, int regnr)
+static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
{
- outb((regnr + portp->uartaddr), portp->ioaddr);
- return(inb(portp->ioaddr + EREG_DATA));
-}
+ stlbrd_t *brdp;
+ int i;
-static void stl_setreg(stlport_t *portp, int regnr, int value)
-{
- outb((regnr + portp->uartaddr), portp->ioaddr);
- outb(value, portp->ioaddr + EREG_DATA);
+#if DEBUG
+ printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+#endif
+
+ for (i = 0; (i < stl_nrbrds); i++) {
+ if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
+ continue;
+ if (brdp->state == 0)
+ continue;
+ (* brdp->isr)(brdp);
+ }
}
-static int stl_updatereg(stlport_t *portp, int regnr, int value)
+/*****************************************************************************/
+
+/*
+ * Interrupt service routine for EasyIO board types.
+ */
+
+static void stl_eiointr(stlbrd_t *brdp)
{
- outb((regnr + portp->uartaddr), portp->ioaddr);
- if (inb(portp->ioaddr + EREG_DATA) != value) {
- outb(value, portp->ioaddr + EREG_DATA);
- return(1);
- }
- return(0);
+ stlpanel_t *panelp;
+ unsigned int iobase;
+
+ panelp = brdp->panels[0];
+ iobase = panelp->iobase;
+ while (inb(brdp->iostatus) & EIO_INTRPEND)
+ (* panelp->isr)(panelp, iobase);
}
/*****************************************************************************/
/*
- * Transmit interrupt handler. This has gotta be fast! Handling TX
- * chars is pretty simple, stuff as many as possible from the TX buffer
- * into the cd1400 FIFO. Must also handle TX breaks here, since they
- * are embedded as commands in the data stream. Oh no, had to use a goto!
- * This could be optimized more, will do when I get time...
- * In practice it is possible that interrupts are enabled but that the
- * port has been hung up. Need to handle not having any TX buffer here,
- * this is done by using the side effect that head and tail will also
- * be NULL if the buffer has been freed.
+ * Interrupt service routine for ECH-AT board types.
*/
-static inline void stl_txisr(stlpanel_t *panelp, int ioaddr)
+static void stl_echatintr(stlbrd_t *brdp)
{
- stlport_t *portp;
- int len, stlen;
- char *head, *tail;
- unsigned char ioack, srer;
+ stlpanel_t *panelp;
+ unsigned int ioaddr;
+ int bnknr;
-#if DEBUG
- printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
-#endif
+ outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
- ioack = inb(ioaddr + EREG_TXACK);
- if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
- printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
- return;
+ while (inb(brdp->iostatus) & ECH_INTRPEND) {
+ for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+ ioaddr = brdp->bnkstataddr[bnknr];
+ if (inb(ioaddr) & ECH_PNLINTRPEND) {
+ panelp = brdp->bnk2panel[bnknr];
+ (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+ }
+ }
}
- portp = panelp->ports[(ioack >> 3)];
+
+ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+}
+
+/*****************************************************************************/
/*
- * Unfortunately we need to handle breaks in the data stream, since
- * this is the only way to generate them on the cd1400. Do it now if
- * a break is to be sent.
+ * Interrupt service routine for ECH-MCA board types.
*/
- if (portp->brklen != 0) {
- if (portp->brklen > 0) {
- outb((TDR + portp->uartaddr), ioaddr);
- outb(ETC_CMD, (ioaddr + EREG_DATA));
- outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
- outb(ETC_CMD, (ioaddr + EREG_DATA));
- outb(ETC_DELAY, (ioaddr + EREG_DATA));
- outb(portp->brklen, (ioaddr + EREG_DATA));
- outb(ETC_CMD, (ioaddr + EREG_DATA));
- outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
- portp->brklen = -1;
- goto stl_txalldone;
- } else {
- outb((COR2 + portp->uartaddr), ioaddr);
- outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
- portp->brklen = 0;
+
+static void stl_echmcaintr(stlbrd_t *brdp)
+{
+ stlpanel_t *panelp;
+ unsigned int ioaddr;
+ int bnknr;
+
+ while (inb(brdp->iostatus) & ECH_INTRPEND) {
+ for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+ ioaddr = brdp->bnkstataddr[bnknr];
+ if (inb(ioaddr) & ECH_PNLINTRPEND) {
+ panelp = brdp->bnk2panel[bnknr];
+ (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+ }
}
}
+}
- head = portp->tx.head;
- tail = portp->tx.tail;
- len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
- if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
- set_bit(ASYI_TXLOW, &portp->istate);
- queue_task_irq_off(&portp->tqueue, &tq_scheduler);
- }
+/*****************************************************************************/
- if (len == 0) {
- outb((SRER + portp->uartaddr), ioaddr);
- srer = inb(ioaddr + EREG_DATA);
- if (srer & SRER_TXDATA) {
- srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
- } else {
- srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
- clear_bit(ASYI_TXBUSY, &portp->istate);
- }
- outb(srer, (ioaddr + EREG_DATA));
- } else {
- len = MIN(len, CD1400_TXFIFOSIZE);
- portp->stats.txtotal += len;
- stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
- outb((TDR + portp->uartaddr), ioaddr);
- outsb((ioaddr + EREG_DATA), tail, stlen);
- len -= stlen;
- tail += stlen;
- if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
- tail = portp->tx.buf;
- if (len > 0) {
- outsb((ioaddr + EREG_DATA), tail, len);
- tail += len;
+/*
+ * Interrupt service routine for ECH-PCI board types.
+ */
+
+static void stl_echpciintr(stlbrd_t *brdp)
+{
+ stlpanel_t *panelp;
+ unsigned int ioaddr;
+ int bnknr, recheck;
+
+ while (1) {
+ recheck = 0;
+ for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+ outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl);
+ ioaddr = brdp->bnkstataddr[bnknr];
+ if (inb(ioaddr) & ECH_PNLINTRPEND) {
+ panelp = brdp->bnk2panel[bnknr];
+ (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+ recheck++;
+ }
}
- portp->tx.tail = tail;
+ if (! recheck)
+ break;
}
-
-stl_txalldone:
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
}
/*****************************************************************************/
/*
- * Receive character interrupt handler. Determine if we have good chars
- * or bad chars and then process appropriately. Good chars are easy
- * just shove the lot into the RX buffer and set all status byte to 0.
- * If a bad RX char then process as required. This routine needs to be
- * fast! In practice it is possible that we get an interrupt on a port
- * that is closed. This can happen on hangups - since they completely
- * shutdown a port not in user context. Need to handle this case.
+ * Service an off-level request for some channel.
*/
-static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr)
+static void stl_offintr(void *private)
{
stlport_t *portp;
struct tty_struct *tty;
- unsigned int ioack, len, buflen;
- unsigned char status;
- char ch;
- static char unwanted[CD1400_RXFIFOSIZE];
+ unsigned int oldsigs;
+
+ portp = private;
#if DEBUG
- printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+ printk("stl_offintr(portp=%x)\n", (int) portp);
#endif
- ioack = inb(ioaddr + EREG_RXACK);
- if ((ioack & panelp->ackmask) != 0) {
- printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ if (portp == (stlport_t *) NULL)
return;
- }
- portp = panelp->ports[(ioack >> 3)];
tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return;
- if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
- outb((RDCR + portp->uartaddr), ioaddr);
- len = inb(ioaddr + EREG_DATA);
- if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
- ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
- outb((RDSR + portp->uartaddr), ioaddr);
- insb((ioaddr + EREG_DATA), &unwanted[0], len);
- portp->stats.rxlost += len;
- portp->stats.rxtotal += len;
- } else {
- len = MIN(len, buflen);
- if (len > 0) {
- outb((RDSR + portp->uartaddr), ioaddr);
- insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
- memset(tty->flip.flag_buf_ptr, 0, len);
- tty->flip.flag_buf_ptr += len;
- tty->flip.char_buf_ptr += len;
- tty->flip.count += len;
- tty_schedule_flip(tty);
- portp->stats.rxtotal += len;
- }
- }
- } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
- outb((RDSR + portp->uartaddr), ioaddr);
- status = inb(ioaddr + EREG_DATA);
- ch = inb(ioaddr + EREG_DATA);
- if (status & ST_PARITY)
- portp->stats.rxparity++;
- if (status & ST_FRAMING)
- portp->stats.rxframing++;
- if (status & ST_OVERRUN)
- portp->stats.rxoverrun++;
- if (status & ST_BREAK)
- portp->stats.rxbreaks++;
- if (status & ST_SCHARMASK) {
- if ((status & ST_SCHARMASK) == ST_SCHAR1)
- portp->stats.txxon++;
- if ((status & ST_SCHARMASK) == ST_SCHAR2)
- portp->stats.txxoff++;
- goto stl_rxalldone;
- }
- if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
- if (portp->rxmarkmsk & status) {
- if (status & ST_BREAK) {
- status = TTY_BREAK;
-#ifndef MODULE
- if (portp->flags & ASYNC_SAK) {
- do_SAK(tty);
- BRDENABLE(portp->brdnr, portp->pagenr);
- }
-#endif
- } else if (status & ST_PARITY) {
- status = TTY_PARITY;
- } else if (status & ST_FRAMING) {
- status = TTY_FRAME;
- } else if(status & ST_OVERRUN) {
- status = TTY_OVERRUN;
- } else {
- status = 0;
- }
- } else {
- status = 0;
- }
- if (tty->flip.char_buf_ptr != (char *) NULL) {
- if (tty->flip.count < TTY_FLIPBUF_SIZE) {
- *tty->flip.flag_buf_ptr++ = status;
- *tty->flip.char_buf_ptr++ = ch;
- tty->flip.count++;
+ if (test_bit(ASYI_TXLOW, &portp->istate)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
+ clear_bit(ASYI_DCDCHANGE, &portp->istate);
+ oldsigs = portp->sigs;
+ portp->sigs = stl_getsignals(portp);
+ if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+ wake_up_interruptible(&portp->open_wait);
+ if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+ if (portp->flags & ASYNC_CHECK_CD) {
+ if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (portp->flags & ASYNC_CALLOUT_NOHUP))) {
+ tty_hangup(tty);
}
- tty_schedule_flip(tty);
}
}
- } else {
- printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
- return;
}
-
-stl_rxalldone:
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
}
/*****************************************************************************/
/*
- * Modem interrupt handler. The is called when the modem signal line
- * (DCD) has changed state. Leave most of the work to the off-level
- * processing routine.
+ * Map in interrupt vector to this driver. Check that we don't
+ * already have this vector mapped, we might be sharing this
+ * interrupt across multiple boards.
*/
-static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr)
+static int stl_mapirq(int irq)
{
- stlport_t *portp;
- unsigned int ioack;
- unsigned char misr;
+ int rc, i;
#if DEBUG
- printk("stl_mdmisr(panelp=%x)\n", (int) panelp);
+ printk("stl_mapirq(irq=%d)\n", irq);
#endif
- ioack = inb(ioaddr + EREG_MDACK);
- if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
- printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
- return;
+ rc = 0;
+ for (i = 0; (i < stl_numintrs); i++) {
+ if (stl_gotintrs[i] == irq)
+ break;
}
- portp = panelp->ports[(ioack >> 3)];
-
- outb((MISR + portp->uartaddr), ioaddr);
- misr = inb(ioaddr + EREG_DATA);
- if (misr & MISR_DCD) {
- set_bit(ASYI_DCDCHANGE, &portp->istate);
- queue_task_irq_off(&portp->tqueue, &tq_scheduler);
- portp->stats.modem++;
+ if (i >= stl_numintrs) {
+ if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
+ printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
+ rc = -ENODEV;
+ } else {
+ stl_gotintrs[stl_numintrs++] = irq;
+ }
}
-
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
+ return(rc);
}
/*****************************************************************************/
/*
- * Interrupt handler for EIO and ECH boards. This code ain't all that
- * pretty, but the idea is to make it as fast as possible. This code is
- * well suited to be assemblerized :-) We don't use the general purpose
- * register access functions here, for speed we will go strait to the
- * io region.
+ * Initialize all the ports on a panel.
*/
-static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
{
- stlbrd_t *brdp;
- stlpanel_t *panelp;
- unsigned char svrtype;
- int i, panelnr, iobase;
+ stlport_t *portp;
+ int chipmask, i;
#if DEBUG
- printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+ printk("stl_initports(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
#endif
- panelp = (stlpanel_t *) NULL;
- for (i = 0; (i < stl_nrbrds); ) {
- if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) {
- i++;
- continue;
- }
- if (brdp->state == 0) {
- i++;
- continue;
- }
-/*
- * The following section of code handles the subtle differences
- * between board types. It is sort of similar, but different
- * enough to handle each separately.
- */
- if (brdp->brdtype == BRD_EASYIO) {
- if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) {
- i++;
- continue;
- }
- panelp = brdp->panels[0];
- iobase = panelp->iobase;
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- if (brdp->nrports > 4) {
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- }
- } else if (brdp->brdtype == BRD_ECH) {
- if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
- i++;
- continue;
- }
- outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
- for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
- panelp = brdp->panels[panelnr];
- iobase = panelp->iobase;
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- if (panelp->nrports > 8) {
- iobase += 0x8;
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- }
- }
- if (panelnr >= brdp->nrpanels) {
- i++;
- continue;
- }
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- } else if (brdp->brdtype == BRD_ECHPCI) {
- iobase = brdp->ioaddr2;
- for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
- panelp = brdp->panels[panelnr];
- outb(panelp->pagenr, brdp->ioctrl);
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- if (panelp->nrports > 8) {
- outb((panelp->pagenr + 1), brdp->ioctrl);
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- }
- }
- if (panelnr >= brdp->nrpanels) {
- i++;
- continue;
- }
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- } else if (brdp->brdtype == BRD_ECHMC) {
- if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
- i++;
- continue;
- }
- for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
- panelp = brdp->panels[panelnr];
- iobase = panelp->iobase;
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- if (panelp->nrports > 8) {
- iobase += 0x8;
- if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
- break;
- }
- }
- if (panelnr >= brdp->nrpanels) {
- i++;
- continue;
- }
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- } else {
- printk("STALLION: unknown board type=%x\n", brdp->brdtype);
- i++;
- continue;
- }
+ chipmask = stl_panelinit(brdp, panelp);
/*
- * We have determined what type of service is required for a
- * port. From here on in the service of a port is the same no
- * matter what the board type...
- */
- if (svrtype & SVRR_RX)
- stl_rxisr(panelp, iobase);
- if (svrtype & SVRR_TX)
- stl_txisr(panelp, iobase);
- if (svrtype & SVRR_MDM)
- stl_mdmisr(panelp, iobase);
+ * All UART's are initialized (if found!). Now go through and setup
+ * each ports data structures.
+ */
+ for (i = 0; (i < panelp->nrports); i++) {
+ portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
+ if (portp == (stlport_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
+ break;
+ }
+ memset(portp, 0, sizeof(stlport_t));
- if (brdp->brdtype == BRD_ECH)
- outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+ portp->magic = STL_PORTMAGIC;
+ portp->portnr = i;
+ portp->brdnr = panelp->brdnr;
+ portp->panelnr = panelp->panelnr;
+ portp->uartp = panelp->uartp;
+ portp->clk = brdp->clk;
+ portp->baud_base = STL_BAUDBASE;
+ portp->close_delay = STL_CLOSEDELAY;
+ portp->closing_wait = 30 * HZ;
+ portp->normaltermios = stl_deftermios;
+ portp->callouttermios = stl_deftermios;
+ portp->tqueue.routine = stl_offintr;
+ portp->tqueue.data = portp;
+ portp->stats.brd = portp->brdnr;
+ portp->stats.panel = portp->panelnr;
+ portp->stats.port = portp->portnr;
+ panelp->ports[i] = portp;
+ stl_portinit(brdp, panelp, portp);
}
+
+ return(0);
}
/*****************************************************************************/
/*
- * Service an off-level request for some channel.
+ * Try to find and initialize an EasyIO board.
*/
-static void stl_offintr(void *private)
+static int stl_initeio(stlbrd_t *brdp)
{
- stlport_t *portp;
- struct tty_struct *tty;
- unsigned int oldsigs;
+ stlpanel_t *panelp;
+ unsigned int status;
+ int rc;
- portp = private;
#if DEBUG
- printk("stl_offintr(portp=%x)\n", (int) portp);
+ printk("stl_initeio(brdp=%x)\n", (int) brdp);
#endif
- if (portp == (stlport_t *) NULL)
- return;
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return;
-
- if (test_bit(ASYI_TXLOW, &portp->istate)) {
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
- wake_up_interruptible(&tty->write_wait);
+ if (check_region(brdp->ioaddr1, 8)) {
+ printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+ brdp->brdnr, brdp->ioaddr1);
}
- if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
- clear_bit(ASYI_DCDCHANGE, &portp->istate);
- oldsigs = portp->sigs;
- portp->sigs = stl_getsignals(portp);
- if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
- wake_up_interruptible(&portp->open_wait);
- if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
- if (portp->flags & ASYNC_CHECK_CD) {
- if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
- (portp->flags & ASYNC_CALLOUT_NOHUP))) {
- tty_hangup(tty);
- }
- }
+
+ brdp->ioctrl = brdp->ioaddr1 + 1;
+ brdp->iostatus = brdp->ioaddr1 + 2;
+ brdp->clk = CD1400_CLK;
+ brdp->isr = stl_eiointr;
+
+ status = inb(brdp->iostatus);
+ switch (status & EIO_IDBITMASK) {
+ case EIO_8PORTM:
+ brdp->clk = CD1400_CLK8M;
+ /* fall thru */
+ case EIO_8PORTRS:
+ case EIO_8PORTDI:
+ brdp->nrports = 8;
+ break;
+ case EIO_4PORTRS:
+ brdp->nrports = 4;
+ break;
+ case EIO_MK3:
+ switch (status & EIO_BRDMASK) {
+ case ID_BRD4:
+ brdp->nrports = 4;
+ break;
+ case ID_BRD8:
+ brdp->nrports = 8;
+ break;
+ case ID_BRD16:
+ brdp->nrports = 16;
+ break;
+ default:
+ return(-ENODEV);
}
+ brdp->ioctrl++;
+ break;
+ default:
+ return(-ENODEV);
}
-}
-/*****************************************************************************/
+ request_region(brdp->ioaddr1, 8, "serial(EIO)");
/*
- * Wait for the command register to be ready. We will poll this,
- * since it won't usually take too long to be ready.
+ * Check that the supplied IRQ is good and then use it to setup the
+ * programmable interrupt bits on EIO board. Also set the edge/level
+ * triggered interrupt bit.
*/
-
-static void stl_ccrwait(stlport_t *portp)
-{
- int i;
-
- for (i = 0; (i < CCR_MAXWAIT); i++) {
- if (stl_getreg(portp, CCR) == 0) {
- return;
- }
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
}
+ outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
- printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+ panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+ if (panelp == (stlpanel_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+ return(-ENOMEM);
+ }
+ memset(panelp, 0, sizeof(stlpanel_t));
+
+ panelp->magic = STL_PANELMAGIC;
+ panelp->brdnr = brdp->brdnr;
+ panelp->panelnr = 0;
+ panelp->nrports = brdp->nrports;
+ panelp->iobase = brdp->ioaddr1;
+ panelp->hwid = status;
+ if ((status & EIO_IDBITMASK) == EIO_MK3) {
+ panelp->uartp = (void *) &stl_sc26198uart;
+ panelp->isr = stl_sc26198intr;
+ } else {
+ panelp->uartp = (void *) &stl_cd1400uart;
+ panelp->isr = stl_cd1400intr;
+ }
+
+ brdp->panels[0] = panelp;
+ brdp->nrpanels = 1;
+ brdp->state |= BRD_FOUND;
+ brdp->hwid = status;
+ rc = stl_mapirq(brdp->irq);
+ return(rc);
}
/*****************************************************************************/
/*
- * Set up the cd1400 registers for a port based on the termios port
- * settings.
+ * Try to find an ECH board and initialize it. This code is capable of
+ * dealing with all types of ECH board.
*/
-static void stl_setport(stlport_t *portp, struct termios *tiosp)
+static int stl_initech(stlbrd_t *brdp)
{
- stlbrd_t *brdp;
- unsigned long flags;
- unsigned int clkdiv, baudrate;
- unsigned char cor1, cor2, cor3;
- unsigned char cor4, cor5, ccr;
- unsigned char srer, sreron, sreroff;
- unsigned char mcor1, mcor2, rtpr;
- unsigned char clk, div;
+ stlpanel_t *panelp;
+ unsigned int status, nxtid, ioaddr, conflict;
+ int panelnr, banknr, i;
- cor1 = 0;
- cor2 = 0;
- cor3 = 0;
- cor4 = 0;
- cor5 = 0;
- ccr = 0;
- rtpr = 0;
- clk = 0;
- div = 0;
- mcor1 = 0;
- mcor2 = 0;
- sreron = 0;
- sreroff = 0;
+#if DEBUG
+ printk("stl_initech(brdp=%x)\n", (int) brdp);
+#endif
- brdp = stl_brds[portp->brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return;
+ status = 0;
+ conflict = 0;
/*
- * Set up the RX char ignore mask with those RX error types we
- * can ignore. We can get the cd1400 to help us out a little here,
- * it will ignore parity errors and breaks for us.
+ * Set up the initial board register contents for boards. This varies a
+ * bit between the different board types. So we need to handle each
+ * separately. Also do a check that the supplied IRQ is good.
*/
- portp->rxignoremsk = 0;
- if (tiosp->c_iflag & IGNPAR) {
- portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
- cor1 |= COR1_PARIGNORE;
- }
- if (tiosp->c_iflag & IGNBRK) {
- portp->rxignoremsk |= ST_BREAK;
- cor4 |= COR4_IGNBRK;
- }
+ if (brdp->brdtype == BRD_ECH) {
+ brdp->isr = stl_echatintr;
+ brdp->ioctrl = brdp->ioaddr1 + 1;
+ brdp->iostatus = brdp->ioaddr1 + 1;
+ status = inb(brdp->iostatus);
+ if ((status & ECH_IDBITMASK) != ECH_ID)
+ return(-ENODEV);
- portp->rxmarkmsk = ST_OVERRUN;
- if (tiosp->c_iflag & (INPCK | PARMRK))
- portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
- if (tiosp->c_iflag & BRKINT)
- portp->rxmarkmsk |= ST_BREAK;
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
+ }
+ status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+ status |= (stl_vecmap[brdp->irq] << 1);
+ outb((status | ECH_BRDRESET), brdp->ioaddr1);
+ brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+ outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+ conflict = check_region(brdp->ioaddr1, 2) ? brdp->ioaddr1 : 0;
+ if (conflict == 0)
+ conflict = check_region(brdp->ioaddr2, 32) ? brdp->ioaddr2 : 0;
+ request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
+ request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
+ outb(status, brdp->ioaddr1);
+ } else if (brdp->brdtype == BRD_ECHMC) {
+ brdp->isr = stl_echmcaintr;
+ brdp->ioctrl = brdp->ioaddr1 + 0x20;
+ brdp->iostatus = brdp->ioctrl;
+ status = inb(brdp->iostatus);
+ if ((status & ECH_IDBITMASK) != ECH_ID)
+ return(-ENODEV);
-/*
- * Go through the char size, parity and stop bits and set all the
- * option register appropriately.
- */
- switch (tiosp->c_cflag & CSIZE) {
- case CS5:
- cor1 |= COR1_CHL5;
- break;
- case CS6:
- cor1 |= COR1_CHL6;
- break;
- case CS7:
- cor1 |= COR1_CHL7;
- break;
- default:
- cor1 |= COR1_CHL8;
- break;
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
+ }
+ outb(ECHMC_BRDRESET, brdp->ioctrl);
+ outb(ECHMC_INTENABLE, brdp->ioctrl);
+ conflict = check_region(brdp->ioaddr1, 64) ? brdp->ioaddr1 : 0;
+ request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
+ } else if (brdp->brdtype == BRD_ECHPCI) {
+ brdp->isr = stl_echpciintr;
+ brdp->ioctrl = brdp->ioaddr1 + 2;
+ conflict = check_region(brdp->ioaddr1, 4) ? brdp->ioaddr1 : 0;
+ if (conflict == 0)
+ conflict = check_region(brdp->ioaddr2, 8) ? brdp->ioaddr2 : 0;
+ request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
+ request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
}
- if (tiosp->c_cflag & CSTOPB)
- cor1 |= COR1_STOP2;
- else
- cor1 |= COR1_STOP1;
-
- if (tiosp->c_cflag & PARENB) {
- if (tiosp->c_cflag & PARODD)
- cor1 |= (COR1_PARENB | COR1_PARODD);
- else
- cor1 |= (COR1_PARENB | COR1_PAREVEN);
- } else {
- cor1 |= COR1_PARNONE;
+ if (conflict) {
+ printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+ brdp->brdnr, conflict);
}
-/*
- * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
- * space for hardware flow control and the like. This should be set to
- * VMIN. Also here we will set the RX data timeout to 10ms - this should
- * really be based on VTIME.
- */
- cor3 |= FIFO_RXTHRESHOLD;
- rtpr = 2;
+ brdp->clk = CD1400_CLK;
+ brdp->hwid = status;
/*
- * Calculate the baud rate timers. For now we will just assume that
- * the input and output baud are the same. Could have used a baud
- * table here, but this way we can generate virtually any baud rate
- * we like!
+ * Scan through the secondary io address space looking for panels.
+ * As we find'em allocate and initialize panel structures for each.
*/
- baudrate = tiosp->c_cflag & CBAUD;
- if (baudrate & CBAUDEX) {
- baudrate &= ~CBAUDEX;
- if ((baudrate < 1) || (baudrate > 2))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- baudrate += 15;
- }
- baudrate = stl_baudrates[baudrate];
- if ((tiosp->c_cflag & CBAUD) == B38400) {
- if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- baudrate = 57600;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- baudrate = 115200;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- baudrate = (portp->baud_base / portp->custom_divisor);
- }
- if (baudrate > STL_MAXBAUD)
- baudrate = STL_MAXBAUD;
+ ioaddr = brdp->ioaddr2;
+ banknr = 0;
+ panelnr = 0;
+ nxtid = 0;
- if (baudrate > 0) {
- for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
- clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / baudrate);
- if (clkdiv < 0x100)
- break;
+ for (i = 0; (i < STL_MAXPANELS); i++) {
+ if (brdp->brdtype == BRD_ECHPCI) {
+ outb(nxtid, brdp->ioctrl);
+ ioaddr = brdp->ioaddr2;
+ }
+ status = inb(ioaddr + ECH_PNLSTATUS);
+ if ((status & ECH_PNLIDMASK) != nxtid)
+ break;
+ panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+ if (panelp == (stlpanel_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+ break;
+ }
+ memset(panelp, 0, sizeof(stlpanel_t));
+ panelp->magic = STL_PANELMAGIC;
+ panelp->brdnr = brdp->brdnr;
+ panelp->panelnr = panelnr;
+ panelp->iobase = ioaddr;
+ panelp->pagenr = nxtid;
+ panelp->hwid = status;
+ brdp->bnk2panel[banknr] = panelp;
+ brdp->bnkpageaddr[banknr] = nxtid;
+ brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+
+ if (status & ECH_PNLXPID) {
+ panelp->uartp = (void *) &stl_sc26198uart;
+ panelp->isr = stl_sc26198intr;
+ if (status & ECH_PNL16PORT) {
+ panelp->nrports = 16;
+ brdp->bnk2panel[banknr] = panelp;
+ brdp->bnkpageaddr[banknr] = nxtid;
+ brdp->bnkstataddr[banknr++] = ioaddr + 4 + ECH_PNLSTATUS;
+ } else {
+ panelp->nrports = 8;
+ }
+ } else {
+ panelp->uartp = (void *) &stl_cd1400uart;
+ panelp->isr = stl_cd1400intr;
+ if (status & ECH_PNL16PORT) {
+ if ((brdp->nrports + 16) > 32)
+ break;
+ panelp->nrports = 16;
+ panelp->ackmask = 0x80;
+ if (brdp->brdtype != BRD_ECHPCI)
+ ioaddr += EREG_BANKSIZE;
+ brdp->bnk2panel[banknr] = panelp;
+ brdp->bnkpageaddr[banknr] = ++nxtid;
+ brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+ } else {
+ panelp->nrports = 8;
+ panelp->ackmask = 0xc0;
+ }
}
- div = (unsigned char) clkdiv;
- }
-/*
- * Check what form of modem signaling is required and set it up.
- */
- if ((tiosp->c_cflag & CLOCAL) == 0) {
- mcor1 |= MCOR1_DCD;
- mcor2 |= MCOR2_DCD;
- sreron |= SRER_MODEM;
- portp->flags |= ASYNC_CHECK_CD;
- } else {
- portp->flags &= ~ASYNC_CHECK_CD;
+ nxtid++;
+ ioaddr += EREG_BANKSIZE;
+ brdp->nrports += panelp->nrports;
+ brdp->panels[panelnr++] = panelp;
+ if (ioaddr >= (brdp->ioaddr2 + 0x20))
+ break;
}
+ brdp->nrpanels = panelnr;
+ brdp->nrbnks = banknr;
+ if (brdp->brdtype == BRD_ECH)
+ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+ brdp->state |= BRD_FOUND;
+ i = stl_mapirq(brdp->irq);
+ return(i);
+}
+
+/*****************************************************************************/
+
/*
- * Setup cd1400 enhanced modes if we can. In particular we want to
- * handle as much of the flow control as possible automatically. As
- * well as saving a few CPU cycles it will also greatly improve flow
- * control reliability.
+ * Initialize and configure the specified board.
+ * Scan through all the boards in the configuration and see what we
+ * can find. Handle EIO and the ECH boards a little differently here
+ * since the initial search and setup is very different.
*/
- if (tiosp->c_iflag & IXON) {
- cor2 |= COR2_TXIBE;
- cor3 |= COR3_SCD12;
- if (tiosp->c_iflag & IXANY)
- cor2 |= COR2_IXM;
- }
+
+static int stl_brdinit(stlbrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+ switch (brdp->brdtype) {
+ case BRD_EASYIO:
+ stl_initeio(brdp);
+ break;
+ case BRD_ECH:
+ case BRD_ECHMC:
+ case BRD_ECHPCI:
+ stl_initech(brdp);
+ break;
+ default:
+ printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+ return(ENODEV);
+ }
+
+ stl_brds[brdp->brdnr] = brdp;
+ if ((brdp->state & BRD_FOUND) == 0) {
+ printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
+ return(ENODEV);
+ }
+
+ for (i = 0; (i < STL_MAXPANELS); i++)
+ if (brdp->panels[i] != (stlpanel_t *) NULL)
+ stl_initports(brdp, brdp->panels[i]);
+
+ printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Find any ECH-PCI boards that might be installed. Initialize each
+ * one as it is found.
+ */
+
+#ifdef CONFIG_PCI
+
+static int stl_findpcibrds()
+{
+ stlbrd_t *brdp;
+ unsigned char busnr, devnr, irq;
+ unsigned short class;
+ unsigned int ioaddr;
+ int i, rc;
+
+#if DEBUG
+ printk("stl_findpcibrds()\n");
+#endif
+
+ if (pcibios_present()) {
+ for (i = 0; (i < STL_MAXBRDS); i++) {
+ if (pcibios_find_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, i, &busnr, &devnr))
+ if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
+ break;
+
+/*
+ * Found a device on the PCI bus that has our vendor and
+ * device ID. Need to check now that it is really us.
+ */
+ if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
+ printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ if (class == PCI_CLASS_STORAGE_IDE)
+ continue;
+
+ if (stl_nrbrds >= STL_MAXBRDS) {
+ printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
+ break;
+ }
+
+/*
+ * We have a Stallion board. Allocate a board structure
+ * and initialize it. Read its IO and IRQ resources
+ * from conf space.
+ */
+ brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+ if (brdp == (stlbrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+ return(-ENOMEM);
+ }
+ memset(brdp, 0, sizeof(stlbrd_t));
+ brdp->magic = STL_BOARDMAGIC;
+ brdp->brdnr = stl_nrbrds++;
+ brdp->brdtype = BRD_ECHPCI;
+
+ if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+
+ if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+#if DEBUG
+ printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+#endif
+
+ if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->irq = irq;
+
+ stl_brdinit(brdp);
+ }
+ }
+
+ return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Scan through all the boards in the configuration and see what we
+ * can find. Handle EIO and the ECH boards a little differently here
+ * since the initial search and setup is too different.
+ */
+
+static int stl_initbrds()
+{
+ stlbrd_t *brdp;
+ stlconf_t *confp;
+ int i;
+
+#if DEBUG
+ printk("stl_initbrds()\n");
+#endif
+
+ if (stl_nrbrds > STL_MAXBRDS) {
+ printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+ stl_nrbrds = STL_MAXBRDS;
+ }
+
+/*
+ * Firstly scan the list of static boards configured. Allocate
+ * resources and initialize the boards as found.
+ */
+ for (i = 0; (i < stl_nrbrds); i++) {
+ confp = &stl_brdconf[i];
+ brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+ if (brdp == (stlbrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+ return(-ENOMEM);
+ }
+ memset(brdp, 0, sizeof(stlbrd_t));
+
+ brdp->magic = STL_BOARDMAGIC;
+ brdp->brdnr = i;
+ brdp->brdtype = confp->brdtype;
+ brdp->ioaddr1 = confp->ioaddr1;
+ brdp->ioaddr2 = confp->ioaddr2;
+ brdp->irq = confp->irq;
+ brdp->irqtype = confp->irqtype;
+ stl_brdinit(brdp);
+ }
+
+#ifdef CONFIG_PCI
+/*
+ * If the PCI BIOS support is compiled in then let's go looking for
+ * ECH-PCI boards.
+ */
+ stl_findpcibrds();
+#endif
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the board stats structure to user app.
+ */
+
+static int stl_getbrdstats(combrd_t *bp)
+{
+ stlbrd_t *brdp;
+ stlpanel_t *panelp;
+ int i;
+
+ copy_from_user(&stl_brdstats, bp, sizeof(combrd_t));
+ if (stl_brdstats.brd >= STL_MAXBRDS)
+ return(-ENODEV);
+ brdp = stl_brds[stl_brdstats.brd];
+ if (brdp == (stlbrd_t *) NULL)
+ return(-ENODEV);
+
+ memset(&stl_brdstats, 0, sizeof(combrd_t));
+ stl_brdstats.brd = brdp->brdnr;
+ stl_brdstats.type = brdp->brdtype;
+ stl_brdstats.hwid = brdp->hwid;
+ stl_brdstats.state = brdp->state;
+ stl_brdstats.ioaddr = brdp->ioaddr1;
+ stl_brdstats.ioaddr2 = brdp->ioaddr2;
+ stl_brdstats.irq = brdp->irq;
+ stl_brdstats.nrpanels = brdp->nrpanels;
+ stl_brdstats.nrports = brdp->nrports;
+ for (i = 0; (i < brdp->nrpanels); i++) {
+ panelp = brdp->panels[i];
+ stl_brdstats.panels[i].panel = i;
+ stl_brdstats.panels[i].hwid = panelp->hwid;
+ stl_brdstats.panels[i].nrports = panelp->nrports;
+ }
+
+ copy_to_user(bp, &stl_brdstats, sizeof(combrd_t));
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Resolve the referenced port number into a port struct pointer.
+ */
+
+static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
+{
+ stlbrd_t *brdp;
+ stlpanel_t *panelp;
+
+ if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
+ return((stlport_t *) NULL);
+ brdp = stl_brds[brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return((stlport_t *) NULL);
+ if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
+ return((stlport_t *) NULL);
+ panelp = brdp->panels[panelnr];
+ if (panelp == (stlpanel_t *) NULL)
+ return((stlport_t *) NULL);
+ if ((portnr < 0) || (portnr >= panelp->nrports))
+ return((stlport_t *) NULL);
+ return(panelp->ports[portnr]);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the port stats structure to user app. A NULL port struct
+ * pointer passed in means that we need to find out from the app
+ * what port to get stats for (used through board control device).
+ */
+
+static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+{
+ unsigned char *head, *tail;
+ unsigned long flags;
+
+ if (portp == (stlport_t *) NULL) {
+ copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
+ portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+ if (portp == (stlport_t *) NULL)
+ return(-ENODEV);
+ }
+
+ portp->stats.state = portp->istate;
+ portp->stats.flags = portp->flags;
+ portp->stats.hwid = portp->hwid;
+
+ portp->stats.ttystate = 0;
+ portp->stats.cflags = 0;
+ portp->stats.iflags = 0;
+ portp->stats.oflags = 0;
+ portp->stats.lflags = 0;
+ portp->stats.rxbuffered = 0;
+
+ save_flags(flags);
+ cli();
+ if (portp->tty != (struct tty_struct *) NULL) {
+ if (portp->tty->driver_data == portp) {
+ portp->stats.ttystate = portp->tty->flags;
+ portp->stats.rxbuffered = portp->tty->flip.count;
+ if (portp->tty->termios != (struct termios *) NULL) {
+ portp->stats.cflags = portp->tty->termios->c_cflag;
+ portp->stats.iflags = portp->tty->termios->c_iflag;
+ portp->stats.oflags = portp->tty->termios->c_oflag;
+ portp->stats.lflags = portp->tty->termios->c_lflag;
+ }
+ }
+ }
+ restore_flags(flags);
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+
+ portp->stats.signals = (unsigned long) stl_getsignals(portp);
+
+ copy_to_user(cp, &portp->stats, sizeof(comstats_t));
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Clear the port stats structure. We also return it zeroed out...
+ */
+
+static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+{
+ if (portp == (stlport_t *) NULL) {
+ copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
+ portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+ if (portp == (stlport_t *) NULL)
+ return(-ENODEV);
+ }
+
+ memset(&portp->stats, 0, sizeof(comstats_t));
+ portp->stats.brd = portp->brdnr;
+ portp->stats.panel = portp->panelnr;
+ portp->stats.port = portp->portnr;
+ copy_to_user(cp, &portp->stats, sizeof(comstats_t));
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the entire driver ports structure to a user app.
+ */
+
+static int stl_getportstruct(unsigned long arg)
+{
+ stlport_t *portp;
+
+ copy_from_user(&stl_dummyport, (void *) arg, sizeof(stlport_t));
+ portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
+ stl_dummyport.portnr);
+ if (portp == (stlport_t *) NULL)
+ return(-ENODEV);
+ copy_to_user((void *) arg, portp, sizeof(stlport_t));
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the entire driver board structure to a user app.
+ */
+
+static int stl_getbrdstruct(unsigned long arg)
+{
+ stlbrd_t *brdp;
+
+ copy_from_user(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
+ if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
+ return(-ENODEV);
+ brdp = stl_brds[stl_dummybrd.brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return(-ENODEV);
+ copy_to_user((void *) arg, brdp, sizeof(stlbrd_t));
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * The "staliomem" device is also required to do some special operations
+ * on the board and/or ports. In this driver it is mostly used for stats
+ * collection.
+ */
+
+static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ int brdnr, rc;
+
+#if DEBUG
+ printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+#endif
+
+ brdnr = MINOR(ip->i_rdev);
+ if (brdnr >= STL_MAXBRDS)
+ return(-ENODEV);
+ rc = 0;
+
+ switch (cmd) {
+ case COM_GETPORTSTATS:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+ rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
+ break;
+ case COM_CLRPORTSTATS:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+ rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
+ break;
+ case COM_GETBRDSTATS:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
+ rc = stl_getbrdstats((combrd_t *) arg);
+ break;
+ case COM_READPORT:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
+ rc = stl_getportstruct(arg);
+ break;
+ case COM_READBOARD:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
+ rc = stl_getbrdstruct(arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+int stl_init(void)
+{
+ printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+
+ stl_initbrds();
+
+/*
+ * Allocate a temporary write buffer.
+ */
+ stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
+ if (stl_tmpwritebuf == (char *) NULL)
+ printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+
+/*
+ * Set up a character driver for per board stuff. This is mainly used
+ * to do stats ioctls on the ports.
+ */
+ if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+ printk("STALLION: failed to register serial board device\n");
+
+/*
+ * Set up the tty driver structure and register us as a driver.
+ * Also setup the callout tty device.
+ */
+ memset(&stl_serial, 0, sizeof(struct tty_driver));
+ stl_serial.magic = TTY_DRIVER_MAGIC;
+ stl_serial.name = stl_serialname;
+ stl_serial.major = STL_SERIALMAJOR;
+ stl_serial.minor_start = 0;
+ stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+ stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
+ stl_serial.subtype = STL_DRVTYPSERIAL;
+ stl_serial.init_termios = stl_deftermios;
+ stl_serial.flags = TTY_DRIVER_REAL_RAW;
+ stl_serial.refcount = &stl_refcount;
+ stl_serial.table = stl_ttys;
+ stl_serial.termios = stl_termios;
+ stl_serial.termios_locked = stl_termioslocked;
+
+ stl_serial.open = stl_open;
+ stl_serial.close = stl_close;
+ stl_serial.write = stl_write;
+ stl_serial.put_char = stl_putchar;
+ stl_serial.flush_chars = stl_flushchars;
+ stl_serial.write_room = stl_writeroom;
+ stl_serial.chars_in_buffer = stl_charsinbuffer;
+ stl_serial.ioctl = stl_ioctl;
+ stl_serial.set_termios = stl_settermios;
+ stl_serial.throttle = stl_throttle;
+ stl_serial.unthrottle = stl_unthrottle;
+ stl_serial.stop = stl_stop;
+ stl_serial.start = stl_start;
+ stl_serial.hangup = stl_hangup;
+ stl_serial.flush_buffer = stl_flushbuffer;
+
+ stl_callout = stl_serial;
+ stl_callout.name = stl_calloutname;
+ stl_callout.major = STL_CALLOUTMAJOR;
+ stl_callout.subtype = STL_DRVTYPCALLOUT;
+
+ if (tty_register_driver(&stl_serial))
+ printk("STALLION: failed to register serial driver\n");
+ if (tty_register_driver(&stl_callout))
+ printk("STALLION: failed to register callout driver\n");
+
+ return(0);
+}
+
+/*****************************************************************************/
+/* CD1400 HARDWARE FUNCTIONS */
+/*****************************************************************************/
+
+/*
+ * These functions get/set/update the registers of the cd1400 UARTs.
+ * Access to the cd1400 registers is via an address/data io port pair.
+ * (Maybe should make this inline...)
+ */
+
+static int stl_cd1400getreg(stlport_t *portp, int regnr)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ return(inb(portp->ioaddr + EREG_DATA));
+}
+
+static void stl_cd1400setreg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ if (inb(portp->ioaddr + EREG_DATA) != value) {
+ outb(value, portp->ioaddr + EREG_DATA);
+ return(1);
+ }
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Inbitialize the UARTs in a panel. We don't care what sort of board
+ * these ports are on - since the port io registers are almost
+ * identical when dealing with ports.
+ */
+
+static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
+{
+ unsigned int gfrcr;
+ int chipmask, i, j;
+ int nrchips, uartaddr, ioaddr;
+
+#if DEBUG
+ printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
+#endif
+
+ BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ * Check that each chip is present and started up OK.
+ */
+ chipmask = 0;
+ nrchips = panelp->nrports / CD1400_PORTS;
+ for (i = 0; (i < nrchips); i++) {
+ if (brdp->brdtype == BRD_ECHPCI) {
+ outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+ ioaddr = panelp->iobase;
+ } else {
+ ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+ }
+ uartaddr = (i & 0x01) ? 0x080 : 0;
+ outb((GFRCR + uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+ outb((CCR + uartaddr), ioaddr);
+ outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+ outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+ outb((GFRCR + uartaddr), ioaddr);
+ for (j = 0; (j < CCR_MAXWAIT); j++) {
+ if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+ break;
+ }
+ if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+ printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+ continue;
+ }
+ chipmask |= (0x1 << i);
+ outb((PPR + uartaddr), ioaddr);
+ outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+ }
+
+ BRDDISABLE(panelp->brdnr);
+ return(chipmask);
+}
+
+/*****************************************************************************/
+
+/*
+ * Initialize hardware specific port registers.
+ */
+
+static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
+{
+#if DEBUG
+ printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n",
+ (int) brdp, (int) panelp, (int) portp);
+#endif
+
+ if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+ return;
+
+ portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
+ (portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
+ portp->uartaddr = (portp->portnr & 0x04) << 5;
+ portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
+
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
+ portp->hwid = stl_cd1400getreg(portp, GFRCR);
+ BRDDISABLE(portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ * Wait for the command register to be ready. We will poll this,
+ * since it won't usually take too long to be ready.
+ */
+
+static void stl_cd1400ccrwait(stlport_t *portp)
+{
+ int i;
+
+ for (i = 0; (i < CCR_MAXWAIT); i++) {
+ if (stl_cd1400getreg(portp, CCR) == 0) {
+ return;
+ }
+ }
+
+ printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ * Set up the cd1400 registers for a port based on the termios port
+ * settings.
+ */
+
+static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
+{
+ stlbrd_t *brdp;
+ unsigned long flags;
+ unsigned int clkdiv, baudrate;
+ unsigned char cor1, cor2, cor3;
+ unsigned char cor4, cor5, ccr;
+ unsigned char srer, sreron, sreroff;
+ unsigned char mcor1, mcor2, rtpr;
+ unsigned char clk, div;
+
+ cor1 = 0;
+ cor2 = 0;
+ cor3 = 0;
+ cor4 = 0;
+ cor5 = 0;
+ ccr = 0;
+ rtpr = 0;
+ clk = 0;
+ div = 0;
+ mcor1 = 0;
+ mcor2 = 0;
+ sreron = 0;
+ sreroff = 0;
+
+ brdp = stl_brds[portp->brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return;
+
+/*
+ * Set up the RX char ignore mask with those RX error types we
+ * can ignore. We can get the cd1400 to help us out a little here,
+ * it will ignore parity errors and breaks for us.
+ */
+ portp->rxignoremsk = 0;
+ if (tiosp->c_iflag & IGNPAR) {
+ portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+ cor1 |= COR1_PARIGNORE;
+ }
+ if (tiosp->c_iflag & IGNBRK) {
+ portp->rxignoremsk |= ST_BREAK;
+ cor4 |= COR4_IGNBRK;
+ }
+
+ portp->rxmarkmsk = ST_OVERRUN;
+ if (tiosp->c_iflag & (INPCK | PARMRK))
+ portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+ if (tiosp->c_iflag & BRKINT)
+ portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ * Go through the char size, parity and stop bits and set all the
+ * option register appropriately.
+ */
+ switch (tiosp->c_cflag & CSIZE) {
+ case CS5:
+ cor1 |= COR1_CHL5;
+ break;
+ case CS6:
+ cor1 |= COR1_CHL6;
+ break;
+ case CS7:
+ cor1 |= COR1_CHL7;
+ break;
+ default:
+ cor1 |= COR1_CHL8;
+ break;
+ }
+
+ if (tiosp->c_cflag & CSTOPB)
+ cor1 |= COR1_STOP2;
+ else
+ cor1 |= COR1_STOP1;
+
+ if (tiosp->c_cflag & PARENB) {
+ if (tiosp->c_cflag & PARODD)
+ cor1 |= (COR1_PARENB | COR1_PARODD);
+ else
+ cor1 |= (COR1_PARENB | COR1_PAREVEN);
+ } else {
+ cor1 |= COR1_PARNONE;
+ }
+
+/*
+ * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
+ * space for hardware flow control and the like. This should be set to
+ * VMIN. Also here we will set the RX data timeout to 10ms - this should
+ * really be based on VTIME.
+ */
+ cor3 |= FIFO_RXTHRESHOLD;
+ rtpr = 2;
+
+/*
+ * Calculate the baud rate timers. For now we will just assume that
+ * the input and output baud are the same. Could have used a baud
+ * table here, but this way we can generate virtually any baud rate
+ * we like!
+ */
+ baudrate = tiosp->c_cflag & CBAUD;
+ if (baudrate & CBAUDEX) {
+ baudrate &= ~CBAUDEX;
+ if ((baudrate < 1) || (baudrate > 2))
+ tiosp->c_cflag &= ~CBAUDEX;
+ else
+ baudrate += 15;
+ }
+ baudrate = stl_baudrates[baudrate];
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baudrate = 57600;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baudrate = 115200;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ baudrate = (portp->baud_base / portp->custom_divisor);
+ }
+ if (baudrate > STL_MAXBAUD)
+ baudrate = STL_MAXBAUD;
+
+ if (baudrate > 0) {
+ for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
+ clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / baudrate);
+ if (clkdiv < 0x100)
+ break;
+ }
+ div = (unsigned char) clkdiv;
+ }
+
+/*
+ * Check what form of modem signaling is required and set it up.
+ */
+ if ((tiosp->c_cflag & CLOCAL) == 0) {
+ mcor1 |= MCOR1_DCD;
+ mcor2 |= MCOR2_DCD;
+ sreron |= SRER_MODEM;
+ portp->flags |= ASYNC_CHECK_CD;
+ } else {
+ portp->flags &= ~ASYNC_CHECK_CD;
+ }
+
+/*
+ * Setup cd1400 enhanced modes if we can. In particular we want to
+ * handle as much of the flow control as possible automatically. As
+ * well as saving a few CPU cycles it will also greatly improve flow
+ * control reliability.
+ */
+ if (tiosp->c_iflag & IXON) {
+ cor2 |= COR2_TXIBE;
+ cor3 |= COR3_SCD12;
+ if (tiosp->c_iflag & IXANY)
+ cor2 |= COR2_IXM;
+ }
if (tiosp->c_cflag & CRTSCTS) {
cor2 |= COR2_CTSAE;
@@ -2111,89 +2883,332 @@
}
/*
- * All register cd1400 register values calculated so go through and set
+ * All cd1400 register values calculated so go through and set
* them all up.
*/
#if DEBUG
- printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
- printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
- printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
- printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
- printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+ printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+ printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
+ printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
+ printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+ printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
+ srer = stl_cd1400getreg(portp, SRER);
+ stl_cd1400setreg(portp, SRER, 0);
+ if (stl_cd1400updatereg(portp, COR1, cor1))
+ ccr = 1;
+ if (stl_cd1400updatereg(portp, COR2, cor2))
+ ccr = 1;
+ if (stl_cd1400updatereg(portp, COR3, cor3))
+ ccr = 1;
+ if (ccr) {
+ stl_cd1400ccrwait(portp);
+ stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
+ }
+ stl_cd1400setreg(portp, COR4, cor4);
+ stl_cd1400setreg(portp, COR5, cor5);
+ stl_cd1400setreg(portp, MCOR1, mcor1);
+ stl_cd1400setreg(portp, MCOR2, mcor2);
+ if (baudrate > 0) {
+ stl_cd1400setreg(portp, TCOR, clk);
+ stl_cd1400setreg(portp, TBPR, div);
+ stl_cd1400setreg(portp, RCOR, clk);
+ stl_cd1400setreg(portp, RBPR, div);
+ }
+ stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+ stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+ stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+ stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+ stl_cd1400setreg(portp, RTPR, rtpr);
+ mcor1 = stl_cd1400getreg(portp, MSVR1);
+ if (mcor1 & MSVR1_DCD)
+ portp->sigs |= TIOCM_CD;
+ else
+ portp->sigs &= ~TIOCM_CD;
+ stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Set the state of the DTR and RTS signals.
+ */
+
+static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
+{
+ unsigned char msvr1, msvr2;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
+
+ msvr1 = 0;
+ msvr2 = 0;
+ if (dtr > 0)
+ msvr1 = MSVR1_DTR;
+ if (rts > 0)
+ msvr2 = MSVR2_RTS;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ if (rts >= 0)
+ stl_cd1400setreg(portp, MSVR2, msvr2);
+ if (dtr >= 0)
+ stl_cd1400setreg(portp, MSVR1, msvr1);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the state of the signals.
+ */
+
+static int stl_cd1400getsignals(stlport_t *portp)
+{
+ unsigned char msvr1, msvr2;
+ unsigned long flags;
+ int sigs;
+
+#if DEBUG
+ printk("stl_cd1400getsignals(portp=%x)\n", (int) portp);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ msvr1 = stl_cd1400getreg(portp, MSVR1);
+ msvr2 = stl_cd1400getreg(portp, MSVR2);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+
+ sigs = 0;
+ sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+ sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+ sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+ sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+ sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+ sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+ return(sigs);
+}
+
+/*****************************************************************************/
+
+/*
+ * Enable/Disable the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx)
+{
+ unsigned char ccr;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+ ccr = 0;
+
+ if (tx == 0)
+ ccr |= CCR_TXDISABLE;
+ else if (tx > 0)
+ ccr |= CCR_TXENABLE;
+ if (rx == 0)
+ ccr |= CCR_RXDISABLE;
+ else if (rx > 0)
+ ccr |= CCR_RXENABLE;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400ccrwait(portp);
+ stl_cd1400setreg(portp, CCR, ccr);
+ stl_cd1400ccrwait(portp);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Start/stop the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
+{
+ unsigned char sreron, sreroff;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
+ rx, tx);
+#endif
+
+ sreron = 0;
+ sreroff = 0;
+ if (tx == 0)
+ sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+ else if (tx == 1)
+ sreron |= SRER_TXDATA;
+ else if (tx >= 2)
+ sreron |= SRER_TXEMPTY;
+ if (rx == 0)
+ sreroff |= SRER_RXDATA;
+ else if (rx > 0)
+ sreron |= SRER_RXDATA;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
+ BRDDISABLE(portp->brdnr);
+ if (tx > 0)
+ set_bit(ASYI_TXBUSY, &portp->istate);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Disable all interrupts from this port.
+ */
+
+static void stl_cd1400disableintrs(stlport_t *portp)
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp);
+#endif
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400setreg(portp, SRER, 0);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static void stl_cd1400sendbreak(stlport_t *portp, long len)
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400setreg(portp, COR2, (stl_cd1400getreg(portp, COR2) | COR2_ETC));
+ stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
+ BRDDISABLE(portp->brdnr);
+ len = len / 5;
+ portp->brklen = (len > 255) ? 255 : len;
+ portp->stats.txbreaks++;
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Take flow control actions...
+ */
+
+static void stl_cd1400flowctrl(stlport_t *portp, int state)
+{
+ struct tty_struct *tty;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_cd1400flowctrl(portp=%x,state=%x)\n", (int) portp, state);
#endif
+ if (portp == (stlport_t *) NULL)
+ return;
+ tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return;
+
save_flags(flags);
cli();
BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x3));
- srer = stl_getreg(portp, SRER);
- stl_setreg(portp, SRER, 0);
- if (stl_updatereg(portp, COR1, cor1))
- ccr = 1;
- if (stl_updatereg(portp, COR2, cor2))
- ccr = 1;
- if (stl_updatereg(portp, COR3, cor3))
- ccr = 1;
- if (ccr) {
- stl_ccrwait(portp);
- stl_setreg(portp, CCR, CCR_CORCHANGE);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+
+ if (state) {
+ if (tty->termios->c_iflag & IXOFF) {
+ stl_cd1400ccrwait(portp);
+ stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+ portp->stats.rxxon++;
+ stl_cd1400ccrwait(portp);
+ }
+/*
+ * Question: should we return RTS to what it was before? It may
+ * have been set by an ioctl... Suppose not, since if you have
+ * hardware flow control set then it is pretty silly to go and
+ * set the RTS line by hand.
+ */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
+ stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
+ portp->stats.rxrtson++;
+ }
+ } else {
+ if (tty->termios->c_iflag & IXOFF) {
+ stl_cd1400ccrwait(portp);
+ stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+ portp->stats.rxxoff++;
+ stl_cd1400ccrwait(portp);
+ }
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) & 0xf0));
+ stl_cd1400setreg(portp, MSVR2, 0);
+ portp->stats.rxrtsoff++;
+ }
}
- stl_setreg(portp, COR4, cor4);
- stl_setreg(portp, COR5, cor5);
- stl_setreg(portp, MCOR1, mcor1);
- stl_setreg(portp, MCOR2, mcor2);
- if (baudrate > 0) {
- stl_setreg(portp, TCOR, clk);
- stl_setreg(portp, TBPR, div);
- stl_setreg(portp, RCOR, clk);
- stl_setreg(portp, RBPR, div);
- }
- stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
- stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
- stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
- stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
- stl_setreg(portp, RTPR, rtpr);
- mcor1 = stl_getreg(portp, MSVR1);
- if (mcor1 & MSVR1_DCD)
- portp->sigs |= TIOCM_CD;
- else
- portp->sigs &= ~TIOCM_CD;
- stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+
BRDDISABLE(portp->brdnr);
restore_flags(flags);
}
/*****************************************************************************/
-/*
- * Set the state of the DTR and RTS signals.
- */
-
-static void stl_setsignals(stlport_t *portp, int dtr, int rts)
+static void stl_cd1400flush(stlport_t *portp)
{
- unsigned char msvr1, msvr2;
unsigned long flags;
#if DEBUG
- printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+ printk("stl_cd1400flush(portp=%x)\n", (int) portp);
#endif
- msvr1 = 0;
- msvr2 = 0;
- if (dtr > 0)
- msvr1 = MSVR1_DTR;
- if (rts > 0)
- msvr2 = MSVR2_RTS;
+ if (portp == (stlport_t *) NULL)
+ return;
save_flags(flags);
cli();
BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- if (rts >= 0)
- stl_setreg(portp, MSVR2, msvr2);
- if (dtr >= 0)
- stl_setreg(portp, MSVR1, msvr1);
+ stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_cd1400ccrwait(portp);
+ stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
+ stl_cd1400ccrwait(portp);
+ portp->tx.tail = portp->tx.head;
BRDDISABLE(portp->brdnr);
restore_flags(flags);
}
@@ -2201,982 +3216,1189 @@
/*****************************************************************************/
/*
- * Return the state of the signals.
+ * Interrupt service routine for cd1400 panels.
+ */
+
+static void stl_cd1400intr(stlpanel_t *panelp, unsigned int iobase)
+{
+ unsigned char svrtype;
+
+#if DEBUG
+ printk("stl_cd1400intr(panelp=%x,iobase=%x)\n", (int) panelp, iobase);
+#endif
+
+ outb(SVRR, iobase);
+ svrtype = inb(iobase + EREG_DATA);
+ outb((SVRR + 0x80), iobase);
+ svrtype |= inb(iobase + EREG_DATA);
+ if (svrtype & SVRR_RX)
+ stl_cd1400rxisr(panelp, iobase);
+ if (svrtype & SVRR_TX)
+ stl_cd1400txisr(panelp, iobase);
+ if (svrtype & SVRR_MDM)
+ stl_cd1400mdmisr(panelp, iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ * Transmit interrupt handler. This has gotta be fast! Handling TX
+ * chars is pretty simple, stuff as many as possible from the TX buffer
+ * into the cd1400 FIFO. Must also handle TX breaks here, since they
+ * are embedded as commands in the data stream. Oh no, had to use a goto!
+ * This could be optimized more, will do when I get time...
+ * In practice it is possible that interrupts are enabled but that the
+ * port has been hung up. Need to handle not having any TX buffer here,
+ * this is done by using the side effect that head and tail will also
+ * be NULL if the buffer has been freed.
+ */
+
+static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr)
+{
+ stlport_t *portp;
+ int len, stlen;
+ char *head, *tail;
+ unsigned char ioack, srer;
+
+#if DEBUG
+ printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+ ioack = inb(ioaddr + EREG_TXACK);
+ if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+ printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+
+/*
+ * Unfortunately we need to handle breaks in the data stream, since
+ * this is the only way to generate them on the cd1400. Do it now if
+ * a break is to be sent.
+ */
+ if (portp->brklen != 0) {
+ if (portp->brklen > 0) {
+ outb((TDR + portp->uartaddr), ioaddr);
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_DELAY, (ioaddr + EREG_DATA));
+ outb(portp->brklen, (ioaddr + EREG_DATA));
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+ portp->brklen = -1;
+ goto stl_txalldone;
+ } else {
+ outb((COR2 + portp->uartaddr), ioaddr);
+ outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
+ portp->brklen = 0;
+ }
+ }
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+ if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+ set_bit(ASYI_TXLOW, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ }
+
+ if (len == 0) {
+ outb((SRER + portp->uartaddr), ioaddr);
+ srer = inb(ioaddr + EREG_DATA);
+ if (srer & SRER_TXDATA) {
+ srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+ } else {
+ srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+ clear_bit(ASYI_TXBUSY, &portp->istate);
+ }
+ outb(srer, (ioaddr + EREG_DATA));
+ } else {
+ len = MIN(len, CD1400_TXFIFOSIZE);
+ portp->stats.txtotal += len;
+ stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+ outb((TDR + portp->uartaddr), ioaddr);
+ outsb((ioaddr + EREG_DATA), tail, stlen);
+ len -= stlen;
+ tail += stlen;
+ if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+ tail = portp->tx.buf;
+ if (len > 0) {
+ outsb((ioaddr + EREG_DATA), tail, len);
+ tail += len;
+ }
+ portp->tx.tail = tail;
+ }
+
+stl_txalldone:
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ * Receive character interrupt handler. Determine if we have good chars
+ * or bad chars and then process appropriately. Good chars are easy
+ * just shove the lot into the RX buffer and set all status byte to 0.
+ * If a bad RX char then process as required. This routine needs to be
+ * fast! In practice it is possible that we get an interrupt on a port
+ * that is closed. This can happen on hangups - since they completely
+ * shutdown a port not in user context. Need to handle this case.
+ */
+
+static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr)
+{
+ stlport_t *portp;
+ struct tty_struct *tty;
+ unsigned int ioack, len, buflen;
+ unsigned char status;
+ char ch;
+
+#if DEBUG
+ printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+ ioack = inb(ioaddr + EREG_RXACK);
+ if ((ioack & panelp->ackmask) != 0) {
+ printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+ tty = portp->tty;
+
+ if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+ outb((RDCR + portp->uartaddr), ioaddr);
+ len = inb(ioaddr + EREG_DATA);
+ if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
+ ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
+ portp->stats.rxlost += len;
+ portp->stats.rxtotal += len;
+ } else {
+ len = MIN(len, buflen);
+ if (len > 0) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
+ memset(tty->flip.flag_buf_ptr, 0, len);
+ tty->flip.flag_buf_ptr += len;
+ tty->flip.char_buf_ptr += len;
+ tty->flip.count += len;
+ tty_schedule_flip(tty);
+ portp->stats.rxtotal += len;
+ }
+ }
+ } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ status = inb(ioaddr + EREG_DATA);
+ ch = inb(ioaddr + EREG_DATA);
+ if (status & ST_PARITY)
+ portp->stats.rxparity++;
+ if (status & ST_FRAMING)
+ portp->stats.rxframing++;
+ if (status & ST_OVERRUN)
+ portp->stats.rxoverrun++;
+ if (status & ST_BREAK)
+ portp->stats.rxbreaks++;
+ if (status & ST_SCHARMASK) {
+ if ((status & ST_SCHARMASK) == ST_SCHAR1)
+ portp->stats.txxon++;
+ if ((status & ST_SCHARMASK) == ST_SCHAR2)
+ portp->stats.txxoff++;
+ goto stl_rxalldone;
+ }
+ if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
+ if (portp->rxmarkmsk & status) {
+ if (status & ST_BREAK) {
+ status = TTY_BREAK;
+#ifndef MODULE
+ if (portp->flags & ASYNC_SAK) {
+ do_SAK(tty);
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ }
+#endif
+ } else if (status & ST_PARITY) {
+ status = TTY_PARITY;
+ } else if (status & ST_FRAMING) {
+ status = TTY_FRAME;
+ } else if(status & ST_OVERRUN) {
+ status = TTY_OVERRUN;
+ } else {
+ status = 0;
+ }
+ } else {
+ status = 0;
+ }
+ if (tty->flip.char_buf_ptr != (char *) NULL) {
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ *tty->flip.flag_buf_ptr++ = status;
+ *tty->flip.char_buf_ptr++ = ch;
+ tty->flip.count++;
+ }
+ tty_schedule_flip(tty);
+ }
+ }
+ } else {
+ printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ return;
+ }
+
+stl_rxalldone:
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ * Modem interrupt handler. The is called when the modem signal line
+ * (DCD) has changed state. Leave most of the work to the off-level
+ * processing routine.
*/
-static int stl_getsignals(stlport_t *portp)
+static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr)
{
- unsigned char msvr1, msvr2;
- unsigned long flags;
- int sigs;
+ stlport_t *portp;
+ unsigned int ioack;
+ unsigned char misr;
#if DEBUG
- printk("stl_getsignals(portp=%x)\n", (int) portp);
+ printk("stl_cd1400mdmisr(panelp=%x)\n", (int) panelp);
#endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- msvr1 = stl_getreg(portp, MSVR1);
- msvr2 = stl_getreg(portp, MSVR2);
- BRDDISABLE(portp->brdnr);
- sigs = 0;
- sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
- sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
- sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
- sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
- sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
- sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
- restore_flags(flags);
- return(sigs);
+ ioack = inb(ioaddr + EREG_MDACK);
+ if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+ printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+
+ outb((MISR + portp->uartaddr), ioaddr);
+ misr = inb(ioaddr + EREG_DATA);
+ if (misr & MISR_DCD) {
+ set_bit(ASYI_DCDCHANGE, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ portp->stats.modem++;
+ }
+
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
}
/*****************************************************************************/
+/* SC26198 HARDWARE FUNCTIONS */
+/*****************************************************************************/
/*
- * Enable/Disable the Transmitter and/or Receiver.
+ * These functions get/set/update the registers of the sc26198 UARTs.
+ * Access to the sc26198 registers is via an address/data io port pair.
+ * (Maybe should make this inline...)
*/
-static void stl_enablerxtx(stlport_t *portp, int rx, int tx)
+static int stl_sc26198getreg(stlport_t *portp, int regnr)
{
- unsigned char ccr;
- unsigned long flags;
-
-#if DEBUG
- printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
-#endif
- ccr = 0;
+ outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+ return(inb(portp->ioaddr + XP_DATA));
+}
- if (tx == 0)
- ccr |= CCR_TXDISABLE;
- else if (tx > 0)
- ccr |= CCR_TXENABLE;
- if (rx == 0)
- ccr |= CCR_RXDISABLE;
- else if (rx > 0)
- ccr |= CCR_RXENABLE;
+static void stl_sc26198setreg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+ outb(value, (portp->ioaddr + XP_DATA));
+}
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- stl_ccrwait(portp);
- stl_setreg(portp, CCR, ccr);
- stl_ccrwait(portp);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+ if (inb(portp->ioaddr + XP_DATA) != value) {
+ outb(value, (portp->ioaddr + XP_DATA));
+ return(1);
+ }
+ return(0);
}
/*****************************************************************************/
/*
- * Start/stop the Transmitter and/or Receiver.
+ * Functions to get and set the sc26198 global registers.
*/
-static void stl_startrxtx(stlport_t *portp, int rx, int tx)
+static int stl_sc26198getglobreg(stlport_t *portp, int regnr)
{
- unsigned char sreron, sreroff;
- unsigned long flags;
-
-#if DEBUG
- printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
-#endif
-
- sreron = 0;
- sreroff = 0;
- if (tx == 0)
- sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
- else if (tx == 1)
- sreron |= SRER_TXDATA;
- else if (tx >= 2)
- sreron |= SRER_TXEMPTY;
- if (rx == 0)
- sreroff |= SRER_RXDATA;
- else if (rx > 0)
- sreron |= SRER_RXDATA;
+ outb(regnr, (portp->ioaddr + XP_ADDR));
+ return(inb(portp->ioaddr + XP_DATA));
+}
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron));
- BRDDISABLE(portp->brdnr);
- if (tx > 0)
- set_bit(ASYI_TXBUSY, &portp->istate);
- restore_flags(flags);
+#if 0
+static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value)
+{
+ outb(regnr, (portp->ioaddr + XP_ADDR));
+ outb(value, (portp->ioaddr + XP_DATA));
}
+#endif
/*****************************************************************************/
/*
- * Disable all interrupts from this port.
+ * Inbitialize the UARTs in a panel. We don't care what sort of board
+ * these ports are on - since the port io registers are almost
+ * identical when dealing with ports.
*/
-static void stl_disableintrs(stlport_t *portp)
+static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
{
- unsigned long flags;
+ int chipmask, i;
+ int nrchips, ioaddr;
#if DEBUG
- printk("stl_disableintrs(portp=%x)\n", (int) portp);
+ printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
#endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- stl_setreg(portp, SRER, 0);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
-}
-/*****************************************************************************/
+ BRDENABLE(panelp->brdnr, panelp->pagenr);
-static void stl_sendbreak(stlport_t *portp, long len)
-{
- unsigned long flags;
+/*
+ * Check that each chip is present and started up OK.
+ */
+ chipmask = 0;
+ nrchips = panelp->nrports / SC26198_PORTS;
+ if (brdp->brdtype == BRD_ECHPCI)
+ outb(panelp->pagenr, brdp->ioctrl);
-#if DEBUG
- printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
-#endif
+ for (i = 0; (i < nrchips); i++) {
+ ioaddr = panelp->iobase + (i * 4);
+ outb(SCCR, (ioaddr + XP_ADDR));
+ outb(CR_RESETALL, (ioaddr + XP_DATA));
+ outb(TSTR, (ioaddr + XP_ADDR));
+ if (inb(ioaddr + XP_DATA) != 0) {
+ printk("STALLION: sc26198 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+ continue;
+ }
+ chipmask |= (0x1 << i);
+ outb(GCCR, (ioaddr + XP_ADDR));
+ outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
+ outb(WDTRCR, (ioaddr + XP_ADDR));
+ outb(0xff, (ioaddr + XP_DATA));
+ }
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_setreg(portp, CAR, (portp->portnr & 0x03));
- stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC));
- stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
- BRDDISABLE(portp->brdnr);
- len = len / 5;
- portp->brklen = (len > 255) ? 255 : len;
- portp->stats.txbreaks++;
- restore_flags(flags);
+ BRDDISABLE(panelp->brdnr);
+ return(chipmask);
}
/*****************************************************************************/
/*
- * Map in interrupt vector to this driver. Check that we don't
- * already have this vector mapped, we might be sharing this
- * interrupt across multiple boards.
+ * Initialize hardware specific port registers.
*/
-static int stl_mapirq(int irq)
+static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
{
- int rc, i;
-
#if DEBUG
- printk("stl_mapirq(irq=%d)\n", irq);
+ printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)\n",
+ (int) brdp, (int) panelp, (int) portp);
#endif
- rc = 0;
- for (i = 0; (i < stl_numintrs); i++) {
- if (stl_gotintrs[i] == irq)
- break;
- }
- if (i >= stl_numintrs) {
- if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
- printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
- rc = -ENODEV;
- } else {
- stl_gotintrs[stl_numintrs++] = irq;
- }
- }
- return(rc);
+ if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+ return;
+
+ portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
+ portp->uartaddr = (portp->portnr & 0x07) << 4;
+ portp->pagenr = panelp->pagenr;
+ portp->hwid = 0x1;
+
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
+ BRDDISABLE(portp->brdnr);
}
/*****************************************************************************/
/*
- * Try to find and initialize all the ports on a panel. We don't care
- * what sort of board these ports are on - since the port io registers
- * are almost identical when dealing with ports.
+ * Set up the sc26198 registers for a port based on the termios port
+ * settings.
*/
-static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
+static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
{
- stlport_t *portp;
- unsigned int chipmask;
- unsigned int gfrcr;
- int nrchips, uartaddr, ioaddr;
- int i, j;
-
-#if DEBUG
- printk("stl_initports(panelp=%x)\n", (int) panelp);
-#endif
+ stlbrd_t *brdp;
+ unsigned long flags;
+ unsigned int baudrate;
+ unsigned char mr0, mr1, mr2, clk;
+ unsigned char imron, imroff, iopr, ipr;
+
+ mr0 = 0;
+ mr1 = 0;
+ mr2 = 0;
+ clk = 0;
+ iopr = 0;
+ imron = 0;
+ imroff = 0;
- BRDENABLE(panelp->brdnr, panelp->pagenr);
+ brdp = stl_brds[portp->brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return;
/*
- * Check that each chip is present and started up OK.
+ * Set up the RX char ignore mask with those RX error types we
+ * can ignore.
*/
- chipmask = 0;
- nrchips = panelp->nrports / CD1400_PORTS;
- for (i = 0; (i < nrchips); i++) {
- if (brdp->brdtype == BRD_ECHPCI) {
- outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
- ioaddr = panelp->iobase;
- } else {
- ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
- }
- uartaddr = (i & 0x01) ? 0x080 : 0;
- outb((GFRCR + uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
- outb((CCR + uartaddr), ioaddr);
- outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
- outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
- outb((GFRCR + uartaddr), ioaddr);
- for (j = 0; (j < CCR_MAXWAIT); j++) {
- if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
- break;
- }
- if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
- printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
- continue;
- }
- chipmask |= (0x1 << i);
- outb((PPR + uartaddr), ioaddr);
- outb(PPR_SCALAR, (ioaddr + EREG_DATA));
- }
+ portp->rxignoremsk = 0;
+ if (tiosp->c_iflag & IGNPAR)
+ portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN);
+ if (tiosp->c_iflag & IGNBRK)
+ portp->rxignoremsk |= SR_RXBREAK;
+
+ portp->rxmarkmsk = SR_RXOVERRUN;
+ if (tiosp->c_iflag & (INPCK | PARMRK))
+ portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
+ if (tiosp->c_iflag & BRKINT)
+ portp->rxmarkmsk |= SR_RXBREAK;
/*
- * All cd1400's are initialized (if found!). Now go through and setup
- * each ports data structures. Also init the LIVR register of cd1400
- * for each port.
+ * Go through the char size, parity and stop bits and set all the
+ * option register appropriately.
*/
- ioaddr = panelp->iobase;
- for (i = 0; (i < panelp->nrports); i++) {
- if (brdp->brdtype == BRD_ECHPCI) {
- outb((panelp->pagenr + (i >> 3)), brdp->ioctrl);
- ioaddr = panelp->iobase;
- } else {
- ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3));
- }
- if ((chipmask & (0x1 << (i / 4))) == 0)
- continue;
- portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
- if (portp == (stlport_t *) NULL) {
- printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
- break;
- }
- memset(portp, 0, sizeof(stlport_t));
- portp->magic = STL_PORTMAGIC;
- portp->portnr = i;
- portp->brdnr = panelp->brdnr;
- portp->panelnr = panelp->panelnr;
- portp->ioaddr = ioaddr;
- portp->uartaddr = (i & 0x04) << 5;
- portp->pagenr = panelp->pagenr + (i >> 3);
- portp->clk = brdp->clk;
- portp->baud_base = STL_BAUDBASE;
- portp->close_delay = STL_CLOSEDELAY;
- portp->closing_wait = 30 * HZ;
- portp->normaltermios = stl_deftermios;
- portp->callouttermios = stl_deftermios;
- portp->tqueue.routine = stl_offintr;
- portp->tqueue.data = portp;
- portp->stats.brd = portp->brdnr;
- portp->stats.panel = portp->panelnr;
- portp->stats.port = portp->portnr;
- stl_setreg(portp, CAR, (i & 0x03));
- stl_setreg(portp, LIVR, (i << 3));
- portp->hwid = stl_getreg(portp, GFRCR);
- panelp->ports[i] = portp;
+ switch (tiosp->c_cflag & CSIZE) {
+ case CS5:
+ mr1 |= MR1_CS5;
+ break;
+ case CS6:
+ mr1 |= MR1_CS6;
+ break;
+ case CS7:
+ mr1 |= MR1_CS7;
+ break;
+ default:
+ mr1 |= MR1_CS8;
+ break;
+ }
+
+ if (tiosp->c_cflag & CSTOPB)
+ mr2 |= MR2_STOP2;
+ else
+ mr2 |= MR2_STOP1;
+
+ if (tiosp->c_cflag & PARENB) {
+ if (tiosp->c_cflag & PARODD)
+ mr1 |= (MR1_PARENB | MR1_PARODD);
+ else
+ mr1 |= (MR1_PARENB | MR1_PAREVEN);
+ } else {
+ mr1 |= MR1_PARNONE;
}
- BRDDISABLE(panelp->brdnr);
- return(0);
-}
-
-/*****************************************************************************/
+ mr1 |= MR1_ERRBLOCK;
/*
- * Try to find and initialize an EasyIO board.
+ * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
+ * space for hardware flow control and the like. This should be set to
+ * VMIN.
*/
+ mr2 |= MR2_RXFIFOHALF;
-static int stl_initeio(stlbrd_t *brdp)
-{
- stlpanel_t *panelp;
- unsigned int status;
- int rc;
-
-#if DEBUG
- printk("stl_initeio(brdp=%x)\n", (int) brdp);
-#endif
-
- brdp->ioctrl = brdp->ioaddr1 + 1;
- brdp->iostatus = brdp->ioaddr1 + 2;
- brdp->clk = EIO_CLK;
-
- status = inb(brdp->iostatus);
- switch (status & EIO_IDBITMASK) {
- case EIO_8PORTM:
- brdp->clk = EIO_CLK8M;
- /* fall thru */
- case EIO_8PORTRS:
- case EIO_8PORTDI:
- brdp->nrports = 8;
- break;
- case EIO_4PORTRS:
- brdp->nrports = 4;
- break;
- default:
- return(-ENODEV);
+/*
+ * Calculate the baud rate timers. For now we will just assume that
+ * the input and output baud are the same. The sc26198 has a fixed
+ * baud rate table, so only discrete baud rates possible.
+ */
+ baudrate = tiosp->c_cflag & CBAUD;
+ if (baudrate & CBAUDEX) {
+ baudrate &= ~CBAUDEX;
+ if ((baudrate < 1) || (baudrate > 5))
+ tiosp->c_cflag &= ~CBAUDEX;
+ else
+ baudrate += 15;
+ }
+ baudrate = stl_baudrates[baudrate];
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baudrate = 57600;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baudrate = 115200;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ baudrate = (portp->baud_base / portp->custom_divisor);
}
+ if (baudrate > STL_MAXBAUD)
+ baudrate = STL_MAXBAUD;
- request_region(brdp->ioaddr1, 8, "serial(EIO)");
+ if (baudrate > 0) {
+ for (clk = 0; (clk < SC26198_NRBAUDS); clk++) {
+ if (baudrate <= sc26198_baudtable[clk])
+ break;
+ }
+ }
/*
- * Check that the supplied IRQ is good and then use it to setup the
- * programmable interrupt bits on EIO board. Also set the edge/level
- * triggered interrupt bit.
+ * Check what form of modem signaling is required and set it up.
*/
- if ((brdp->irq < 0) || (brdp->irq > 15) ||
- (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
- printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
- return(-EINVAL);
+ if (tiosp->c_cflag & CLOCAL) {
+ portp->flags &= ~ASYNC_CHECK_CD;
+ } else {
+ iopr |= IOPR_DCDCOS;
+ imron |= IR_IOPORT;
+ portp->flags |= ASYNC_CHECK_CD;
}
- outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
- panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
- if (panelp == (stlpanel_t *) NULL) {
- printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
- return(-ENOMEM);
+/*
+ * Setup sc26198 enhanced modes if we can. In particular we want to
+ * handle as much of the flow control as possible automatically. As
+ * well as saving a few CPU cycles it will also greatly improve flow
+ * control reliability.
+ */
+ if (tiosp->c_iflag & IXON) {
+ mr0 |= MR0_SWFTX | MR0_SWFT;
+ imron |= IR_XONXOFF;
+ } else {
+ imroff |= IR_XONXOFF;
}
- memset(panelp, 0, sizeof(stlpanel_t));
-
- panelp->magic = STL_PANELMAGIC;
- panelp->brdnr = brdp->brdnr;
- panelp->panelnr = 0;
- panelp->nrports = brdp->nrports;
- panelp->iobase = brdp->ioaddr1;
- panelp->hwid = status;
- brdp->panels[0] = panelp;
- brdp->nrpanels = 1;
- brdp->state |= BRD_FOUND;
- brdp->hwid = status;
- rc = stl_mapirq(brdp->irq);
- return(rc);
-}
+ if (tiosp->c_iflag & IXOFF)
+ mr0 |= MR0_SWFRX;
-/*****************************************************************************/
+ if (tiosp->c_cflag & CRTSCTS) {
+ mr2 |= MR2_AUTOCTS;
+ mr1 |= MR1_AUTORTS;
+ }
/*
- * Try to find an ECH board and initialize it. This code is capable of
- * dealing with all types of ECH board.
+ * All sc26198 register values calculated so go through and set
+ * them all up.
*/
-static int stl_initech(stlbrd_t *brdp)
-{
- stlpanel_t *panelp;
- unsigned int status, nxtid;
- int panelnr, ioaddr, i;
-
#if DEBUG
- printk("stl_initech(brdp=%x)\n", (int) brdp);
+ printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+ printk(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
+ printk(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
+ printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
#endif
- status = 0;
-
-/*
- * Set up the initial board register contents for boards. This varies a
- * bit between the different board types. So we need to handle each
- * separately. Also do a check that the supplied IRQ is good.
- */
- if (brdp->brdtype == BRD_ECH) {
- brdp->ioctrl = brdp->ioaddr1 + 1;
- brdp->iostatus = brdp->ioaddr1 + 1;
- status = inb(brdp->iostatus);
- if ((status & ECH_IDBITMASK) != ECH_ID)
- return(-ENODEV);
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, IMR, 0);
+ stl_sc26198updatereg(portp, MR0, mr0);
+ stl_sc26198updatereg(portp, MR1, mr1);
+ stl_sc26198setreg(portp, CCR, CR_RXERRBLOCK);
+ stl_sc26198updatereg(portp, MR2, mr2);
+ stl_sc26198updatereg(portp, IOPIOR,
+ ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
- if ((brdp->irq < 0) || (brdp->irq > 15) ||
- (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
- printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
- return(-EINVAL);
- }
- status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
- status |= (stl_vecmap[brdp->irq] << 1);
- outb((status | ECH_BRDRESET), brdp->ioaddr1);
- brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
- outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
- outb(status, brdp->ioaddr1);
+ if (baudrate > 0) {
+ stl_sc26198setreg(portp, TXCSR, clk);
+ stl_sc26198setreg(portp, RXCSR, clk);
+ }
- request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
- request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
- } else if (brdp->brdtype == BRD_ECHMC) {
- brdp->ioctrl = brdp->ioaddr1 + 0x20;
- brdp->iostatus = brdp->ioctrl;
- status = inb(brdp->iostatus);
- if ((status & ECH_IDBITMASK) != ECH_ID)
- return(-ENODEV);
+ stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
+ stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
- if ((brdp->irq < 0) || (brdp->irq > 15) ||
- (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
- printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
- return(-EINVAL);
- }
- outb(ECHMC_BRDRESET, brdp->ioctrl);
- outb(ECHMC_INTENABLE, brdp->ioctrl);
+ ipr = stl_sc26198getreg(portp, IPR);
+ if (ipr & MSVR1_DCD)
+ portp->sigs &= ~TIOCM_CD;
+ else
+ portp->sigs |= TIOCM_CD;
- request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
- } else if (brdp->brdtype == BRD_ECHPCI) {
- brdp->ioctrl = brdp->ioaddr1 + 2;
- request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
- request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
- }
+ portp->imr = (portp->imr & ~imroff) | imron;
+ stl_sc26198setreg(portp, IMR, portp->imr);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
- brdp->clk = ECH_CLK;
- brdp->hwid = status;
+/*****************************************************************************/
/*
- * Scan through the secondary io address space looking for panels.
- * As we find'em allocate and initialize panel structures for each.
+ * Set the state of the DTR and RTS signals.
*/
- ioaddr = brdp->ioaddr2;
- panelnr = 0;
- nxtid = 0;
- for (i = 0; (i < STL_MAXPANELS); i++) {
- if (brdp->brdtype == BRD_ECHPCI) {
- outb(nxtid, brdp->ioctrl);
- ioaddr = brdp->ioaddr2;
- }
- status = inb(ioaddr + ECH_PNLSTATUS);
- if ((status & ECH_PNLIDMASK) != nxtid)
- break;
- panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
- if (panelp == (stlpanel_t *) NULL) {
- printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
- break;
- }
- memset(panelp, 0, sizeof(stlpanel_t));
- panelp->magic = STL_PANELMAGIC;
- panelp->brdnr = brdp->brdnr;
- panelp->panelnr = panelnr;
- panelp->iobase = ioaddr;
- panelp->pagenr = nxtid;
- panelp->hwid = status;
- if (status & ECH_PNL16PORT) {
- if ((brdp->nrports + 16) > 32)
- break;
- panelp->nrports = 16;
- panelp->ackmask = 0x80;
- brdp->nrports += 16;
- ioaddr += (EREG_BANKSIZE * 2);
- nxtid += 2;
- } else {
- panelp->nrports = 8;
- panelp->ackmask = 0xc0;
- brdp->nrports += 8;
- ioaddr += EREG_BANKSIZE;
- nxtid++;
- }
- brdp->panels[panelnr++] = panelp;
- brdp->nrpanels++;
- if (ioaddr >= (brdp->ioaddr2 + 0x20))
- break;
- }
+static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts)
+{
+ unsigned char iopioron, iopioroff;
+ unsigned long flags;
- if (brdp->brdtype == BRD_ECH)
- outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+#if DEBUG
+ printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
- brdp->state |= BRD_FOUND;
- i = stl_mapirq(brdp->irq);
- return(i);
+ iopioron = 0;
+ iopioroff = 0;
+ if (dtr == 0)
+ iopioroff |= IPR_DTR;
+ else if (dtr > 0)
+ iopioron |= IPR_DTR;
+ if (rts == 0)
+ iopioroff |= IPR_RTS;
+ else if (rts > 0)
+ iopioron |= IPR_RTS;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, IOPIOR,
+ ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
}
/*****************************************************************************/
/*
- * Initialize and configure the specified board.
- * Scan through all the boards in the configuration and see what we
- * can find. Handle EIO and the ECH boards a little differently here
- * since the initial search and setup is too different.
+ * Return the state of the signals.
*/
-static int stl_brdinit(stlbrd_t *brdp)
+static int stl_sc26198getsignals(stlport_t *portp)
{
- int i;
+ unsigned char ipr;
+ unsigned long flags;
+ int sigs;
#if DEBUG
- printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+ printk("stl_sc26198getsignals(portp=%x)\n", (int) portp);
#endif
- switch (brdp->brdtype) {
- case BRD_EASYIO:
- stl_initeio(brdp);
- break;
- case BRD_ECH:
- case BRD_ECHMC:
- case BRD_ECHPCI:
- stl_initech(brdp);
- break;
- default:
- printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
- return(ENODEV);
- }
-
- stl_brds[brdp->brdnr] = brdp;
- if ((brdp->state & BRD_FOUND) == 0) {
- printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
- return(ENODEV);
- }
-
- for (i = 0; (i < STL_MAXPANELS); i++)
- if (brdp->panels[i] != (stlpanel_t *) NULL)
- stl_initports(brdp, brdp->panels[i]);
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ ipr = stl_sc26198getreg(portp, IPR);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
- printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
- return(0);
+ sigs = 0;
+ sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
+ sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
+ sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
+ sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
+ return(sigs);
}
/*****************************************************************************/
/*
- * Find any ECH-PCI boards that might be installed. Initialize each
- * one as it is found.
+ * Enable/Disable the Transmitter and/or Receiver.
*/
-#ifdef CONFIG_PCI
-
-static int stl_findpcibrds()
+static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx)
{
- stlbrd_t *brdp;
- unsigned char busnr, devnr, irq;
- unsigned short class;
- unsigned int ioaddr;
- int i, rc;
+ unsigned char ccr;
+ unsigned long flags;
#if DEBUG
- printk("stl_findpcibrds()\n");
+ printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
#endif
- if (pcibios_present()) {
- for (i = 0; (i < STL_MAXBRDS); i++) {
- if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
- break;
+ ccr = portp->crenable;
+ if (tx == 0)
+ ccr &= ~CR_TXENABLE;
+ else if (tx > 0)
+ ccr |= CR_TXENABLE;
+ if (rx == 0)
+ ccr &= ~CR_RXENABLE;
+ else if (rx > 0)
+ ccr |= CR_RXENABLE;
-/*
- * Found a device on the PCI bus that has our vendor and
- * device ID. Need to check now that it is really us.
- */
- if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
- printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
- continue;
- }
- if (class == PCI_CLASS_STORAGE_IDE)
- continue;
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, SCCR, ccr);
+ BRDDISABLE(portp->brdnr);
+ portp->crenable = ccr;
+ restore_flags(flags);
+}
- if (stl_nrbrds >= STL_MAXBRDS) {
- printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
- break;
- }
+/*****************************************************************************/
/*
- * We have a Stallion board. Allocate a board structure
- * and initialize it. Read its IO and IRQ resources
- * from conf space.
+ * Start/stop the Transmitter and/or Receiver.
*/
- brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
- if (brdp == (stlbrd_t *) NULL) {
- printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
- return(-ENOMEM);
- }
- memset(brdp, 0, sizeof(stlbrd_t));
- brdp->magic = STL_BOARDMAGIC;
- brdp->brdnr = stl_nrbrds++;
- brdp->brdtype = BRD_ECHPCI;
- if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
- printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
- continue;
- }
- brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx)
+{
+ unsigned char imr;
+ unsigned long flags;
- if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
- printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
- continue;
- }
- brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
#if DEBUG
- printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+ printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
+ rx, tx);
#endif
- if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
- printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
- continue;
- }
- brdp->irq = irq;
-
- stl_brdinit(brdp);
- }
- }
+ imr = portp->imr;
+ if (tx == 0)
+ imr &= ~IR_TXRDY;
+ else if (tx == 1)
+ imr |= IR_TXRDY;
+ if (rx == 0)
+ imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
+ else if (rx > 0)
+ imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
- return(0);
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, IMR, imr);
+ BRDDISABLE(portp->brdnr);
+ portp->imr = imr;
+ if (tx > 0)
+ set_bit(ASYI_TXBUSY, &portp->istate);
+ restore_flags(flags);
}
-#endif
-
/*****************************************************************************/
/*
- * Scan through all the boards in the configuration and see what we
- * can find. Handle EIO and the ECH boards a little differently here
- * since the initial search and setup is too different.
+ * Disable all interrupts from this port.
*/
-static int stl_initbrds()
+static void stl_sc26198disableintrs(stlport_t *portp)
{
- stlbrd_t *brdp;
- stlconf_t *confp;
- int i;
+ unsigned long flags;
#if DEBUG
- printk("stl_initbrds()\n");
+ printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp);
#endif
- if (stl_nrbrds > STL_MAXBRDS) {
- printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
- stl_nrbrds = STL_MAXBRDS;
- }
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ portp->imr = 0;
+ stl_sc26198setreg(portp, IMR, 0);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
-/*
- * Firstly scan the list of static boards configured. Allocate
- * resources and initialize the boards as found.
- */
- for (i = 0; (i < stl_nrbrds); i++) {
- confp = &stl_brdconf[i];
- brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
- if (brdp == (stlbrd_t *) NULL) {
- printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
- return(-ENOMEM);
- }
- memset(brdp, 0, sizeof(stlbrd_t));
+/*****************************************************************************/
- brdp->magic = STL_BOARDMAGIC;
- brdp->brdnr = i;
- brdp->brdtype = confp->brdtype;
- brdp->ioaddr1 = confp->ioaddr1;
- brdp->ioaddr2 = confp->ioaddr2;
- brdp->irq = confp->irq;
- brdp->irqtype = confp->irqtype;
- stl_brdinit(brdp);
- }
+static void stl_sc26198sendbreak(stlport_t *portp, long len)
+{
+ unsigned long flags;
-#ifdef CONFIG_PCI
-/*
- * If the PCI BIOS support is compiled in then let's go looking for
- * ECH-PCI boards.
- */
- stl_findpcibrds();
+#if DEBUG
+ printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
#endif
- return(0);
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + (len / (1000 / HZ));
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
+ BRDDISABLE(portp->brdnr);
+ portp->stats.txbreaks++;
+
+ schedule();
+
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
}
/*****************************************************************************/
/*
- * Return the board stats structure to user app.
+ * Take flow control actions...
*/
-static int stl_getbrdstats(combrd_t *bp)
+static void stl_sc26198flowctrl(stlport_t *portp, int state)
{
- stlbrd_t *brdp;
- stlpanel_t *panelp;
- int i;
-
- copy_from_user(&stl_brdstats, bp, sizeof(combrd_t));
- if (stl_brdstats.brd >= STL_MAXBRDS)
- return(-ENODEV);
- brdp = stl_brds[stl_brdstats.brd];
- if (brdp == (stlbrd_t *) NULL)
- return(-ENODEV);
-
- memset(&stl_brdstats, 0, sizeof(combrd_t));
- stl_brdstats.brd = brdp->brdnr;
- stl_brdstats.type = brdp->brdtype;
- stl_brdstats.hwid = brdp->hwid;
- stl_brdstats.state = brdp->state;
- stl_brdstats.ioaddr = brdp->ioaddr1;
- stl_brdstats.ioaddr2 = brdp->ioaddr2;
- stl_brdstats.irq = brdp->irq;
- stl_brdstats.nrpanels = brdp->nrpanels;
- stl_brdstats.nrports = brdp->nrports;
- for (i = 0; (i < brdp->nrpanels); i++) {
- panelp = brdp->panels[i];
- stl_brdstats.panels[i].panel = i;
- stl_brdstats.panels[i].hwid = panelp->hwid;
- stl_brdstats.panels[i].nrports = panelp->nrports;
- }
+ struct tty_struct *tty;
+ unsigned long flags;
+ unsigned char mr0;
- copy_to_user(bp, &stl_brdstats, sizeof(combrd_t));
- return(0);
-}
+#if DEBUG
+ printk("stl_sc26198flowctrl(portp=%x,state=%x)\n", (int) portp, state);
+#endif
-/*****************************************************************************/
+ if (portp == (stlport_t *) NULL)
+ return;
+ tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return;
-/*
- * Resolve the referenced port number into a port struct pointer.
- */
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
-static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
-{
- stlbrd_t *brdp;
- stlpanel_t *panelp;
+ if (state) {
+ if (tty->termios->c_iflag & IXOFF) {
+ mr0 = stl_sc26198getreg(portp, MR0);
+ stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+ stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+ mr0 |= MR0_SWFRX;
+ portp->stats.rxxon++;
+ stl_sc26198setreg(portp, MR0, mr0);
+ }
+/*
+ * Question: should we return RTS to what it was before? It may
+ * have been set by an ioctl... Suppose not, since if you have
+ * hardware flow control set then it is pretty silly to go and
+ * set the RTS line by hand.
+ */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_sc26198setreg(portp, MR1,
+ (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
+ stl_sc26198setreg(portp, IOPIOR,
+ (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
+ portp->stats.rxrtson++;
+ }
+ } else {
+ if (tty->termios->c_iflag & IXOFF) {
+ mr0 = stl_sc26198getreg(portp, MR0);
+ stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+ stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+ mr0 &= ~MR0_SWFRX;
+ portp->stats.rxxoff++;
+ stl_sc26198setreg(portp, MR0, mr0);
+ }
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_sc26198setreg(portp, MR1,
+ (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
+ stl_sc26198setreg(portp, IOPIOR,
+ (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
+ portp->stats.rxrtsoff++;
+ }
+ }
- if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
- return((stlport_t *) NULL);
- brdp = stl_brds[brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return((stlport_t *) NULL);
- if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
- return((stlport_t *) NULL);
- panelp = brdp->panels[panelnr];
- if (panelp == (stlpanel_t *) NULL)
- return((stlport_t *) NULL);
- if ((portnr < 0) || (portnr >= panelp->nrports))
- return((stlport_t *) NULL);
- return(panelp->ports[portnr]);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
}
/*****************************************************************************/
-/*
- * Return the port stats structure to user app. A NULL port struct
- * pointer passed in means that we need to find out from the app
- * what port to get stats for (used through board control device).
- */
-
-static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+static void stl_sc26198flush(stlport_t *portp)
{
- unsigned char *head, *tail;
unsigned long flags;
- if (portp == (stlport_t *) NULL) {
- copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
- portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- }
-
- portp->stats.state = portp->istate;
- portp->stats.flags = portp->flags;
- portp->stats.hwid = portp->hwid;
+#if DEBUG
+ printk("stl_sc26198flush(portp=%x)\n", (int) portp);
+#endif
- portp->stats.ttystate = 0;
- portp->stats.cflags = 0;
- portp->stats.iflags = 0;
- portp->stats.oflags = 0;
- portp->stats.lflags = 0;
- portp->stats.rxbuffered = 0;
+ if (portp == (stlport_t *) NULL)
+ return;
save_flags(flags);
cli();
- if (portp->tty != (struct tty_struct *) NULL) {
- if (portp->tty->driver_data == portp) {
- portp->stats.ttystate = portp->tty->flags;
- portp->stats.rxbuffered = portp->tty->flip.count;
- if (portp->tty->termios != (struct termios *) NULL) {
- portp->stats.cflags = portp->tty->termios->c_cflag;
- portp->stats.iflags = portp->tty->termios->c_iflag;
- portp->stats.oflags = portp->tty->termios->c_oflag;
- portp->stats.lflags = portp->tty->termios->c_lflag;
- }
- }
- }
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_sc26198setreg(portp, SCCR, CR_TXRESET);
+ stl_sc26198setreg(portp, SCCR, portp->crenable);
+ BRDDISABLE(portp->brdnr);
+ portp->tx.tail = portp->tx.head;
restore_flags(flags);
+}
- head = portp->tx.head;
- tail = portp->tx.tail;
- portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+/*****************************************************************************/
- portp->stats.signals = (unsigned long) stl_getsignals(portp);
+/*
+ * If we are TX flow controlled and in IXANY mode then we may
+ * need to unflow control here. We gotta do this because of the
+ * automatic flow control modes of the sc26198.
+ */
+static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty)
+{
+ unsigned char mr0;
- copy_to_user(cp, &portp->stats, sizeof(comstats_t));
- return(0);
+ mr0 = stl_sc26198getreg(portp, MR0);
+ stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+ stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
+ stl_sc26198setreg(portp, MR0, mr0);
+ clear_bit(ASYI_TXFLOWED, &portp->istate);
}
/*****************************************************************************/
/*
- * Clear the port stats structure. We also return it zeroed out...
+ * Interrupt service routine for sc26198 panels.
*/
-static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
{
- if (portp == (stlport_t *) NULL) {
- copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
- portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- }
+ stlport_t *portp;
+ unsigned int iack;
- memset(&portp->stats, 0, sizeof(comstats_t));
- portp->stats.brd = portp->brdnr;
- portp->stats.panel = portp->panelnr;
- portp->stats.port = portp->portnr;
- copy_to_user(cp, &portp->stats, sizeof(comstats_t));
- return(0);
+/*
+ * Work around bug in sc26198 chip... Cannot have A6 address
+ * line of UART high, else iack will be returned as 0.
+ */
+ outb(0, (iobase + 1));
+
+ iack = inb(iobase + XP_IACK);
+ portp = panelp->ports[(iack & IVR_CHANMASK)];
+
+ if (iack & IVR_RXDATA)
+ stl_sc26198rxisr(portp, iack);
+ else if (iack & IVR_TXDATA)
+ stl_sc26198txisr(portp);
+ else
+ stl_sc26198otherisr(portp, iack);
}
/*****************************************************************************/
/*
- * Return the entire driver ports structure to a user app.
+ * Transmit interrupt handler. This has gotta be fast! Handling TX
+ * chars is pretty simple, stuff as many as possible from the TX buffer
+ * into the sc26198 FIFO.
+ * In practice it is possible that interrupts are enabled but that the
+ * port has been hung up. Need to handle not having any TX buffer here,
+ * this is done by using the side effect that head and tail will also
+ * be NULL if the buffer has been freed.
*/
-static int stl_getportstruct(unsigned long arg)
+static void stl_sc26198txisr(stlport_t *portp)
{
- stlport_t *portp;
+ unsigned int ioaddr;
+ unsigned char mr0;
+ int len, stlen;
+ char *head, *tail;
- copy_from_user(&stl_dummyport, (void *) arg, sizeof(stlport_t));
- portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
- stl_dummyport.portnr);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- copy_to_user((void *) arg, portp, sizeof(stlport_t));
- return(0);
+#if DEBUG
+ printk("stl_sc26198txisr(portp=%x)\n", (int) portp);
+#endif
+
+ ioaddr = portp->ioaddr;
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+ if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+ set_bit(ASYI_TXLOW, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ }
+
+ if (len == 0) {
+ outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
+ mr0 = inb(ioaddr + XP_DATA);
+ if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
+ portp->imr &= ~IR_TXRDY;
+ outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
+ outb(portp->imr, (ioaddr + XP_DATA));
+ clear_bit(ASYI_TXBUSY, &portp->istate);
+ } else {
+ mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
+ outb(mr0, (ioaddr + XP_DATA));
+ }
+ } else {
+ len = MIN(len, SC26198_TXFIFOSIZE);
+ portp->stats.txtotal += len;
+ stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+ outb((GTXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+ outsb((ioaddr + XP_DATA), tail, stlen);
+ len -= stlen;
+ tail += stlen;
+ if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+ tail = portp->tx.buf;
+ if (len > 0) {
+ outsb((ioaddr + XP_DATA), tail, len);
+ tail += len;
+ }
+ portp->tx.tail = tail;
+ }
}
/*****************************************************************************/
/*
- * Return the entire driver board structure to a user app.
+ * Receive character interrupt handler. Determine if we have good chars
+ * or bad chars and then process appropriately. Good chars are easy
+ * just shove the lot into the RX buffer and set all status byte to 0.
+ * If a bad RX char then process as required. This routine needs to be
+ * fast! In practice it is possible that we get an interrupt on a port
+ * that is closed. This can happen on hangups - since they completely
+ * shutdown a port not in user context. Need to handle this case.
*/
-static int stl_getbrdstruct(unsigned long arg)
+static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack)
{
- stlbrd_t *brdp;
+ struct tty_struct *tty;
+ unsigned int len, buflen, ioaddr;
- copy_from_user(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
- if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
- return(-ENODEV);
- brdp = stl_brds[stl_dummybrd.brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return(-ENODEV);
- copy_to_user((void *) arg, brdp, sizeof(stlbrd_t));
- return(0);
+#if DEBUG
+ printk("stl_sc26198rxisr(portp=%x,iack=%x)\n", (int) portp, iack);
+#endif
+
+ tty = portp->tty;
+ ioaddr = portp->ioaddr;
+ outb((GIBCR | portp->uartaddr), (ioaddr + XP_ADDR));
+ len = inb(ioaddr + XP_DATA) + 1;
+
+ if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
+ if ((tty == (struct tty_struct *) NULL) ||
+ (tty->flip.char_buf_ptr == (char *) NULL) ||
+ ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+ outb((GRXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+ insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
+ portp->stats.rxlost += len;
+ portp->stats.rxtotal += len;
+ } else {
+ len = MIN(len, buflen);
+ if (len > 0) {
+ outb((GRXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+ insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len);
+ memset(tty->flip.flag_buf_ptr, 0, len);
+ tty->flip.flag_buf_ptr += len;
+ tty->flip.char_buf_ptr += len;
+ tty->flip.count += len;
+ tty_schedule_flip(tty);
+ portp->stats.rxtotal += len;
+ }
+ }
+ } else {
+ stl_sc26198rxbadchars(portp);
+ }
+
+/*
+ * If we are TX flow controlled and in IXANY mode then we may need
+ * to unflow control here. We gotta do this because of the automatic
+ * flow control modes of the sc26198.
+ */
+ if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
+ if ((tty != (struct tty_struct *) NULL) &&
+ (tty->termios != (struct termios *) NULL) &&
+ (tty->termios->c_iflag & IXANY)) {
+ stl_sc26198txunflow(portp, tty);
+ }
+ }
}
/*****************************************************************************/
/*
- * The "staliomem" device is also required to do some special operations
- * on the board and/or ports. In this driver it is mostly used for stats
- * collection.
+ * Process an RX bad character.
*/
-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch)
{
- int brdnr, rc;
+ struct tty_struct *tty;
+ unsigned int ioaddr;
-#if DEBUG
- printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+ tty = portp->tty;
+ ioaddr = portp->ioaddr;
+
+ if (status & SR_RXPARITY)
+ portp->stats.rxparity++;
+ if (status & SR_RXFRAMING)
+ portp->stats.rxframing++;
+ if (status & SR_RXOVERRUN)
+ portp->stats.rxoverrun++;
+ if (status & SR_RXBREAK)
+ portp->stats.rxbreaks++;
+
+ if ((tty != (struct tty_struct *) NULL) &&
+ ((portp->rxignoremsk & status) == 0)) {
+ if (portp->rxmarkmsk & status) {
+ if (status & SR_RXBREAK) {
+ status = TTY_BREAK;
+#ifndef MODULE
+ if (portp->flags & ASYNC_SAK) {
+ do_SAK(tty);
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ }
#endif
+ } else if (status & SR_RXPARITY) {
+ status = TTY_PARITY;
+ } else if (status & SR_RXFRAMING) {
+ status = TTY_FRAME;
+ } else if(status & SR_RXOVERRUN) {
+ status = TTY_OVERRUN;
+ } else {
+ status = 0;
+ }
+ } else {
+ status = 0;
+ }
- brdnr = MINOR(ip->i_rdev);
- if (brdnr >= STL_MAXBRDS)
- return(-ENODEV);
- rc = 0;
+ if (tty->flip.char_buf_ptr != (char *) NULL) {
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ *tty->flip.flag_buf_ptr++ = status;
+ *tty->flip.char_buf_ptr++ = ch;
+ tty->flip.count++;
+ }
+ tty_schedule_flip(tty);
+ }
- switch (cmd) {
- case COM_GETPORTSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
- rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
- break;
- case COM_CLRPORTSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
- rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
- break;
- case COM_GETBRDSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
- rc = stl_getbrdstats((combrd_t *) arg);
- break;
- case COM_READPORT:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
- rc = stl_getportstruct(arg);
- break;
- case COM_READBOARD:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
- rc = stl_getbrdstruct(arg);
- break;
- default:
- rc = -ENOIOCTLCMD;
- break;
+ if (status == 0)
+ portp->stats.rxtotal++;
}
-
- return(rc);
}
/*****************************************************************************/
-int stl_init(void)
-{
- printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+/*
+ * Process all characters in the RX FIFO of the UART. Check all char
+ * status bytes as well, and process as required. We need to check
+ * all bytes in the FIFO, in case some more enter the FIFO while we
+ * are here. To get the exact character error type we need to switch
+ * into CHAR error mode (that is why we need to make sure we empty
+ * the FIFO).
+ */
- stl_initbrds();
+static void stl_sc26198rxbadchars(stlport_t *portp)
+{
+ unsigned char status, mr1;
+ char ch;
/*
- * Allocate a temporary write buffer.
+ * To get the precise error type for each character we must switch
+ * back into CHAR error mode.
*/
- stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
- if (stl_tmpwritebuf == (char *) NULL)
- printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+ mr1 = stl_sc26198getreg(portp, MR1);
+ stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
+
+ while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
+ stl_sc26198setreg(portp, CCR, CR_CLEARRXERR);
+ ch = stl_sc26198getreg(portp, RXFIFO);
+ stl_sc26198rxbadch(portp, status, ch);
+ }
/*
- * Set up a character driver for per board stuff. This is mainly used
- * to do stats ioctls on the ports.
+ * To get correct interrupt class we must switch back into BLOCK
+ * error mode.
*/
- if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
- printk("STALLION: failed to register serial board device\n");
+ stl_sc26198setreg(portp, MR1, mr1);
+}
+
+/*****************************************************************************/
/*
- * Set up the tty driver structure and register us as a driver.
- * Also setup the callout tty device.
+ * Other interrupt handler. This includes modem signals, flow
+ * control actions, etc. Most stuff is left to off-level interrupt
+ * processing time.
*/
- memset(&stl_serial, 0, sizeof(struct tty_driver));
- stl_serial.magic = TTY_DRIVER_MAGIC;
- stl_serial.name = stl_serialname;
- stl_serial.major = STL_SERIALMAJOR;
- stl_serial.minor_start = 0;
- stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
- stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
- stl_serial.subtype = STL_DRVTYPSERIAL;
- stl_serial.init_termios = stl_deftermios;
- stl_serial.flags = TTY_DRIVER_REAL_RAW;
- stl_serial.refcount = &stl_refcount;
- stl_serial.table = stl_ttys;
- stl_serial.termios = stl_termios;
- stl_serial.termios_locked = stl_termioslocked;
-
- stl_serial.open = stl_open;
- stl_serial.close = stl_close;
- stl_serial.write = stl_write;
- stl_serial.put_char = stl_putchar;
- stl_serial.flush_chars = stl_flushchars;
- stl_serial.write_room = stl_writeroom;
- stl_serial.chars_in_buffer = stl_charsinbuffer;
- stl_serial.ioctl = stl_ioctl;
- stl_serial.set_termios = stl_settermios;
- stl_serial.throttle = stl_throttle;
- stl_serial.unthrottle = stl_unthrottle;
- stl_serial.stop = stl_stop;
- stl_serial.start = stl_start;
- stl_serial.hangup = stl_hangup;
- stl_serial.flush_buffer = stl_flushbuffer;
- stl_callout = stl_serial;
- stl_callout.name = stl_calloutname;
- stl_callout.major = STL_CALLOUTMAJOR;
- stl_callout.subtype = STL_DRVTYPCALLOUT;
+static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack)
+{
+ unsigned char cir, ipr, xisr;
- if (tty_register_driver(&stl_serial))
- printk("STALLION: failed to register serial driver\n");
- if (tty_register_driver(&stl_callout))
- printk("STALLION: failed to register callout driver\n");
+#if DEBUG
+ printk("stl_sc26198otherisr(portp=%x,iack=%x)\n", (int) portp, iack);
+#endif
- return(0);
+ cir = stl_sc26198getglobreg(portp, CIR);
+
+ switch (cir & CIR_SUBTYPEMASK) {
+ case CIR_SUBCOS:
+ ipr = stl_sc26198getreg(portp, IPR);
+ if (ipr & IPR_DCDCHANGE) {
+ set_bit(ASYI_DCDCHANGE, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ portp->stats.modem++;
+ }
+ break;
+ case CIR_SUBXONXOFF:
+ xisr = stl_sc26198getreg(portp, XISR);
+ if (xisr & XISR_RXXONGOT) {
+ set_bit(ASYI_TXFLOWED, &portp->istate);
+ portp->stats.txxoff++;
+ }
+ if (xisr & XISR_RXXOFFGOT) {
+ clear_bit(ASYI_TXFLOWED, &portp->istate);
+ portp->stats.txxon++;
+ }
+ break;
+ case CIR_SUBBREAK:
+ stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
+ stl_sc26198rxbadchars(portp);
+ break;
+ default:
+ /*printk("%s(%d): unknown other intr cir=%x, iack=%x!\n", __FILE__, __LINE__, cir, iack);*/
+ break;
+ }
}
/*****************************************************************************/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov