public inbox for linux-serial@vger.kernel.org
 help / color / mirror / Atom feed
From: Finn Thain <fthain@telegraphics.com.au>
To: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>,
	linux-m68k@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
	linux-serial@vger.kernel.org
Subject: [PATCH 01/16 v3] pmac_zilog: fix unexpected irq
Date: Wed, 7 Dec 2011 14:49:36 +1100 (EST)	[thread overview]
Message-ID: <alpine.LNX.2.00.1112071427280.28552@nippy.intranet> (raw)
In-Reply-To: <alpine.LNX.2.00.1112070203310.14968@nippy.intranet>


On most 68k Macs the SCC IRQ is an autovector interrupt and cannot be 
masked. This can be a problem when pmac_zilog starts up.

For example, the serial debugging code in arch/m68k/kernel/head.S may be 
used beforehand. It disables the SCC interrupts at the chip but doesn't 
ack them. Then when a pmac_zilog port is used, the machine locks up with 
"unexpected interrupt".

This can happen in pmz_shutdown() since the irq is freed before the 
channel interrupts are disabled.

Fix this by clearing interrupt enable bits before the handler is 
uninstalled. Also move the interrupt control bit flipping into a separate 
pmz_interrupt_control() routine. Replace all instances of these operations 
with calls to this routine. Omit the zssync() calls that seem to serve no 
purpose.

Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Acked-by: Alan Cox <alan@linux.intel.com>

---
   
Re-implemented as v2 using a simpler approach that avoids messing with the 
Master Interrupt Enable bit. As well as the ifdef problem, it turns out 
that v1 was not sufficient to entirely fix the problem.

v3 avoids needless changes to the logic and locking in the suspend and 
shutdown code and tries to keep register writes closer to their original 
sequence.

This patch has been tested on a PowerBook 520 but no PowerMacs yet.


Index: linux-git/drivers/tty/serial/pmac_zilog.c
===================================================================
--- linux-git.orig/drivers/tty/serial/pmac_zilog.c	2011-12-07 12:36:32.000000000 +1100
+++ linux-git/drivers/tty/serial/pmac_zilog.c	2011-12-07 14:29:21.000000000 +1100
@@ -216,6 +216,18 @@ static void pmz_maybe_update_regs(struct
 	}
 }
 
+static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable)
+{
+	if (enable) {
+		uap->curregs[1] |= INT_ALL_Rx | TxINT_ENAB;
+		if (!ZS_IS_EXTCLK(uap))
+			uap->curregs[1] |= EXT_INT_ENAB;
+	} else {
+		uap->curregs[1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
+	}
+	write_zsreg(uap, R1, uap->curregs[1]);
+}
+
 static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
 {
 	struct tty_struct *tty = NULL;
@@ -339,9 +351,7 @@ static struct tty_struct *pmz_receive_ch
 
 	return tty;
  flood:
-	uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
-	write_zsreg(uap, R1, uap->curregs[R1]);
-	zssync(uap);
+	pmz_interrupt_control(uap, 0);
 	pmz_error("pmz: rx irq flood !\n");
 	return tty;
 }
@@ -990,12 +1000,9 @@ static int pmz_startup(struct uart_port
 	if (ZS_IS_IRDA(uap))
 		pmz_irda_reset(uap);
 
-	/* Enable interrupts emission from the chip */
+	/* Enable interrupt requests for the channel */
 	spin_lock_irqsave(&port->lock, flags);
-	uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB;
-	if (!ZS_IS_EXTCLK(uap))
-		uap->curregs[R1] |= EXT_INT_ENAB;
-	write_zsreg(uap, R1, uap->curregs[R1]);
+	pmz_interrupt_control(uap, 1);
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	pmz_debug("pmz: startup() done.\n");
@@ -1015,6 +1022,25 @@ static void pmz_shutdown(struct uart_por
 
 	mutex_lock(&pmz_irq_mutex);
 
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (!ZS_IS_ASLEEP(uap)) {
+		/* Disable interrupt requests for the channel */
+		pmz_interrupt_control(uap, 0);
+
+		if (!ZS_IS_CONS(uap)) {
+			/* Disable receiver and transmitter */
+			uap->curregs[R3] &= ~RxENABLE;
+			uap->curregs[R5] &= ~TxENABLE;
+
+			/* Disable break assertion */
+			uap->curregs[R5] &= ~SND_BRK;
+			pmz_maybe_update_regs(uap);
+		}
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
 	/* Release interrupt handler */
 	free_irq(uap->port.irq, uap);
 
@@ -1025,29 +1051,8 @@ static void pmz_shutdown(struct uart_por
 	if (!ZS_IS_OPEN(uap->mate))
 		pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON;
 
-	/* Disable interrupts */
-	if (!ZS_IS_ASLEEP(uap)) {
-		uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
-		write_zsreg(uap, R1, uap->curregs[R1]);
-		zssync(uap);
-	}
-
-	if (ZS_IS_CONS(uap) || ZS_IS_ASLEEP(uap)) {
-		spin_unlock_irqrestore(&port->lock, flags);
-		mutex_unlock(&pmz_irq_mutex);
-		return;
-	}
-
-	/* Disable receiver and transmitter.  */
-	uap->curregs[R3] &= ~RxENABLE;
-	uap->curregs[R5] &= ~TxENABLE;
-
-	/* Disable all interrupts and BRK assertion.  */
-	uap->curregs[R5] &= ~SND_BRK;
-	pmz_maybe_update_regs(uap);
-
-	/* Shut the chip down */
-	pmz_set_scc_power(uap, 0);
+	if (!ZS_IS_ASLEEP(uap) && !ZS_IS_CONS(uap))
+		pmz_set_scc_power(uap, 0);	/* Shut the chip down */
 
 	spin_unlock_irqrestore(&port->lock, flags);
 
@@ -1352,19 +1357,15 @@ static void pmz_set_termios(struct uart_
 	spin_lock_irqsave(&port->lock, flags);	
 
 	/* Disable IRQs on the port */
-	uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
-	write_zsreg(uap, R1, uap->curregs[R1]);
+	pmz_interrupt_control(uap, 0);
 
 	/* Setup new port configuration */
 	__pmz_set_termios(port, termios, old);
 
 	/* Re-enable IRQs on the port */
-	if (ZS_IS_OPEN(uap)) {
-		uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB;
-		if (!ZS_IS_EXTCLK(uap))
-			uap->curregs[R1] |= EXT_INT_ENAB;
-		write_zsreg(uap, R1, uap->curregs[R1]);
-	}
+	if (ZS_IS_OPEN(uap))
+		pmz_interrupt_control(uap, 1);
+
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -1671,14 +1672,17 @@ static int pmz_suspend(struct macio_dev
 	spin_lock_irqsave(&uap->port.lock, flags);
 
 	if (ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)) {
-		/* Disable receiver and transmitter.  */
+		/* Disable interrupt requests for the channel */
+		pmz_interrupt_control(uap, 0);
+
+		/* Disable receiver and transmitter */
 		uap->curregs[R3] &= ~RxENABLE;
 		uap->curregs[R5] &= ~TxENABLE;
 
-		/* Disable all interrupts and BRK assertion.  */
-		uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
+		/* Disable break assertion */
 		uap->curregs[R5] &= ~SND_BRK;
 		pmz_load_zsregs(uap, uap->curregs);
+
 		uap->flags |= PMACZILOG_FLAG_IS_ASLEEP;
 		mb();
 	}
@@ -1738,14 +1742,6 @@ static int pmz_resume(struct macio_dev *
 	/* Take care of config that may have changed while asleep */
 	__pmz_set_termios(&uap->port, &uap->termios_cache, NULL);
 
-	if (ZS_IS_OPEN(uap)) {
-		/* Enable interrupts */		
-		uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB;
-		if (!ZS_IS_EXTCLK(uap))
-			uap->curregs[R1] |= EXT_INT_ENAB;
-		write_zsreg(uap, R1, uap->curregs[R1]);
-	}
-
 	spin_unlock_irqrestore(&uap->port.lock, flags);
 
 	if (ZS_IS_CONS(uap))
@@ -1757,6 +1753,12 @@ static int pmz_resume(struct macio_dev *
 		enable_irq(uap->port.irq);
 	}
 
+	if (ZS_IS_OPEN(uap)) {
+		spin_lock_irqsave(&uap->port.lock, flags);
+		pmz_interrupt_control(uap, 1);
+		spin_unlock_irqrestore(&uap->port.lock, flags);
+	}
+
  bail:
 	mutex_unlock(&state->port.mutex);
 	mutex_unlock(&pmz_irq_mutex);

  parent reply	other threads:[~2011-12-07  3:49 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20111023141108.856998818@telegraphics.com.au>
     [not found] ` <20111023141115.208699274@telegraphics.com.au>
2011-11-24 14:34   ` [PATCH 01/16] pmac_zilog: fix unexpected irq Finn Thain
2011-11-24 14:56     ` Alan Cox
2011-11-24 20:41       ` Benjamin Herrenschmidt
2011-11-25  3:15       ` Finn Thain
2011-11-28  0:30         ` Benjamin Herrenschmidt
2011-11-24 15:28     ` David Laight
2011-11-24 20:43       ` Benjamin Herrenschmidt
2011-12-06 15:13   ` [PATCH 01/16 v2] " Finn Thain
2011-12-06 15:27     ` Geert Uytterhoeven
2011-12-07  1:26       ` Finn Thain
2011-12-06 15:39     ` Alan Cox
2011-12-07  3:49     ` Finn Thain [this message]
2011-12-08  3:17       ` [PATCH 01/16 v3] " Benjamin Herrenschmidt
2011-12-08  4:20       ` Benjamin Herrenschmidt
2011-12-08  4:30         ` Benjamin Herrenschmidt
2011-12-08 11:26           ` Finn Thain
2011-12-08 11:54             ` Geert Uytterhoeven
2011-12-08 19:44             ` Benjamin Herrenschmidt
2011-12-11 23:48             ` Benjamin Herrenschmidt
2011-12-11 23:55               ` Benjamin Herrenschmidt
2011-12-12 13:34               ` Finn Thain
2011-12-12 20:06                 ` Benjamin Herrenschmidt
2011-12-13  1:24                   ` Finn Thain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=alpine.LNX.2.00.1112071427280.28552@nippy.intranet \
    --to=fthain@telegraphics.com.au \
    --cc=benh@kernel.crashing.org \
    --cc=geert@linux-m68k.org \
    --cc=linux-m68k@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox