fixes for Cyclades driver

Randolph Bentson (bentson@grieg.holmsjoen.com)
Fri, 10 Apr 1998 13:35:24 -0700


The following fixes have been developed among a small community
of Cyclades users (and the staff at Cyclades). They appear
ready for prime-time...or at least rigorous testing. I've found
it works for me under 2.1.94. The "NEW_PCI" can be undefined
for older 2.1 kernels.

Randoph Bentson

--- linux/drivers/char/cyclades.c.2.1.94 Tue Apr 7 13:05:10 1998
+++ linux/drivers/char/cyclades.c Fri Apr 10 12:43:13 1998
@@ -1,6 +1,9 @@
#define BLOCKMOVE
+#define NEW_INTR_FLOW
+#define Z_WAKE
+#define NEW_PCI
static char rcsid[] =
-"$Revision: 2.1.1.1 $$Date: 1997/12/03 17:31:19 $";
+"$Revision: 2.2.1.1 $$Date: 1998/03/19 16:43:12 $";

/*
* linux/drivers/char/cyclades.c
@@ -30,11 +33,28 @@
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 2.2.1.1 1998/03/19 16:43:12 ivan
+ * added conditional compilation for new/old PCI structure support;
+ * removed kernel series (2.0.x / 2.1.x) conditional compilation.
+ *
+ * Revision 2.1.1.3 1998/03/16 18:01:12 ivan
+ * cleaned up the data loss fix;
+ * fixed XON/XOFF handling once more (Cyclades-Z);
+ * general revision in the driver routines;
+ * introduction of a mechanism to prevent data loss with slow
+ * printers, by forcing a delay before closing the port.
+ *
+ * Revision 2.1.1.2 1998/02/17 16:50:00 ivan
+ * fixed detection/handling of new CD1400 in Ye boards;
+ * fixed XON/XOFF handling (Cyclades-Z);
+ * fixed data loss caused by a premature port close;
+ * introduction of a flag that holds the CD1400 version ID per port
+ * (used by the CYGETCD1400VER new ioctl).
+ *
* Revision 2.1.1.1 1997/12/03 17:31:19 ivan
- * Code review for the module cleanup routine (fixed memory leak);
+ * Code review for the module cleanup routine;
* fixed RTS and DTR status report for new CD1400's in get_modem_info;
- * purged conditional code for older kernels;
- * includes anonymous changes regarding signal_pending
+ * includes anonymous changes regarding signal_pending.
*
* Revision 2.1 1997/11/01 17:42:41 ivan
* Changes in the driver to support Alpha systems (except 8Zo V_1);
@@ -461,17 +481,16 @@
#define ZO_V2 1
#define ZE_V1 2

-#define SERIAL_PARANOIA_CHECK
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_THROTTLE
-#undef SERIAL_DEBUG_OTHER
-#undef SERIAL_DEBUG_IO
-#undef SERIAL_DEBUG_COUNT
-#undef SERIAL_DEBUG_DTR
-#undef CYCLOM_16Y_HACK
-#undef CYCLOM_ENABLE_MONITORING
-#undef CY_PCI_DEBUG
-
+#define SERIAL_PARANOIA_CHECK
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_THROTTLE
+#undef SERIAL_DEBUG_OTHER
+#undef SERIAL_DEBUG_IO
+#undef SERIAL_DEBUG_COUNT
+#undef SERIAL_DEBUG_DTR
+#undef CYCLOM_16Y_HACK
+#undef CYCLOM_ENABLE_MONITORING
+#undef CY_PCI_DEBUG

#if 0
#define PAUSE __asm__("nop");
@@ -525,6 +544,9 @@
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
+#ifndef NEW_PCI
+#include <linux/bios32.h>
+#endif
#include <linux/pci.h>

#include <linux/version.h>
@@ -566,10 +588,9 @@
#define SERIAL_TYPE_NORMAL 1
#define SERIAL_TYPE_CALLOUT 2

+static DECLARE_TASK_QUEUE(tq_cyclades);

-DECLARE_TASK_QUEUE(tq_cyclades);
-
-struct tty_driver cy_serial_driver, cy_callout_driver;
+static struct tty_driver cy_serial_driver, cy_callout_driver;

static volatile int cy_irq_triggered;
static volatile int cy_triggered;
@@ -856,7 +877,6 @@
run_task_queue(&tq_cyclades);
} /* do_cyclades_bh */

