aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordavem <davem>2002-01-04 21:12:44 +0000
committerdavem <davem>2002-01-04 21:12:44 +0000
commitfe58c5e863f549493b9234d6461715440be63de5 (patch)
tree6b0a66eb8edbb11639e0bac1ace28203fdc7d681
parentb60ee3e6302370eb9bb1860ee9c0a0f02e899cc3 (diff)
downloadnetdev-vger-cvs-fe58c5e863f549493b9234d6461715440be63de5.tar.gz
Use R3 for interrupt status reading instead
of R2. Change receive_chars to read the status byte with each incomming character.
-rw-r--r--drivers/sbus/char/zs.c203
1 files changed, 79 insertions, 124 deletions
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
index 70c054018..8d714e1a6 100644
--- a/drivers/sbus/char/zs.c
+++ b/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.68 2001-10-25 18:48:03 davem Exp $
+/* $Id: zs.c,v 1.69 2002-01-04 21:12:44 davem Exp $
* zs.c: Zilog serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -486,10 +486,19 @@ extern void breakpoint(void); /* For the KGDB frame character */
static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
{
struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
- int do_queue_task = 1;
+ int do_queue_task = 0;
+
+ while (1) {
+ unsigned char ch, r1;
+
+ r1 = read_zsreg(info->zs_channel, R1);
+ if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+ sbus_writeb(ERR_RES, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, ERR_RES, 1);
+ }
- do {
ch = sbus_readb(&info->zs_channel->data);
ZSLOG(REGDATA, ch, 0);
ch &= info->parity_mask;
@@ -498,17 +507,17 @@ static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
/* If this is the console keyboard, we need to handle
* L1-A's here.
*/
- if(info->cons_keyb) {
- if(ch == SUNKBD_RESET) {
+ if (info->cons_keyb) {
+ if (ch == SUNKBD_RESET) {
l1a_state.kbd_id = 1;
l1a_state.l1_down = 0;
- } else if(l1a_state.kbd_id) {
+ } else if (l1a_state.kbd_id) {
l1a_state.kbd_id = 0;
- } else if(ch == SUNKBD_L1) {
+ } else if (ch == SUNKBD_L1) {
l1a_state.l1_down = 1;
- } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ } else if (ch == (SUNKBD_L1|SUNKBD_UP)) {
l1a_state.l1_down = 0;
- } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ } else if (ch == SUNKBD_A && l1a_state.l1_down) {
/* whee... */
batten_down_hatches();
/* Continue execution... */
@@ -517,16 +526,14 @@ static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
return;
}
sunkbd_inchar(ch, regs);
- do_queue_task = 0;
goto next_char;
}
- if(info->cons_mouse) {
+ if (info->cons_mouse) {
sun_mouse_inbyte(ch, 0);
- do_queue_task = 0;
goto next_char;
}
- if(info->is_cons) {
- if(ch == 0) {
+ if (info->is_cons) {
+ if (ch == 0) {
/* whee, break received */
batten_down_hatches();
/* Continue execution... */
@@ -540,32 +547,42 @@ static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
* documentation for remote target debugging and
* arch/sparc/kernel/sparc-stub.c to see how all this works.
*/
- if((info->kgdb_channel) && (ch =='\003')) {
+ if (info->kgdb_channel && (ch =='\003')) {
breakpoint();
return;
}
#endif
- if(!tty)
+ if (!tty)
return;
+ do_queue_task++;
+
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = 0;
+ if (r1 & PAR_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ else if (r1 & Rx_OVR)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ else if (r1 & CRC_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+ else
+ *tty->flip.flag_buf_ptr++ = 0;
*tty->flip.char_buf_ptr++ = ch;
next_char:
- /* Check if we have another character... */
- stat = sbus_readb(&info->zs_channel->control);
- ZSDELAY();
- ZSLOG(REGCTRL, stat, 0);
- if (!(stat & Rx_CH_AV))
- break;
-
- /* ... and see if it is clean. */
- stat = read_zsreg(info->zs_channel, R1);
- } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+ {
+ unsigned char stat;
+
+ /* Check if we have another character... */
+ stat = sbus_readb(&info->zs_channel->control);
+ ZSDELAY();
+ ZSLOG(REGCTRL, stat, 0);
+ if (!(stat & Rx_CH_AV))
+ break;
+ }
+ }
if (do_queue_task != 0)
queue_task(&tty->flip.tqueue, &tq_timer);
@@ -582,7 +599,7 @@ static void transmit_chars(struct sun_serial *info)
return;
}
- if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+ if ((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
/* That's peculiar... */
sbus_writeb(RES_Tx_P, &info->zs_channel->control);
ZSDELAY();
@@ -599,7 +616,7 @@ static void transmit_chars(struct sun_serial *info)
if (info->xmit_cnt < WAKEUP_CHARS)
zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- if(info->xmit_cnt <= 0) {
+ if (info->xmit_cnt <= 0) {
sbus_writeb(RES_Tx_P, &info->zs_channel->control);
ZSDELAY();
ZS_WSYNC(info->zs_channel);
@@ -621,14 +638,14 @@ static void status_handle(struct sun_serial *info)
ZS_WSYNC(info->zs_channel);
ZSLOG(REGCTRL, RES_EXT_INT, 1);
#if 0
- if(status & DCD) {
- if((info->tty->termios->c_cflag & CRTSCTS) &&
- ((info->curregs[3] & AUTO_ENAB)==0)) {
+ if (status & DCD) {
+ if ((info->tty->termios->c_cflag & CRTSCTS) &&
+ ((info->curregs[3] & AUTO_ENAB)==0)) {
info->curregs[3] |= AUTO_ENAB;
write_zsreg(info->zs_channel, 3, info->curregs[3]);
}
} else {
- if((info->curregs[3] & AUTO_ENAB)) {
+ if ((info->curregs[3] & AUTO_ENAB)) {
info->curregs[3] &= ~AUTO_ENAB;
write_zsreg(info->zs_channel, 3, info->curregs[3]);
}
@@ -638,7 +655,7 @@ static void status_handle(struct sun_serial *info)
* 'break asserted' status change interrupt, call
* the boot prom.
*/
- if(status & BRK_ABRT) {
+ if (status & BRK_ABRT) {
if (info->break_abort)
batten_down_hatches();
if (info->cons_mouse)
@@ -651,110 +668,49 @@ static void status_handle(struct sun_serial *info)
return;
}
-static void special_receive(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- stat = read_zsreg(info->zs_channel, R1);
- if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
- ch = sbus_readb(&info->zs_channel->data);
- ZSDELAY();
- ZSLOG(REGDATA, ch, 0);
- }
-
- if (!tty)
- goto clear;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- goto done;
-
- tty->flip.count++;
- if(stat & PAR_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if(stat & Rx_OVR)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else if(stat & CRC_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
- queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
- sbus_writeb(ERR_RES, &info->zs_channel->control);
- ZSDELAY();
- ZS_WSYNC(info->zs_channel);
- ZSLOG(REGCTRL, ERR_RES, 1);
-}
-
-
/*
* This is the serial driver's generic interrupt routine
*/
void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct sun_serial *info;
- unsigned char zs_intreg;
int i;
info = (struct sun_serial *)dev_id;
ZSLOG(REGIRQ, 0, 0);
for (i = 0; i < NUM_SERIAL; i++) {
- zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
- zs_intreg &= STATUS_MASK;
-
- /* NOTE: The read register 2, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the B
- * channel and is only valid when read from channel B.
- * When read from channel A, read register 2 contains
- * the value written to write register 2.
- */
+ unsigned char r3 = read_zsreg(info->zs_channel, 3);
/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
- if (zs_intreg == CHA_Rx_AVAIL) {
- receive_chars(info, regs);
- return;
- }
- if(zs_intreg == CHA_Tx_EMPTY) {
- transmit_chars(info);
- return;
- }
- if (zs_intreg == CHA_EXT_STAT) {
- status_handle(info);
- return;
- }
- if (zs_intreg == CHA_SPECIAL) {
- special_receive(info);
- return;
+ if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+ sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, RES_H_IUS, 1);
+ if (r3 & CHARxIP)
+ receive_chars(info, regs);
+ if (r3 & CHAEXT)
+ status_handle(info);
+ if (r3 & CHATxIP)
+ transmit_chars(info);
}
/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
- if(zs_intreg == CHB_Rx_AVAIL) {
- receive_chars(info->zs_next, regs);
- return;
+ info = info->zs_next;
+ if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+ sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, RES_H_IUS, 1);
+ if (r3 & CHBRxIP)
+ receive_chars(info, regs);
+ if (r3 & CHBEXT)
+ status_handle(info);
+ if (r3 & CHBTxIP)
+ transmit_chars(info);
}
- if(zs_intreg == CHB_Tx_EMPTY) {
- transmit_chars(info->zs_next);
- return;
- }
- if (zs_intreg == CHB_EXT_STAT) {
- status_handle(info->zs_next);
- return;
- }
-
- /* NOTE: The default value for the IRQ status in read register
- * 2 in channel B is CHB_SPECIAL, so we need to look at
- * read register 3 in channel A to check if this is a
- * real interrupt, or just the default value.
- * Yes... broken hardware...
- */
- zs_intreg = read_zsreg(info->zs_channel, 3);
- if (zs_intreg & CHBRxIP) {
- special_receive(info->zs_next);
- return;
- }
- info = info->zs_next->zs_next;
+ info = info->zs_next;
}
}
@@ -1933,7 +1889,7 @@ int zs_open(struct tty_struct *tty, struct file * filp)
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.68 $";
+ char *revision = "$Revision: 1.69 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2469,8 +2425,7 @@ int __init zs_init(void)
/* Grab IRQ line before poking the chips so we do
* not lose any interrupts.
*/
- if (request_irq(zilog_irq, zs_interrupt,
- (SA_INTERRUPT | SA_STATIC_ALLOC),
+ if (request_irq(zilog_irq, zs_interrupt, SA_SHIRQ,
"Zilog8530", zs_chain)) {
prom_printf("Unable to attach zs intr\n");
prom_halt();