patch-2.4.0-test4 linux/drivers/char/cyclades.c

Next file: linux/drivers/char/drm/ffb_drv.c
Previous file: linux/drivers/char/busmouse.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test3/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
@@ -2,7 +2,7 @@
 #define	Z_WAKE
 #undef	Z_EXT_CHARS_IN_BUFFER
 static char rcsid[] =
-"$Revision: 2.3.2.7 $$Date: 2000/06/01 18:26:34 $";
+"$Revision: 2.3.2.8 $$Date: 2000/07/06 18:14:16 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -25,6 +25,11 @@
  * This version supports shared IRQ's (only for PCI boards).
  *
  * $Log: cyclades.c,v $
+ * Revision 2.3.2.8   2000/07/06 18:14:16 ivan
+ * Fixed the PCI detection function to work properly on Alpha systems.
+ * Implemented support for TIOCSERGETLSR ioctl.
+ * Implemented full support for non-standard baud rates.
+ *
  * Revision 2.3.2.7   2000/06/01 18:26:34 ivan
  * Request PLX I/O region, although driver doesn't use it, to avoid
  * problems with other drivers accessing it.
@@ -1363,9 +1368,16 @@
 
                     while (char_count-- > 0){
 			if (!info->xmit_cnt){
-                            cy_writeb((u_long)base_addr+(CySRER<<index),
-				cy_readb(base_addr+(CySRER<<index)) & 
-					~CyTxRdy);
+			    if (cy_readb(base_addr+(CySRER<<index))&CyTxMpty) {
+				cy_writeb((u_long)base_addr+(CySRER<<index),
+					  cy_readb(base_addr+(CySRER<<index)) &
+					  ~CyTxMpty);
+			    } else {
+				cy_writeb((u_long)base_addr+(CySRER<<index),
+					  ((cy_readb(base_addr+(CySRER<<index))
+					    & ~CyTxRdy)
+					   | CyTxMpty));
+			    }
 			    goto txdone;
 			}
 			if (info->xmit_buf == 0){
@@ -3176,6 +3188,30 @@
  * ------------------------------------------------------------
  */
 
+static void
+cyy_baud_calc(struct cyclades_port *info, uclong baud)
+{
+    int co, co_val, bpr;
+    uclong cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : 25000000);
+
+    if (baud == 0) {
+	info->tbpr = info->tco = info->rbpr = info->rco = 0;
+	return;
+    }
+
+    /* determine which prescaler to use */
+    for (co = 4, co_val = 2048; co; co--, co_val >>= 2) {
+	if (cy_clock / co_val / baud > 63)
+	    break;
+    }
+
+    bpr = (cy_clock / co_val * 2 / baud + 1) / 2;
+    if (bpr > 255)
+	bpr = 255;
+
+    info->tbpr = info->rbpr = bpr;
+    info->tco = info->rco = co;
+}
 
 /*
  * This routine finds or computes the various line characteristics.
@@ -3189,7 +3225,7 @@
   int card,chip,channel,index;
   unsigned cflag, iflag;
   unsigned short chip_number;
-  int baud;
+  int baud, baud_rate = 0;
   int   i;
 
 
@@ -3225,12 +3261,14 @@
 	index = cy_card[card].bus_index;
 
 	/* baud rate */
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
-	    baud = info->baud;
-	} else {
-	    baud = tty_get_baud_rate(info->tty);
-	}
-	if (baud > CD1400_MAX_SPEED) {
+	baud = tty_get_baud_rate(info->tty);
+	if ((baud == 38400) &&
+	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) {
+	    if (info->custom_divisor)
+		baud_rate = info->baud / info->custom_divisor;
+	    else
+		baud_rate = info->baud;
+	} else if (baud > CD1400_MAX_SPEED) {
 	    baud = CD1400_MAX_SPEED;
 	}
 	/* find the baud index */
@@ -3243,22 +3281,29 @@
 	    i = 19; /* CD1400_MAX_SPEED */
 	} 
 
-
-	if(info->chip_rev >= CD1400_REV_J) {
-	    /* It is a CD1400 rev. J or later */
-	    info->tbpr = baud_bpr_60[i]; /* Tx BPR */
-	    info->tco = baud_co_60[i]; /* Tx CO */
-	    info->rbpr = baud_bpr_60[i]; /* Rx BPR */
-	    info->rco = baud_co_60[i]; /* Rx CO */
+	if ((baud == 38400) &&
+	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) {
+	    cyy_baud_calc(info, baud_rate);
 	} else {
-	    info->tbpr = baud_bpr_25[i]; /* Tx BPR */
-	    info->tco = baud_co_25[i]; /* Tx CO */
-	    info->rbpr = baud_bpr_25[i]; /* Rx BPR */
-	    info->rco = baud_co_25[i]; /* Rx CO */
+	    if(info->chip_rev >= CD1400_REV_J) {
+		/* It is a CD1400 rev. J or later */
+		info->tbpr = baud_bpr_60[i]; /* Tx BPR */
+		info->tco = baud_co_60[i]; /* Tx CO */
+		info->rbpr = baud_bpr_60[i]; /* Rx BPR */
+		info->rco = baud_co_60[i]; /* Rx CO */
+	    } else {
+		info->tbpr = baud_bpr_25[i]; /* Tx BPR */
+		info->tco = baud_co_25[i]; /* Tx CO */
+		info->rbpr = baud_bpr_25[i]; /* Rx BPR */
+		info->rco = baud_co_25[i]; /* Rx CO */
+	    }
 	}
 	if (baud_table[i] == 134) {
-	    info->timeout = (info->xmit_fifo_size*HZ*15/269) + 2;
 	    /* get it right for 134.5 baud */
+	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+	} else if ((baud == 38400) &&
+		   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) {
+	    info->timeout = (info->xmit_fifo_size*HZ*15/baud_rate) + 2;
 	} else if (baud_table[i]) {
 	    info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
 	    /* this needs to be propagated into the card info */
@@ -3447,19 +3492,24 @@
 	buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
 
 	/* baud rate */
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
-	    baud = info->baud;
-	} else {
-	    baud = tty_get_baud_rate(info->tty);
-	}
-	if (baud > CYZ_MAX_SPEED) {
+	baud = tty_get_baud_rate(info->tty);
+	if ((baud == 38400) &&
+	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) {
+	    if (info->custom_divisor)
+		baud_rate = info->baud / info->custom_divisor;
+	    else
+		baud_rate = info->baud;
+	} else if (baud > CYZ_MAX_SPEED) {
 	    baud = CYZ_MAX_SPEED;
 	}
 	cy_writel(&ch_ctrl->comm_baud , baud);
 
 	if (baud == 134) {
-	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
 	    /* get it right for 134.5 baud */
+	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+	} else if ((baud == 38400) &&
+		   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) {
+	    info->timeout = (info->xmit_fifo_size*HZ*15/baud_rate) + 2;
 	} else if (baud) {
 	    info->timeout = (info->xmit_fifo_size*HZ*15/baud) + 2;
 	    /* this needs to be propagated into the card info */
@@ -3549,8 +3599,6 @@
 	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	}
     }
-
-
 } /* set_line_char */
 
 
@@ -3571,7 +3619,7 @@
     tmp.flags = info->flags;
     tmp.close_delay = info->close_delay;
     tmp.baud_base = info->baud;
-    tmp.custom_divisor = 0;     /*!!!*/
+    tmp.custom_divisor = info->custom_divisor;
     tmp.hub6 = 0;               /*!!!*/
     return copy_to_user(retinfo,&tmp,sizeof(*retinfo))?-EFAULT:0;
 } /* get_serial_info */
@@ -3597,6 +3645,7 @@
             info->flags = ((info->flags & ~ASYNC_USR_MASK) |
                            (new_serial.flags & ASYNC_USR_MASK));
             info->baud = new_serial.baud_base;
+	    info->custom_divisor = new_serial.custom_divisor;
             goto check_and_exit;
     }
 
@@ -3607,6 +3656,7 @@
      */
 
     info->baud = new_serial.baud_base;
+    info->custom_divisor = new_serial.custom_divisor;
     info->flags = ((info->flags & ~ASYNC_FLAGS) |
                     (new_serial.flags & ASYNC_FLAGS));
     info->close_delay = new_serial.close_delay * HZ/100;
@@ -3621,6 +3671,43 @@
     }
 } /* set_serial_info */
 
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *	    is emptied.  On bus types like RS485, the transmitter must
+ *	    release the bus after transmitting. This must be done when
+ *	    the transmit shift register is empty, not be done when the
+ *	    transmit holding register is empty.  This functionality
+ *	    allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct cyclades_port *info, unsigned int *value)
+{
+    int card, chip, channel, index;
+    unsigned char status;
+    unsigned int result;
+    unsigned long flags;
+    unsigned char *base_addr;
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char *)
+		     (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+
+	CY_LOCK(info, flags);
+	status = cy_readb(base_addr+(CySRER<<index)) & (CyTxRdy|CyTxMpty);
+	CY_UNLOCK(info, flags);
+	result = (status ? 0 : TIOCSER_TEMT);
+    } else {
+	/* Not supported yet */
+	return -EINVAL;
+    }
+    return cy_put_user(result, (unsigned long *) value);
+}
 
 static int
 get_modem_info(struct cyclades_port * info, unsigned int *value)
@@ -4247,6 +4334,9 @@
         case TIOCSSERIAL:
             ret_val = set_serial_info(info, (struct serial_struct *) arg);
             break;
+	case TIOCSERGETLSR: /* Get line status register */
+	    ret_val = get_lsr_info(info, (unsigned int *) arg);
+	    break;
 	/*
 	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change 
 	 * - mask passed in arg for lines of interest
@@ -4937,10 +5027,9 @@
 		    i--;
 	            continue;
                 }
-#else
+#endif
 		cy_pci_addr0 = (ulong)ioremap(cy_pci_phys0, CyPCI_Yctl);
 		cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Ywin);
-#endif
 
 #ifdef CY_PCI_DEBUG
             printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
@@ -5048,9 +5137,7 @@
             printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
                 (ulong)cy_pci_phys2, (ulong)cy_pci_phys0);
 #endif
-#if !defined(__alpha__)
-                cy_pci_addr0 = (ulong)ioremap(cy_pci_phys0, CyPCI_Zctl);
-#endif
+		cy_pci_addr0 = (ulong)ioremap(cy_pci_phys0, CyPCI_Zctl);
 
 		/* Disable interrupts on the PLX before resetting it */
 		cy_writew(cy_pci_addr0+0x68,
@@ -5078,9 +5165,7 @@
 		request_region(cy_pci_phys1, CyPCI_Zctl, "Cyclades-Z");
 
 		if (mailbox == ZE_V1) {
-#if !defined(__alpha__)
-               	    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Ze_win);
-#endif
+		    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Ze_win);
 		    if (ZeIndex == NR_CARDS) {
 			printk("Cyclades-Ze/PCI found at 0x%lx ",
 				(ulong)cy_pci_phys2);
@@ -5097,9 +5182,7 @@
 		    i--;
 		    continue;
 		} else {
-#if !defined(__alpha__)
-                    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Zwin);
-#endif
+		    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Zwin);
 		}
 
 #ifdef CY_PCI_DEBUG
@@ -5538,6 +5621,7 @@
                     info->tco = 0;
                     info->rbpr = 0;
                     info->rco = 0;
+		    info->custom_divisor = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
 		    info->icount.cts = info->icount.dsr = 
@@ -5596,6 +5680,7 @@
                     info->cor3 = 0x08; /* _very_ small rcv threshold */
                     info->cor4 = 0;
                     info->cor5 = 0;
+		    info->custom_divisor = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
 		    info->icount.cts = info->icount.dsr = 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)