On Wed, 2011-12-07 at 14:49 +1100, Finn Thain wrote:
> 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.

Thanks. I'll test it on a powermac or two and will merge it via the
powerpc -next tree if it works out allright.

Cheers,
Ben.
 

> 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 <fth...@telegraphics.com.au>
> Acked-by: Alan Cox <a...@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);


_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to