]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] cyclades async driver update
authorMarcelo Tosatti <marcelo.tosatti@cyclades.com>
Thu, 26 Feb 2004 04:57:26 +0000 (20:57 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 26 Feb 2004 04:57:26 +0000 (20:57 -0800)
This patch is the first of several planned fixes for the cyclades
multiserial cards driver.

Its mostly a sync with in-house driver:

- Prevent users from opening non-existing Z ports
- Implement special XON/XOFF character handling in Z cards
- Prevent data-loss on Z cards
- Throttling fix for Z card
- Only throttle if CTS/RTS are set
- Fix accounting of received data

Kudos to Cyclades R&D

drivers/char/cyclades.c
include/linux/cyclades.h

index 1bb2458e5fcc69e95f9aef9da72ff9055e6d663c..46f8ab327e1cb33a9a473edfc818a36c038991f1 100644 (file)
@@ -2,7 +2,7 @@
 #define        Z_WAKE
 #undef Z_EXT_CHARS_IN_BUFFER
 static char rcsid[] =
-"$Revision: 2.3.2.8 $$Date: 2000/07/06 18:14:16 $";
+"$Revision: 2.3.2.20 $$Date: 2004/02/25 18:14:16 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -12,7 +12,7 @@ static char rcsid[] =
  *
  * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
  * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
- * Currently maintained by Henrique Gobbi <henrique.gobbi@cyclades.com>.
+ * Currently maintained by Cyclades team <async@cyclades.com>.
  *
  * For Technical support and installation problems, please send e-mail
  * to support@cyclades.com.
@@ -25,6 +25,8 @@ static char rcsid[] =
  * This version supports shared IRQ's (only for PCI boards).
  *
  * $Log: cyclades.c,v $
+ * Prevent users from opening non-existing Z ports.
+ *
  * 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.
@@ -676,6 +678,9 @@ static char rcsid[] =
 
 #define cy_put_user    put_user
 
+static void cy_throttle (struct tty_struct *tty);
+static void cy_send_xchar (struct tty_struct *tty, char ch);
+
 static unsigned long 
 cy_get_user(unsigned long *addr)
 {
@@ -1256,8 +1261,6 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                info->mon.char_max = char_count;
                             info->mon.char_last = char_count;
 #endif
-                           info->idle_stats.recv_bytes += char_count;
-                           info->idle_stats.recv_idle   = jiffies;
                             while(char_count--){
                                 if (tty->flip.count >= TTY_FLIPBUF_SIZE){
                                         break;
@@ -1266,11 +1269,13 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                 data = cy_readb(base_addr+(CyRDSR<<index));
                                 *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
                                 *tty->flip.char_buf_ptr++ = data;
+                               info->idle_stats.recv_bytes++;
                                info->icount.rx++;
 #ifdef CY_16Y_HACK
                                 udelay(10L);
 #endif
                             }
+                             info->idle_stats.recv_idle = jiffies;
                         }
                         schedule_delayed_work(&tty->flip.work, 1);
                     }
@@ -1627,9 +1632,12 @@ cyz_handle_rx(struct cyclades_port *info, volatile struct CH_CTRL *ch_ctrl,
            }
 #else
            while(char_count--){
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+               if (tty->flip.count >= N_TTY_BUF_SIZE - tty->read_cnt)
+                    break;
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
                    break;
-               }
+
                data = cy_readb(cinfo->base_addr + rx_bufaddr + new_rx_get);
                new_rx_get = (new_rx_get + 1) & (rx_bufsize - 1);
                tty->flip.count++;
@@ -1768,10 +1776,6 @@ cyz_handle_cmd(struct cyclades_card *cinfo)
     fw_ver = cy_readl(&board_ctrl->fw_version);
     hw_ver = cy_readl(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0);
 
-#ifdef CONFIG_CYZ_INTR
-    if (!cinfo->nports)
-       cinfo->nports = (int) cy_readl(&board_ctrl->n_channel);
-#endif
 
     while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
        special_count = 0;
@@ -1958,7 +1962,8 @@ cyz_poll(unsigned long arg)
            ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
            buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
 
-           cyz_handle_rx(info, ch_ctrl, buf_ctrl);
+           if (!info->throttle)
+               cyz_handle_rx(info, ch_ctrl, buf_ctrl);
            cyz_handle_tx(info, ch_ctrl, buf_ctrl);
        }
        /* poll every 'cyz_polling_cycle' period */