-
static void
do_softint(void *private_)
{
@@ -883,6 +903,11 @@
}
wake_up_interruptible(&tty->write_wait);
}
+#ifdef Z_WAKE
+ if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) {
+ wake_up_interruptible(&info->shutdown_wait);
+ }
+#endif
} /* do_softint */


@@ -1014,6 +1039,7 @@
unsigned long timeout;
volatile ucchar *base_addr;
int index;
+ unsigned long flags;

index = 0; /* IRQ probing is only for ISA */
base_addr = address;
@@ -1023,13 +1049,13 @@
* Enable interrupts and see who answers
*/
cy_irq_triggered = 0;
- cli();
+ save_flags(flags); cli();
cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
cy_writeb((u_long)base_addr+(CySRER<<index),
cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
probe_ready = 1;
- sti();
+ restore_flags(flags);

timeout = jiffies+(HZ/50);
while (timeout >= jiffies) {
@@ -1050,7 +1076,7 @@
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
int retries;
- unsigned long flags;
+ unsigned long flags;

/* Turn on interrupts (they may be off) */
save_flags(flags); sti();
@@ -1346,7 +1372,7 @@
/* delay a bit */
cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
- if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) {
+ if (info->chip_rev >= CD1400_REV_J ) {
/* It is a CD1400 rev. J or later */
cy_writeb((u_long)base_addr + (CyTDR<<index),
info->x_break*500/HZ);
@@ -1361,7 +1387,30 @@
info->x_break = 0;
}

+#ifdef NEW_INTR_FLOW
+ if (!info->xmit_cnt){
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+ cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
+ goto txdone;
+ }
+ if (info->xmit_buf == 0){
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+ goto txdone;
+ }
+ if (info->tty->stopped || info->tty->hw_stopped){
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+ goto txdone;
+ }
+#endif
while (char_count-- > 0){
+#ifdef NEW_INTR_FLOW
+ if (!info->xmit_cnt){
+ goto txdone;
+ }
+#else
if (!info->xmit_cnt){
cy_writeb((u_long)base_addr+(CySRER<<index),
cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
@@ -1375,8 +1424,8 @@
if (info->tty->stopped || info->tty->hw_stopped){
cy_writeb((u_long)base_addr+(CySRER<<index),
cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
- goto txdone;
- }
+ }
+#endif
/* Because the Embedded Transmit Commands have
been enabled, we must check to see if the
escape character, NULL, is being sent. If it
@@ -1412,7 +1461,6 @@
if (info->xmit_cnt < WAKEUP_CHARS) {
cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
}
-
txend:
/* end of service */
cy_writeb((u_long)base_addr+(CyTIR<<index),
@@ -1491,7 +1539,6 @@
/* clear interrupts */
cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
/* Cy_ClrIntr is 0x1800 */
-
} /* cyy_interrupt */

/***********************************************************/
@@ -1714,6 +1761,11 @@
break;
case C_CM_MDSR:
break;
+#ifdef Z_WAKE
+ case C_CM_IOCTLW:
+ cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
+ break;
+#endif
case C_CM_FATAL:
/* should do something with this !!! */
break;
@@ -1994,7 +2046,13 @@
#endif

cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
- cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS);
+#ifdef Z_WAKE
+ cy_writel(&ch_ctrl[channel].intr_enable,
+ C_IN_MDCD|C_IN_MCTS|C_IN_IOCTLW);
+#else
+ cy_writel(&ch_ctrl[channel].intr_enable,
+ C_IN_MDCD|C_IN_MCTS);
+#endif
retval = cyz_issue_cmd( &cy_card[card],
channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */
if (retval != 0){
@@ -2089,20 +2147,14 @@
card, chip, channel, (long)base_addr);
#endif

- /* REALLY SHOULD WAIT FOR LAST CHARACTER TO BE
- SENT BEFORE DROPPING THE LINE !!! (Perhaps
- set some flag that is read when XMTY happens.)
- Other choices are to delay some fixed interval
- or schedule some later processing.
- */
save_flags(flags); cli();
+
if (info->xmit_buf){
unsigned char * temp;
temp = info->xmit_buf;
info->xmit_buf = 0;
free_page((unsigned long) temp);
}
-
cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
@@ -2147,14 +2199,15 @@
board_ctrl = &(zfw_ctrl->board_ctrl);
ch_ctrl = zfw_ctrl->ch_ctrl;

-
save_flags(flags); cli();
+
if (info->xmit_buf){
unsigned char * temp;
temp = info->xmit_buf;
info->xmit_buf = 0;
free_page((unsigned long) temp);
}
+
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
cy_writel((u_long)&ch_ctrl[channel].rs_control,
(uclong)(cy_readl(&ch_ctrl[channel].rs_control) &
@@ -2162,14 +2215,13 @@
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
- printk("cyc:shutdown retval was %x\n",
- retval);
+ printk("cyc:shutdown retval (2) was %x\n", retval);
}
#ifdef SERIAL_DEBUG_DTR
printk("cyc:shutdown dropping Z DTR\n");
#endif
}
-
+
if (info->tty){
set_bit(TTY_IO_ERROR, &info->tty->flags);
}
@@ -2206,8 +2258,10 @@
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
- if (info->flags & ASYNC_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
+ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+ }
if (info->flags & ASYNC_HUP_NOTIFY){
return -EAGAIN;
}else{
@@ -2241,7 +2295,8 @@
* If non-blocking mode is set, then make the check up front
* and then exit.
*/
- if (filp->f_flags & O_NONBLOCK) {
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & ASYNC_CALLOUT_ACTIVE){
return -EBUSY;
}
@@ -2262,7 +2317,10 @@
printk("cyc block_til_ready before block: ttyC%d, count = %d\n",
info->line, info->count);/**/
#endif
- info->count--;
+ save_flags(flags); cli();
+ if (!tty_hung_up_p(filp))
+ info->count--;
+ restore_flags(flags);
#ifdef SERIAL_DEBUG_COUNT
printk("cyc block_til_ready: (%d): decrementing count to %d\n",
current->pid, info->count);
@@ -2568,8 +2626,54 @@
info->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
- if (info->flags & ASYNC_INITIALIZED)
- tty_wait_until_sent(tty, 5*HZ); /* 5 seconds timeout */
+
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait2 != 0) { /* The port's being forced to wait,
+ independent on the port settings */
+ tty_wait_until_sent(tty, info->closing_wait2*HZ);
+ } else {
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait*HZ);
+ }
+
+ /* Waiting for on-board buffers to be empty before closing the port */
+ if (!IS_CYC_Z(cy_card[info->card])) {
+#ifdef NEW_INTR_FLOW
+ unsigned char *base_addr = (unsigned char *)
+ cy_card[info->card].base_addr;
+ int index = cy_card[info->card].bus_index;
+
+ if (cy_readb(base_addr+(CySRER<<index)) & CyTxMpty) {
+ /* Interrupts are enabled, so go to sleep */
+ interruptible_sleep_on(&info->shutdown_wait);
+ }
+#endif
+ } else {
+#ifdef Z_WAKE
+ unsigned char *base_addr = (unsigned char *)
+ cy_card[info->card].base_addr;
+ struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
+ struct ZFW_CTRL *zfw_ctrl =
+ (struct ZFW_CTRL *) (base_addr + cy_readl(&firm_id->zfwctrl_addr));
+ struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl;
+ int channel = info->line - cy_card[info->card].first_line;
+ int retval;
+
+ if (cy_readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) {
+ retval = cyz_issue_cmd(&cy_card[info->card], channel,
+ C_CM_IOCTLW, 0L);
+ if (retval != 0){
+ printk("cyc:shutdown retval (1) was %x\n", retval);
+ }
+ interruptible_sleep_on(&info->shutdown_wait);
+ }
+#endif
+ }
+
shutdown(info);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
@@ -2634,14 +2738,13 @@

if (from_user)
down(&tmp_buf_sem);
+ save_flags(flags);
while (1) {
- save_flags(flags); cli();
+ cli();
c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
- if (c <= 0){
- restore_flags(flags);
+ if (c <= 0)
break;
- }

if (from_user) {
copy_from_user(tmp_buf, buf, c);
@@ -2664,13 +2767,10 @@
}
if (from_user)
up(&tmp_buf_sem);
-
-
- if (info->xmit_cnt
- && !tty->stopped
- && !tty->hw_stopped ) {
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
start_xmit(info);
}
+ restore_flags(flags);
return total;
} /* cy_write */

@@ -2788,16 +2888,47 @@
cy_chars_in_buffer(struct tty_struct *tty)
{
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ int card, channel;

-#ifdef SERIAL_DEBUG_IO
- printk("cyc:cy_chars_in_buffer ttyC%d %d\n",
- info->line, info->xmit_cnt); /* */
-#endif
-
if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
return 0;

- return info->xmit_cnt;
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+
+ if (!IS_CYC_Z(cy_card[card])) {
+#ifdef SERIAL_DEBUG_IO
+ printk("cyc:cy_chars_in_buffer ttyC%d %d\n",
+ info->line, info->xmit_cnt); /* */
+#endif
+ return info->xmit_cnt;
+ } else {
+ static volatile struct FIRM_ID *firm_id;
+ static volatile struct ZFW_CTRL *zfw_ctrl;
+ static volatile struct CH_CTRL *ch_ctrl;
+ static volatile struct BUF_CTRL *buf_ctrl;
+ int char_count;
+ volatile uclong tx_put, tx_get, tx_bufsize;
+
+ firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS);
+ zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr +
+ cy_readl(&firm_id->zfwctrl_addr));
+ ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+ buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
+
+ tx_get = cy_readl(&buf_ctrl->tx_get);
+ tx_put = cy_readl(&buf_ctrl->tx_put);
+ tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+ if (tx_put >= tx_get)
+ char_count = tx_put - tx_get;
+ else
+ char_count = tx_put - tx_get + tx_bufsize;
+#ifdef SERIAL_DEBUG_IO
+ printk("cyc:cy_chars_in_buffer ttyC%d %d\n",
+ info->line, info->xmit_cnt + char_count); /* */
+#endif
+ return (info->xmit_cnt + char_count);
+ }
} /* cy_chars_in_buffer */


@@ -2818,7 +2949,8 @@
unsigned long flags;
unsigned char *base_addr;
int card,chip,channel,index;
- unsigned cflag;
+ unsigned cflag, iflag;
+ unsigned short chip_number;
int i;


@@ -2829,9 +2961,11 @@
return;
}
cflag = info->tty->termios->c_cflag;
+ iflag = info->tty->termios->c_iflag;

card = info->card;
channel = (info->line) - (cy_card[card].first_line);
+ chip_number = channel / 4;

if (!IS_CYC_Z(cy_card[card])) {

@@ -2843,17 +2977,16 @@
if (i & CBAUDEX) {
if (i == B57600)
i = 16;
+#ifdef B76800
+ else if(i == B76800)
+ i = 17;
+#endif
else if(i == B115200)
i = 18;
- else if(i == B230400 &&
- cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ else if(i == B230400 && (info->chip_rev >= CD1400_REV_J)) {
/* It is a CD1400 rev. J or later */
i = 20;
}
-#ifdef B76800
- else if(i == B76800)
- i = 17;
-#endif
else
info->tty->termios->c_cflag &= ~CBAUDEX;
}
@@ -2867,6 +3000,10 @@
switch(info->baud) {
case 57600:
i += 1; break;
+#ifdef B76800
+ case 76800:
+ i += 2; break;
+#endif
case 115200:
i += 3; break;
case 230400:
@@ -2876,7 +3013,7 @@
}
}
}
- if(cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ if(info->chip_rev >= CD1400_REV_J) {
/* It is a CD1400 rev. J or later */
info->tbpr = baud_bpr_60[i]; /* Tx BPR */
info->tco = baud_co_60[i]; /* Tx CO */
@@ -3115,6 +3252,16 @@
case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break;
}

+ if ((i = cy_readl(&ch_ctrl->comm_baud)) == 134) {
+ info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+ /* get it right for 134.5 baud */
+ } else if (i) {
+ info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2;
+ /* this needs to be propagated into the card info */
+ } else {
+ info->timeout = 0;
+ }
+
/* byte size and parity */
switch(cflag & CSIZE){
case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break;
@@ -3150,7 +3297,7 @@
cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
}

- retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L);
+ retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
if (retval != 0){
printk("cyc:set_line_char retval at %d was %x\n",
__LINE__, retval);
@@ -3163,6 +3310,14 @@
info->flags |= ASYNC_CHECK_CD;
}

+ if (iflag & IXON){
+ cy_writel(&ch_ctrl->sw_flow,
+ cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX);
+ } else {
+ cy_writel(&ch_ctrl->sw_flow,
+ cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX);
+ }
+
if(i == 0){ /* baud rate is zero, turn off line */
cy_writel(&ch_ctrl->rs_control,
cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
@@ -3230,8 +3385,9 @@

if (!suser()) {
if ((new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
- (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+ (new_serial.baud_base != info->baud) ||
+ ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+ (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
return -EPERM;
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
@@ -3245,11 +3401,12 @@
* At this point, we start making changes.....
*/

+ info->baud = new_serial.baud_base;
info->flags = ((info->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
- info->baud = new_serial.baud_base;
- info->close_delay = new_serial.close_delay;
-
+ info->close_delay = new_serial.close_delay * HZ/100;
+ info->closing_wait = new_serial.closing_wait * HZ/100;
+

check_and_exit:
if (info->flags & ASYNC_INITIALIZED){
@@ -3806,25 +3963,44 @@
ret_val = set_default_timeout(info, (unsigned long)arg);
break;
case CYSETRFLOW:
- info->rflow = 1;
+ info->rflow = (int)arg;
ret_val = 0;
break;
- case CYRESETRFLOW:
- info->rflow = 0;
- ret_val = 0;
+ case CYGETRFLOW:
+ ret_val = info->rflow;
break;
case CYSETRTSDTR_INV:
- info->rtsdtr_inv = 1;
+ info->rtsdtr_inv = (int)arg;
ret_val = 0;
break;
- case CYRESETRTSDTR_INV:
- info->rtsdtr_inv = 0;
+ case CYGETRTSDTR_INV:
+ ret_val = info->rtsdtr_inv;
+ break;
+ case CYGETCARDINFO:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(struct cyclades_card));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ copy_to_user((void *)arg, (void *)&cy_card[info->card],
+ sizeof (struct cyclades_card));
ret_val = 0;
+ break;
+ case CYGETCD1400VER:
+ ret_val = info->chip_rev;
break;
case CYZPOLLCYCLE:
cyz_polling_cycle = (HZ * arg) / 1000;
ret_val = 0;
break;
+ case CYSETWAIT:
+ info->closing_wait2 = (unsigned short)arg;
+ ret_val = 0;
+ break;
+ case CYGETWAIT:
+ ret_val = info->closing_wait2;
+ break;
case TCSBRK: /* SVID version: non-zero arg --> no break */
ret_val = tty_check_change(tty);
if (ret_val)
@@ -3968,12 +4144,13 @@

#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
-
- printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
+
+ printk("cyc:throttle %s: %d....ttyC%d\n",
+ tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif

- if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+ if (serial_paranoia_check(info, tty->device, "cy_throttle")){
return;
}

@@ -4024,17 +4201,21 @@
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];

- printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
+ printk("cyc:unthrottle %s: %d....ttyC%d\n",
+ tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif

- if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+ if (serial_paranoia_check(info, tty->device, "cy_unthrottle")){
return;
}

if (I_IXOFF(tty)) {
- info->x_char = START_CHAR(tty);
- /* Should use the "Send Special Character" feature!!! */
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ /* Should use the "Send Special Character" feature!!! */
}

card = info->card;
@@ -4146,6 +4327,49 @@
} /* cy_start */


+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ int card, channel;
+ unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
+ return;
+ save_flags(flags); cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ restore_flags(flags);
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+
+ if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board
+ buffers as well */
+ static volatile struct FIRM_ID *firm_id;
+ static volatile struct ZFW_CTRL *zfw_ctrl;
+ static volatile struct CH_CTRL *ch_ctrl;
+ static volatile struct BUF_CTRL *buf_ctrl;
+
+ firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS);
+ zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr +
+ cy_readl(&firm_id->zfwctrl_addr));
+ ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+ buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
+
+ while (cy_readl(&buf_ctrl->tx_get) != cy_readl(&buf_ctrl->tx_put))
+ cy_writel(&buf_ctrl->tx_put, cy_readl(&buf_ctrl->tx_get));
+ }
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+} /* cy_flush_buffer */
+
+
/*
* cy_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
@@ -4160,7 +4384,8 @@

if (serial_paranoia_check(info, tty->device, "cy_hangup"))
return;
-
+
+ cy_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
@@ -4173,28 +4398,6 @@
} /* cy_hangup */


-static void
-cy_flush_buffer(struct tty_struct *tty)
-{
- struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
- unsigned long flags;
-
-#ifdef SERIAL_DEBUG_IO
- printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */
-#endif
-
- if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
- return;
- save_flags(flags); cli();
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- restore_flags(flags);
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
- && tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-} /* cy_flush_buffer */
-
-
/*
* ---------------------------------------------------------------------
* cy_init() and friends
@@ -4265,7 +4468,7 @@
return chip_number;
}
cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
- if (cy_readb(base_addr+(CyGFRCR<<index)) >= 0x48){
+ if (cy_readb(base_addr+(CyGFRCR<<index)) >= CD1400_REV_J){
/* It is a CD1400 rev. J or later */
/* Impossible to reach 5ms with this chip.
Changed to 2ms instead (f = 500 Hz). */
@@ -4275,11 +4478,11 @@
cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
}

- /*
+ /*
printk(" chip #%d at %#6lx is rev 0x%2x\n",
chip_number, (unsigned long)base_addr,
- base_addr[CyGFRCR<<index]);
- */
+ cy_readb(base_addr+(CyGFRCR<<index)));
+ */
}
return chip_number;
} /* cyy_init_card */
@@ -4308,7 +4511,7 @@

/* probe for CD1400... */

-#if !defined(__alpha__)
+#if !defined(__alpha__)
cy_isa_address = ioremap((unsigned int)cy_isa_address,
CyISA_Ywin);
#endif
@@ -4390,45 +4593,89 @@
cy_detect_pci(void))
{
#ifdef CONFIG_PCI
- unsigned char cyy_rev_id;
+
+#ifdef NEW_PCI
+ struct pci_dev *pdev = NULL;
+ unsigned char cyy_rev_id;
+#else
+ unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id;
+#endif
unsigned long pci_intr_ctrl;
unsigned char cy_pci_irq;
uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
unsigned short i,j,cy_pci_nchan;
- unsigned short device_id,dev_index = 0,board_index = 0;
+ unsigned short device_id,dev_index = 0;
+#ifndef NEW_PCI
+ unsigned short board_index = 0;
+#endif
uclong mailbox;
uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
- struct pci_dev *pdev = NULL;

+#ifdef NEW_PCI
if(pci_present() == 0) { /* PCI bus not present */
+#else
+ if(pcibios_present() == 0) { /* PCI bus not present */
+#endif
return(0);
}
for (i = 0; i < NR_CARDS; i++) {
/* look for a Cyclades card by vendor and device id */
while((device_id = cy_pci_dev_id[dev_index]) != 0) {
- if ((pdev = pci_find_device(PCI_VENDOR_ID_CYCLADES, device_id, pdev)) == NULL)
+#ifdef NEW_PCI
+ if((pdev = pci_find_device(PCI_VENDOR_ID_CYCLADES,
+ device_id, pdev)) == NULL) {
dev_index++; /* try next device id */
- else {
+ } else {
+ break; /* found a board */
+ }
+#else
+ if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES,
+ device_id,board_index,
+ &cyy_bus, &cyy_dev_fn) != 0) {
+ dev_index++; /* try next device id */
+ board_index = 0;
+ } else {
board_index++;
break; /* found a board */
}
+#endif
}

if (device_id == 0)
break;

/* read PCI configuration area */
+#ifdef NEW_PCI
cy_pci_irq = pdev->irq;
cy_pci_addr0 = pdev->base_address[0];
- cy_pci_addr1 = pdev->base_address[1];
- cy_pci_addr2 = pdev->base_address[2];
- pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id);
+ cy_pci_addr1 = pdev->base_address[1];
+ cy_pci_addr2 = pdev->base_address[2];
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id);
+#else
+ pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+ PCI_INTERRUPT_LINE, &cy_pci_irq);
+ pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+ PCI_BASE_ADDRESS_0,
+ (unsigned int *) &cy_pci_addr0);
+ pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+ PCI_BASE_ADDRESS_1,
+ (unsigned int *) &cy_pci_addr1);
+ pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+ PCI_BASE_ADDRESS_2,
+ (unsigned int *) &cy_pci_addr2);
+ pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+ PCI_REVISION_ID, &cyy_rev_id);
+#endif

