From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

pmac_zilog wasn't properly updating port->timeout, thus broke with serial
console.  I'm not sure if that timeout thing was added recently or not,
since the driver used to work fine.  This patch fixes that, and adds some
proper SYSRQ support.  By default, BREAK is used for sysrq, though some
Apple zilog's seem to have non-working BREAK detection logic, thus the
driver has a #define option you can manually enable when debugging to do
SYSRQ with ctrl-O instead (like pSeries virtual consoles).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/serial/pmac_zilog.c |   50 ++++++++++++++++++++++++++++--------
 25-akpm/drivers/serial/pmac_zilog.h |    1 
 2 files changed, 40 insertions(+), 11 deletions(-)

diff -puN drivers/serial/pmac_zilog.c~fix-pmac_zilog-as-console drivers/serial/pmac_zilog.c
--- 25/drivers/serial/pmac_zilog.c~fix-pmac_zilog-as-console	2004-11-04 19:39:06.560540016 -0800
+++ 25-akpm/drivers/serial/pmac_zilog.c	2004-11-04 19:39:06.567538952 -0800
@@ -29,6 +29,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * 2004-08-06 Harald Welte <laforge@gnumonks.org>
+ *	- Enable BREAK interrupt
+ *	- Add support for sysreq
+ *
  * TODO:   - Add DMA support
  *         - Defer port shutdown to a few seconds after close
  *         - maybe put something right into uap->clk_divisor
@@ -36,6 +40,7 @@
 
 #undef DEBUG
 #undef DEBUG_HARD
+#undef USE_CTRL_O_SYSRQ
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -54,6 +59,7 @@
 #include <linux/adb.h>
 #include <linux/pmu.h>
 #include <linux/bitops.h>
+#include <linux/sysrq.h>
 #include <asm/sections.h>
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -64,6 +70,10 @@
 #include <asm/macio.h>
 #include <asm/semaphore.h>
 
+#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 
@@ -259,8 +269,27 @@ static struct tty_struct *pmz_receive_ch
 		}
 
 		ch &= uap->parity_mask;
-		if (ch == 0 && uap->prev_status & BRK_ABRT)
-			r1 |= BRK_ABRT;
+		if (ch == 0 && uap->flags & PMACZILOG_FLAG_BREAK) {
+			uap->flags &= ~PMACZILOG_FLAG_BREAK;
+		}
+
+#ifdef CONFIG_MAGIC_SYSRQ
+#ifdef USE_CTRL_O_SYSRQ
+		/* Handle the SysRq ^O Hack */
+		if (ch == '\x0f') {
+			uap->port.sysrq = jiffies + HZ*5;
+			goto next_char;
+		}
+#endif /* USE_CTRL_O_SYSRQ */
+		if (uap->port.sysrq) {
+			int swallow;
+			spin_unlock(&uap->port.lock);
+			swallow = uart_handle_sysrq_char(&uap->port, ch, regs);
+			spin_lock(&uap->port.lock);
+			if (swallow)
+				goto next_char;
+ 		}
+#endif /* CONFIG_MAGIC_SYSRQ */
 
 		/* A real serial line, record the character and status.  */
 		if (drop)
@@ -276,10 +305,8 @@ static struct tty_struct *pmz_receive_ch
 				pmz_debug("pmz: got break !\n");
 				r1 &= ~(PAR_ERR | CRC_ERR);
 				uap->port.icount.brk++;
-				if (uart_handle_break(&uap->port)) {
-					pmz_debug("pmz: do handle break !\n");
+				if (uart_handle_break(&uap->port))
 					goto next_char;
-				}
 			}
 			else if (r1 & PAR_ERR)
 				uap->port.icount.parity++;
@@ -295,10 +322,6 @@ static struct tty_struct *pmz_receive_ch
 			else if (r1 & CRC_ERR)
 				*tty->flip.flag_buf_ptr = TTY_FRAME;
 		}
-		if (uart_handle_sysrq_char(&uap->port, ch, regs)) {
-			pmz_debug("pmz: sysrq swallowed the char\n");
-			goto next_char;
-		}
 
 		if (uap->port.ignore_status_mask == 0xff ||
 		    (r1 & uap->port.ignore_status_mask) == 0) {
@@ -364,6 +387,9 @@ static void pmz_status_handle(struct uar
 		wake_up_interruptible(&uap->port.info->delta_msr_wait);
 	}
 
+	if (status & BRK_ABRT)
+		uap->flags |= PMACZILOG_FLAG_BREAK;
+
 	uap->prev_status = status;
 }
 
@@ -872,8 +898,8 @@ static int __pmz_startup(struct uart_pma
 	uap->curregs[R13] = 0;
 	uap->curregs[R14] = BRENAB;
 
-	/* Clear handshaking */
-	uap->curregs[R15] = 0;
+	/* Clear handshaking, enable BREAK interrupts */
+	uap->curregs[R15] = BRKIE;
 
 	/* Master interrupt enable */
 	uap->curregs[R9] |= NV | MIE;
@@ -1308,6 +1334,8 @@ static void __pmz_set_termios(struct uar
 		/* Load registers to the chip */
 		pmz_maybe_update_regs(uap);
 	}
+	uart_update_timeout(port, termios->c_cflag, baud);
+
 	pmz_debug("pmz: set_termios() done.\n");
 }
 
diff -puN drivers/serial/pmac_zilog.h~fix-pmac_zilog-as-console drivers/serial/pmac_zilog.h
--- 25/drivers/serial/pmac_zilog.h~fix-pmac_zilog-as-console	2004-11-04 19:39:06.561539864 -0800
+++ 25-akpm/drivers/serial/pmac_zilog.h	2004-11-04 19:39:06.567538952 -0800
@@ -47,6 +47,7 @@ struct uart_pmac_port {
 #define PMACZILOG_FLAG_IS_OPEN		0x00002000
 #define PMACZILOG_FLAG_IS_IRQ_ON	0x00004000
 #define PMACZILOG_FLAG_IS_EXTCLK	0x00008000
+#define PMACZILOG_FLAG_BREAK		0x00010000
 
 	unsigned char			parity_mask;
 	unsigned char			prev_status;
_