@@ -2553,12 +2558,15 @@ cy_open(struct tty_struct *tty, struct file * filp)
        will make the user pay attention.
     */
     if (IS_CYC_Z(cy_card[info->card])) {
-        if (!ISZLOADED(cy_card[info->card])) {
+       struct cyclades_card *cinfo = &cy_card[info->card];
+       struct FIRM_ID *firm_id = (struct FIRM_ID *)
+               (cinfo->base_addr + ID_ADDRESS);
+
+        if (!ISZLOADED(*cinfo)) {
            if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
-               ((cy_card[info->card]).ctl_addr))->mail_box_0)) &&
-               Z_FPGA_CHECK(cy_card[info->card])) &&
-               (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *)
-               ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)))
+               (cinfo->ctl_addr))->mail_box_0)) &&
+               Z_FPGA_CHECK (*cinfo)) &&
+               (ZFIRM_HLT == cy_readl (&firm_id->signature)))
            {
                printk ("cyc:Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
            } else {
@@ -2571,20 +2579,33 @@ cy_open(struct tty_struct *tty, struct file * filp)
            /* In case this Z board is operating in interrupt mode, its 
               interrupts should be enabled as soon as the first open happens 
               to one of its ports. */
-           if (!cy_card[info->card].intr_enabled) {
+            if (!cinfo->intr_enabled) {
+               struct ZFW_CTRL *zfw_ctrl;
+               struct BOARD_CTRL *board_ctrl;
+
+               zfw_ctrl = (struct ZFW_CTRL *)
+                (cinfo->base_addr +
+                 (cy_readl (&firm_id->zfwctrl_addr) & 0xfffff));
+
+               board_ctrl = &zfw_ctrl->board_ctrl;
+
                /* Enable interrupts on the PLX chip */
-               cy_writew(cy_card[info->card].ctl_addr+0x68,
-                       cy_readw(cy_card[info->card].ctl_addr+0x68)|0x0900);
+               cy_writew(cinfo->ctl_addr+0x68,
+                       cy_readw(cinfo->ctl_addr+0x68)|0x0900);
                /* Enable interrupts on the FW */
-               retval = cyz_issue_cmd(&cy_card[info->card], 
+               retval = cyz_issue_cmd(cinfo,
                                        0, C_CM_IRQ_ENBL, 0L);
                if (retval != 0){
                    printk("cyc:IRQ enable retval was %x\n", retval);
                }
-               cy_card[info->card].intr_enabled = 1;
+               cinfo->nports = (int) cy_readl (&board_ctrl->n_channel);
+               cinfo->intr_enabled = 1;
            }
        }
 #endif /* CONFIG_CYZ_INTR */
+       /* Make sure this Z port really exists in hardware */
+       if (info->line > (cinfo->first_line + cinfo->nports - 1))
+               return -ENODEV;
     }
 #ifdef CY_DEBUG_OTHER
     printk("cyc:cy_open ttyC%d\n", info->line); /* */
@@ -2639,6 +2660,8 @@ cy_open(struct tty_struct *tty, struct file * filp)
         return retval;
     }
 
+    info->throttle = 0;
+
 #ifdef CY_DEBUG_OPEN
     printk(" cyc:cy_open done\n");/**/
 #endif
@@ -4283,6 +4306,34 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
     return;
 } /* cy_set_termios */
 
+/* This function is used to send a high-priority XON/XOFF character to
+   the device.
+*/
+static void
+cy_send_xchar (struct tty_struct *tty, char ch)
+{
+       struct cyclades_port *info = (struct cyclades_port *) tty->driver_data;
+       int card, channel;
+
+       if (serial_paranoia_check (info, tty->name, "cy_send_xchar"))
+               return;
+
+       info->x_char = ch;
+
+       if (ch)
+               cy_start (tty);
+
+       card = info->card;
+       channel = info->line - cy_card[card].first_line;
+
+       if (IS_CYC_Z (cy_card[card])) {
+               if (ch == STOP_CHAR (tty))
+                       cyz_issue_cmd (&cy_card[card], channel, C_CM_SENDXOFF, 0L);
+               else if (ch == START_CHAR (tty))
+                       cyz_issue_cmd (&cy_card[card], channel, C_CM_SENDXON, 0L);
+       }
+}
+
 /* This routine is called by the upper-layer tty layer to signal
    that incoming characters should be throttled because the input
    buffers are close to full.
@@ -4307,31 +4358,36 @@ cy_throttle(struct tty_struct * tty)
             return;
     }
 
+    card = info->card;
+
     if (I_IXOFF(tty)) {
-        info->x_char = STOP_CHAR(tty);
-            /* Should use the "Send Special Character" feature!!! */
+        if (!IS_CYC_Z (cy_card[card]))
+            cy_send_xchar (tty, STOP_CHAR (tty));
+        else
+            info->throttle = 1;
     }
 
-    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);
-       cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
-       if (info->rtsdtr_inv) {
-               cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+    if (tty->termios->c_cflag & CRTSCTS) {
+        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);
+            cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+            if (info->rtsdtr_inv) {
+                cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+             } else {
+                cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+            }
+           CY_UNLOCK(info, flags);
        } else {
-               cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
-       }
-       CY_UNLOCK(info, flags);
-    } else {
-       // Nothing to do!
+           info->throttle = 1;
+        }
     }
 
     return;
@@ -4367,30 +4423,31 @@ cy_unthrottle(struct tty_struct * tty)
        if (info->x_char)
            info->x_char = 0;
        else
-           info->x_char = START_CHAR(tty);
-            /* Should use the "Send Special Character" feature!!! */
+           cy_send_xchar (tty, START_CHAR (tty));
     }
 
-    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
+    if (tty->termios->c_cflag & CRTSCTS) {
+        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);
-       cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
-       if (info->rtsdtr_inv) {
-               cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
-       } else {
-               cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+           CY_LOCK(info, flags);
+           cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+           if (info->rtsdtr_inv) {
+                   cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+           } else {
+                   cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+           }
+           CY_UNLOCK(info, flags);
+        } else {
+           info->throttle = 0;
        }
-       CY_UNLOCK(info, flags);
-    }else{
-       // Nothing to do!
     }
 
     return;
index 0be662bf061d1e36e13a12d5e7eac2784a5f908f..cc598507b8137215926be33c0acb03288256ddfd 100644 (file)
@@ -7,6 +7,10 @@
  *
  * This file contains the general definitions for the cyclades.c driver
  *$Log: cyclades.h,v $
+ *Revision 3.1  2002/01/29 11:36:16  henrique
+ *added throttle field on struct cyclades_port to indicate whether the
+ *port is throttled or not
+ *
  *Revision 3.1  2000/04/19 18:52:52  ivan
  *converted address fields to unsigned long and added fields for physical
  *addresses on cyclades_card structure;
@@ -141,7 +145,7 @@ struct CYZ_BOOT_CTRL {
 /****************** ****************** *******************/
 /*
  *     The data types defined below are used in all ZFIRM interface
- *     data structures. They accommodate differences between HW
+ *     data structures. They accomodate differences between HW
  *     architectures and compilers.
  */
 
@@ -604,6 +608,7 @@ struct cyclades_port {
        wait_queue_head_t       close_wait;
        wait_queue_head_t       shutdown_wait;
        wait_queue_head_t       delta_msr_wait;
+       int throttle;
 };
 
 /*