if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
|| (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
#ifdef CY_PCI_DEBUG
printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+#ifdef NEW_PCI
pdev->bus->number, pdev->devfn);
+#else
+ cyy_bus, cyy_dev_fn);
+#endif
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
@@ -4440,7 +4687,11 @@
#if defined(__alpha__)
if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
- pdev->bus->number, pdev->devfn);
+#ifdef NEW_PCI
+ pdev->bus->number, pdev->devfn);
+#else
+ cyy_bus, cyy_dev_fn);
+#endif
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
@@ -4525,7 +4776,11 @@
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
/* print message */
printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+#ifdef NEW_PCI
pdev->bus->number, pdev->devfn);
+#else
+ cyy_bus, cyy_dev_fn);
+#endif
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
@@ -4535,7 +4790,11 @@
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
#ifdef CY_PCI_DEBUG
printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
- pdev->bus->number, pdev->devfn);
+#ifdef NEW_PCI
+ pdev->bus->number, pdev->devfn);
+#else
+ cyy_bus, cyy_dev_fn);
+#endif
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
@@ -4777,7 +5036,7 @@
tmp = strchr(rcsvers, ' '); *tmp++ = '\0';
rcsdate = strchr(tmp, ' '); rcsdate++;
tmp = strrchr(rcsdate, ' '); *tmp = '\0';
- printk("Cyclom driver %s %s\n",
+ printk("Cyclades driver %s %s\n",
rcsvers, rcsdate);
printk(" built %s %s\n",
__DATE__, __TIME__);
@@ -4810,6 +5069,7 @@
int number_z_boards = 0;
int board,port,i,index;
unsigned long mailbox;
+ unsigned short chip_number;
int nports;

show_version();
@@ -4858,9 +5118,9 @@
cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;

if (tty_register_driver(&cy_serial_driver))
- panic("Couldn't register Cyclom serial driver\n");
+ panic("Couldn't register Cyclades serial driver\n");
if (tty_register_driver(&cy_callout_driver))
- panic("Couldn't register Cyclom callout driver\n");
+ panic("Couldn't register Cyclades callout driver\n");

init_bh(CYCLADES_BH, do_cyclades_bh);

@@ -4922,9 +5182,13 @@
info->type = PORT_STARTECH;
info->card = board;
info->line = port;
+ info->chip_rev = 0;
info->flags = STD_COM_FLAGS;
info->tty = 0;
- info->xmit_fifo_size = 0;
+ if (mailbox == ZO_V1)
+ info->xmit_fifo_size = CYZ_FIFO_SIZE;
+ else
+ info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
info->cor1 = 0;
info->cor2 = 0;
info->cor3 = 0;
@@ -4934,7 +5198,9 @@
info->tco = 0;
info->rbpr = 0;
info->rco = 0;
- info->close_delay = 0;
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = CLOSING_WAIT_DELAY;
+ info->closing_wait2 = 0;
info->x_char = 0;
info->event = 0;
info->count = 0;
@@ -4952,6 +5218,7 @@
cy_serial_driver.init_termios;
info->open_wait = 0;
info->close_wait = 0;
+ info->shutdown_wait = 0;
/* info->session */
/* info->pgrp */
info->read_status_mask = 0;
@@ -4976,14 +5243,19 @@
info->line = port;
info->flags = STD_COM_FLAGS;
info->tty = 0;
- info->xmit_fifo_size = 12;
+ info->xmit_fifo_size = CyMAX_CHAR_FIFO;
info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
info->cor2 = CyETC;
info->cor3 = 0x08; /* _very_ small rcv threshold */
info->cor4 = 0;
info->cor5 = 0;
- info->close_delay = 0;
- if (cy_readb(cinfo->base_addr+(CyGFRCR<<index)) >= 0x48) {
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = CLOSING_WAIT_DELAY;
+ info->closing_wait2 = 0;
+ chip_number = (port - cinfo->first_line) / 4;
+ if ((info->chip_rev = cy_readb(cinfo->base_addr +
+ (cy_chip_offset[chip_number]<<index) +
+ (CyGFRCR<<index))) >= CD1400_REV_J) {
/* It is a CD1400 rev. J or later */
info->tbpr = baud_bpr_60[13]; /* Tx BPR */
info->tco = baud_co_60[13]; /* Tx CO */
@@ -5016,6 +5288,7 @@
cy_serial_driver.init_termios;
info->open_wait = 0;
info->close_wait = 0;
+ info->shutdown_wait = 0;
/* info->session */
/* info->pgrp */
info->read_status_mask =
@@ -5054,21 +5327,19 @@
int i;
unsigned long flags;

-
if (cyz_timeron){
cyz_timeron = 0;
del_timer(&cyz_timerlist);
}

- save_flags(flags);
- cli();
+ save_flags(flags); cli();
remove_bh(CYCLADES_BH);

free_page((unsigned long)tmp_buf);
if (tty_unregister_driver(&cy_callout_driver))
- printk("Couldn't unregister Cyclom callout driver\n");
+ printk("Couldn't unregister Cyclades callout driver\n");
if (tty_unregister_driver(&cy_serial_driver))
- printk("Couldn't unregister Cyclom serial driver\n");
+ printk("Couldn't unregister Cyclades serial driver\n");

restore_flags(flags);

--- linux/include/linux/cyclades.h.2.1.94 Tue Nov 11 11:04:15 1997
+++ linux/include/linux/cyclades.h Fri Apr 10 12:45:14 1998
@@ -1,11 +1,21 @@
-/* $Revision: 2.1 $$Date: 1997/10/24 16:03:00 $
+/* $Revision: 2.3 $$Date: 1998/03/16 18:01:12 $
* linux/include/linux/cyclades.h
*
- * This file is maintained by Marcio Saito <marcio@cyclades.com> and
+ * This file is maintained by Ivan Passos <ivan@cyclades.com>,
+ * Marcio Saito <marcio@cyclades.com> and
* Randolph Bentson <bentson@grieg.seaslug.org>.
*
* This file contains the general definitions for the cyclades.c driver
*$Log: cyclades.h,v $
+ *Revision 2.3 1998/03/16 18:01:12 ivan
+ *changes in the cyclades_port structure to get it closer to the
+ *standard serial port structure;
+ *added constants for new ioctls;
+ *Revision 2.2 1998/02/17 16:50:00 ivan
+ *changes in the cyclades_port structure (addition of shutdown_wait and
+ *chip_rev variables);
+ *added constants for new ioctls and for CD1400 rev. numbers.
+ *
*Revision 2.1 1997/10/24 16:03:00 ivan
*added rflow (which allows enabling the CD1400 special flow control
*feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to
@@ -58,10 +68,14 @@
#define CYGETDEFTIMEOUT 0x435908
#define CYSETDEFTIMEOUT 0x435909
#define CYSETRFLOW 0x43590a
-#define CYRESETRFLOW 0x43590b
+#define CYGETRFLOW 0x43590b
#define CYSETRTSDTR_INV 0x43590c
-#define CYRESETRTSDTR_INV 0x43590d
+#define CYGETRTSDTR_INV 0x43590d
#define CYZPOLLCYCLE 0x43590e
+#define CYGETCD1400VER 0x43590f
+#define CYGETCARDINFO 0x435910
+#define CYSETWAIT 0x435911
+#define CYGETWAIT 0x435912

/*************** CYCLOM-Z ADDITIONS ***************/

@@ -78,6 +92,8 @@
#define MAX_PORT 128 /* Max number of ports per board */
#define MAX_DEV 256 /* Max number of ports total */

+#define CYZ_FIFO_SIZE 16
+
#define CYZ_BOOT_NWORDS 0x100
struct CYZ_BOOT_CTRL {
unsigned short nboard;
@@ -473,12 +489,13 @@

struct cyclades_port {
int magic;
- int type;
int card;
int line;
int flags; /* defined in tty.h */
+ int type; /* UART type */
struct tty_struct *tty;
int read_status_mask;
+ int ignore_status_mask;
int timeout;
int xmit_fifo_size;
int cor1,cor2,cor3,cor4,cor5;
@@ -486,13 +503,15 @@
int baud;
int rflow;
int rtsdtr_inv;
- int ignore_status_mask;
+ int chip_rev;
+ int custom_divisor;
+ int x_char; /* to be pushed out ASAP */
int close_delay;
- int IER; /* Interrupt Enable Register */
- int event;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
unsigned long last_active;
int count; /* # of fd on device */
- int x_char; /* to be pushed out ASAP */
int x_break;
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
@@ -508,6 +527,7 @@
struct termios callout_termios;
struct wait_queue *open_wait;
struct wait_queue *close_wait;
+ struct wait_queue *shutdown_wait;
struct cyclades_monitor mon;
unsigned long jiffies[3];
unsigned long rflush_count;
@@ -517,13 +537,14 @@
* Events are used to schedule things to happen at timer-interrupt
* time, instead of at cy interrupt time.
*/
-#define Cy_EVENT_READ_PROCESS 0
-#define Cy_EVENT_WRITE_WAKEUP 1
-#define Cy_EVENT_HANGUP 2
-#define Cy_EVENT_BREAK 3
-#define Cy_EVENT_OPEN_WAKEUP 4
-
+#define Cy_EVENT_READ_PROCESS 0
+#define Cy_EVENT_WRITE_WAKEUP 1
+#define Cy_EVENT_HANGUP 2
+#define Cy_EVENT_BREAK 3
+#define Cy_EVENT_OPEN_WAKEUP 4
+#define Cy_EVENT_SHUTDOWN_WAKEUP 5

+#define CLOSING_WAIT_DELAY 30

#define CyMAX_CHIPS_PER_CARD 8
#define CyMAX_CHAR_FIFO 12
@@ -538,10 +559,13 @@

/**** CD1400 registers ****/

-#define CyRegSize 0x0400
-#define Cy_HwReset 0x1400
-#define Cy_ClrIntr 0x1800
-#define Cy_EpldRev 0x1e00
+#define CD1400_REV_G 0x46
+#define CD1400_REV_J 0x48
+
+#define CyRegSize 0x0400
+#define Cy_HwReset 0x1400
+#define Cy_ClrIntr 0x1800
+#define Cy_EpldRev 0x1e00

/* Global Registers */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu