Commit 9eb3a8d0 authored by Marcelo Tosatti's avatar Marcelo Tosatti Committed by Linus Torvalds

[PATCH] cyclades async driver update

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