* [Linux-ia64] Serial patch for HP Diva
@ 2002-12-10 16:47 Paul Bame
0 siblings, 0 replies; only message in thread
From: Paul Bame @ 2002-12-10 16:47 UTC (permalink / raw)
To: linux-ia64
When running gettys on multiple of the ports on HP's "Diva" MP
(Management Processor) card, which shows up in various forms in various
boxes, transmit-empty interrupts from the simulated 16550 uarts get
"used up". You can't get a further transmit empty interrupt from
a single empty condition which causes output to hang. The following
ugly patch:
1) bypasses one common case where the interrupt is used up,
the rs_timer() poll of serial devices which forces a tx-empty
interrupt
2) calculates the correct IIR value (in rs-interrupt()) when
the card won't produce the right one, so the rest of the
interrupt will operate normally
3) adds a once-per-second timer to poll the Diva card
When and if HP produces a box with multiple Diva cards, this patch
will need to be updated. This same logic is recently committed to
the PA-RISC/Linux tree (http://parisc-linux.org) too.
-Paul Bame
Index: drivers/char/serial.c
=================================RCS file: /var/cvs/linux/drivers/char/serial.c,v
retrieving revision 1.20
retrieving revision 1.23
diff -u -u -r1.20 -r1.23
--- drivers/char/serial.c 27 Aug 2002 15:20:48 -0000 1.20
+++ drivers/char/serial.c 27 Nov 2002 21:23:31 -0000 1.23
@@ -260,6 +260,10 @@
static struct timer_list serial_timer;
+#define HP_DIVA_CHECKTIME (1*HZ)
+static struct timer_list hp_diva_timer;
+static int hp_diva_irq = -1;
+
/* serial subtype definitions */
#ifndef SERIAL_TYPE_NORMAL
#define SERIAL_TYPE_NORMAL 1
@@ -797,6 +801,28 @@
#ifdef CONFIG_SERIAL_SHARE_IRQ
/*
+ * It is possible to "use up" transmit empty interrupts in some
+ * cases with HP Diva cards. Figure out if there _should_ be a
+ * transmit interrupt and if so, return a suitable iir value so
+ * that we can recover when called from rs_timer().
+ */
+static inline int hp_diva_iir(int irq, struct async_struct *info)
+{
+ int iir = serial_in(info, UART_IIR);
+
+ if (irq = hp_diva_irq &&
+ (iir & UART_IIR_NO_INT) != 0 &&
+ (info->IER & UART_IER_THRI) != 0 &&
+ (info->xmit.head != info->xmit.tail || info->x_char) &&
+ (serial_in(info, UART_LSR) & UART_LSR_THRE) != 0) {
+ iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
+ iir |= UART_IIR_THRI;
+ }
+
+ return iir;
+}
+
+/*
* This is the serial driver's generic interrupt routine
*/
static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
@@ -826,7 +852,7 @@
do {
if (!info->tty ||
- ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) {
+ ((iir=hp_diva_iir(irq, info)) & UART_IIR_NO_INT)) {
if (!end_mark)
end_mark = info;
goto next;
@@ -1090,9 +1116,11 @@
#ifdef CONFIG_SERIAL_SHARE_IRQ
if (info->next_port) {
do {
- serial_out(info, UART_IER, 0);
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_IER, info->IER);
+ if (i != hp_diva_irq) {
+ serial_out(info, UART_IER, 0);
+ info->IER |= UART_IER_THRI;
+ serial_out(info, UART_IER, info->IER);
+ }
info = info->next_port;
} while (info);
#ifdef CONFIG_SERIAL_MULTIPORT
@@ -1124,6 +1152,32 @@
}
/*
+ * This subroutine is called when the hp_diva_timer goes off. In certain
+ * cases (multiple gettys in particular) Diva seems
+ * to issue only a single transmit empty interrupt instead of one each
+ * time THRI is enabled, causing interrupts to be "used up". This
+ * serves to poll the Diva UARTS more frequently than rs_timer() does.
+ */
+static void hp_diva_check(unsigned long dummy)
+{
+ static unsigned long last_strobe;
+ struct async_struct *info;
+ unsigned long flags;
+
+ if (time_after_eq(jiffies, last_strobe + HP_DIVA_CHECKTIME)) {
+ info = IRQ_ports[hp_diva_irq];
+ if (info) {
+ save_flags(flags); cli();
+ rs_interrupt(hp_diva_irq, NULL, NULL);
+ restore_flags(flags);
+ }
+ }
+ last_strobe = jiffies;
+ mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME);
+}
+
+
+/*
* ---------------------------------------------------------------
* Low level utility subroutines for the serial driver: routines to
* figure out the appropriate timeout for an interrupt chain, routines
@@ -4267,6 +4321,8 @@
if (!enable)
return 0;
+ hp_diva_irq = dev->irq;
+
switch (dev->subsystem_device) {
case 0x1049: /* Prelude Diva 1 */
case 0x1223: /* Superdome */
@@ -4285,6 +4341,10 @@
break;
}
+ init_timer(&hp_diva_timer);
+ hp_diva_timer.function = hp_diva_check;
+ mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME);
+
return 0;
}
@@ -5813,6 +5873,8 @@
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
del_timer_sync(&serial_timer);
+ if (hp_diva_irq != -1)
+ del_timer_sync(&hp_diva_timer);
save_flags(flags); cli();
remove_bh(SERIAL_BH);
if ((e1 = tty_unregister_driver(&serial_driver)))
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2002-12-10 16:47 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-12-10 16:47 [Linux-ia64] Serial patch for HP Diva Paul Bame
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.