pmac_zilog 2/2 : Fix various bugs
PATCH 1/1 pmac_zilog 2/2 : Fix various bugs
This patch fixes possible lockups in pmac_zilog when beeing flooded with incoming data (not that other serial drivers share the same race, I told Russel about it already). It also fixes some SCC initialization problems, add some PM callback, and fix the irda setup code.
Please apply,
Ben.
drivers/serial/pmac_zilog.c +619 -225
@@ -35,6 +35,7 @@*/ #undef DEBUG +#undef DEBUG_HARD#include <linux/config.h> #include <linux/module.h>
@@ -61,21 +62,17 @@#include <asm/pmac_feature.h> #include <asm/dbdma.h> #include <asm/macio.h> +#include <asm/semaphore.h> #include <linux/serial.h> #include <linux/serial_core.h> #include "pmac_zilog.h" -#if defined(CONFIG_SERIAL_PMACZILOG_CONSOLE ) && defined(CONFIG_PPC64 ) -#define HAS_SCCDBG -extern int sccdbg; -#endif -/* Not yet implemented */ #undef HAS_DBDMA -static char version[]__initdata ="pmac_zilog.c 0.5a (Benjamin Herrenschmidt <[email protected]>)" ; +static char version[]__initdata ="pmac_zilog: 0.6 (Benjamin Herrenschmidt <[email protected]>)" ;MODULE_AUTHOR ("Benjamin Herrenschmidt <[email protected]>" );MODULE_DESCRIPTION ("Driver for the PowerMac serial ports." );MODULE_LICENSE ("GPL" );
@@ -89,6 +86,15 @@*/ static struct uart_pmac_port pmz_ports[MAX_ZS_PORTS ];static int pmz_ports_count; +static DECLARE_MUTEX (pmz_irq_sem); + +static struct uart_driver pmz_uart_reg = { + .owner =THIS_MODULE , + .driver_name ="ttyS" , + .devfs_name ="tts/" , + .dev_name ="ttyS" , + .major =TTY_MAJOR , +};/*
@@ -100,6 +106,9 @@ {int i; +if (ZS_IS_ASLEEP (uap)) +return ; +/* Let pending transmits finish. */ for (i =0 ; i <1000 ; i++) {unsigned char stat =read_zsreg (uap,R1 );
@@ -125,8 +134,15 @@write_zsreg (uap,R10 , regs[R10 ]);/* Set TX/RX controls sans the enable bits. */ -write_zsreg (uap,R3 , regs[R3 ] & ~RxENABLE); -write_zsreg (uap,R5 , regs[R5 ] & ~TxENABLE); +write_zsreg (uap,R3 , regs[R3 ] & ~RxENABLE); +write_zsreg (uap,R5 , regs[R5 ] & ~TxENABLE); + +/* now set R7 "prime" on ESCC */ +write_zsreg (uap,R15 , regs[R15 ] |EN85C30 ); +write_zsreg (uap,R7 , regs[R7P ]); + +/* make sure we use R7 "non-prime" on ESCC */ +write_zsreg (uap,R15 , regs[R15 ] & ~EN85C30 );/* Synchronous mode config. */ write_zsreg (uap,R6 , regs[R6 ]);
@@ -145,9 +161,6 @@/* Now rewrite R14, with BRENAB (if set). */ write_zsreg (uap,R14 , regs[R14 ]); -/* External status interrupt control. */ -write_zsreg (uap,R15 , regs[R15 ]); -/* Reset external status interrupts. */ write_zsreg (uap,R0 ,RES_EXT_INT );write_zsreg (uap,R0 ,RES_EXT_INT );
@@ -183,43 +196,90 @@ } } -static void pmz_receive_chars (struct uart_pmac_port *uap,struct pt_regs *regs) +static struct tty_struct *pmz_receive_chars (struct uart_pmac_port *uap, +struct pt_regs *regs) { -struct tty_struct *tty = uap->port.info->tty;/* XXX info==NULL? */ +struct tty_struct *tty =NULL ; +unsigned char ch, r1, drop, error; +int loops =0 ; + + retry: +/* The interrupt can be enabled when the port isn't open, typically +* that happens when using one port is open and the other closed (stale +* interrupt) or when one port is used as a console. +*/ +if (!ZS_IS_OPEN (uap)) { +pmz_debug ("pmz: draining input\n" ); +/* Port is closed, drain input data */ +for (;;) { +if ((++loops) >1000 ) + goto flood; + (void )read_zsreg (uap,R1 ); +write_zsreg (uap,R0 ,ERR_RES ); + (void )read_zsdata (uap); + ch =read_zsreg (uap,R0 ); +if (!(ch & Rx_CH_AV)) +break ; + } +return NULL ; + } + +/* Sanity check, make sure the old bug is no longer happening */ +if (uap->port.info ==NULL || uap->port.info->tty ==NULL ) { +WARN_ON (1 ); + (void )read_zsdata (uap); +return NULL ; + } + tty = uap->port.info->tty;while (1 ) { -unsigned char ch, r1; + error =0 ; + drop =0 ;if (unlikely (tty->flip.count >=TTY_FLIPBUF_SIZE )) { +/* Have to drop the lock here */ +pmz_debug ("pmz: flip overflow\n" ); +spin_unlock (&uap->port.lock); tty->flip.work.func ((void *)tty); +spin_lock (&uap->port.lock);if (tty->flip.count >=TTY_FLIPBUF_SIZE ) -/* XXX Ignores SysRq when we need it most. Fix. */ -return ; + drop =1 ; +if (ZS_IS_ASLEEP (uap)) +return 0 ; +if (!ZS_IS_OPEN (uap)) + goto retry; } r1 =read_zsreg (uap,R1 ); + ch =read_zsdata (uap); +if (r1 & (PAR_ERR | Rx_OVR |CRC_ERR )) {write_zsreg (uap,R0 ,ERR_RES );zssync (uap); } - ch =read_zsdata (uap); ch &= uap->parity_mask; -if (ch ==0 && uap->prev_status &BRK_ABRT ) { +if (ch ==0 && uap->prev_status &BRK_ABRT ) r1 |=BRK_ABRT ; -printk ("rx break\n" ); - }/* A real serial line, record the character and status. */ +if (drop) + goto next_char; + *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr =TTY_NORMAL ; uap->port.icount.rx++; +if (r1 & (PAR_ERR | Rx_OVR |CRC_ERR |BRK_ABRT )) { + error =1 ;if (r1 &BRK_ABRT ) { +pmz_debug ("pmz: got break !\n" ); r1 &= ~(PAR_ERR |CRC_ERR ); uap->port.icount.brk++; -if (uart_handle_break (&uap->port)) +if (uart_handle_break (&uap->port)) { +pmz_debug ("pmz: do handle break !\n" ); goto next_char; + } }else if (r1 &PAR_ERR ) uap->port.icount.parity++;
@@ -235,8 +295,10 @@else if (r1 &CRC_ERR ) *tty->flip.flag_buf_ptr =TTY_FRAME ; } -if (uart_handle_sysrq_char (&uap->port, ch, regs)) +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 ) {
@@ -252,12 +314,27 @@ tty->flip.count++; } next_char: +/* We can get stuck in an infinite loop getting char 0 when the +* line is in a wrong HW state, we break that here. +* When that happens, I disable the receive side of the driver. +* Note that what I've been experiencing is a real irq loop where +* I'm getting flooded regardless of the actual port speed. +* Something stange is going on with the HW +*/ +if ((++loops) >1000 ) + goto flood; ch =read_zsreg (uap,R0 );if (!(ch & Rx_CH_AV))break ; } -tty_flip_buffer_push (tty); +return tty; + flood: + uap->curregs[R1 ] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); +write_zsreg (uap,R1 , uap->curregs[R1 ]); +zssync (uap); +dev_err (&uap->dev->ofdev.dev,"pmz: rx irq flood !\n" ); +return tty; }static void pmz_status_handle (struct uart_pmac_port *uap,struct pt_regs *regs)
@@ -268,16 +345,7 @@write_zsreg (uap,R0 ,RES_EXT_INT );zssync (uap); -#ifdef HAS_SCCDBG -if (sccdbg && (status &BRK_ABRT ) && !(uap->prev_status &BRK_ABRT )) { -#ifdef CONFIG_XMON -extern void xmon (struct pt_regs *); -xmon (regs); -#endif - } -#endif /* HAS_SCCDBG */ - -if (ZS_WANTS_MODEM_STATUS (uap)) { +if (ZS_IS_OPEN (uap) &&ZS_WANTS_MODEM_STATUS (uap)) {if (status &SYNC_HUNT ) uap->port.icount.dsr++;
@@ -302,6 +370,8 @@ {struct circ_buf *xmit; +if (ZS_IS_ASLEEP (uap)) +return ;if (ZS_IS_CONS (uap)) {unsigned char status =read_zsreg (uap,R0 );
@@ -369,49 +439,62 @@static irqreturn_t pmz_interrupt (int irq,void *dev_id,struct pt_regs *regs) {struct uart_pmac_port *uap = dev_id; -struct uart_pmac_port *up_a; -struct uart_pmac_port *up_b; +struct uart_pmac_port *uap_a; +struct uart_pmac_port *uap_b;int rc =IRQ_NONE ; +struct tty_struct *tty;u8 r3; - up_a =ZS_IS_CHANNEL_A (uap) ? uap : uap->mate; - up_b = up_a->mate; + uap_a =pmz_get_port_A (uap); + uap_b = uap_a->mate; -spin_lock (&up_a->port.lock); - r3 =read_zsreg (uap,R3 ); -pmz_debug ("pmz_irq: %x\n" , r3); +spin_lock (&uap_a->port.lock); + r3 =read_zsreg (uap_a,R3 ); +#ifdef DEBUG_HARD +pmz_debug ("irq, r3: %x\n" , r3); +#endif /* Channel A */ + tty =NULL ;if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { -write_zsreg (up_a,R0 ,RES_H_IUS ); -zssync (up_a); -pmz_debug ("pmz: irq channel A: %x\n" , r3); +write_zsreg (uap_a,R0 ,RES_H_IUS ); +zssync (uap_a);if (r3 &CHAEXT ) -pmz_status_handle (up_a, regs); +pmz_status_handle (uap_a, regs);if (r3 & CHARxIP) -pmz_receive_chars (up_a, regs); + tty =pmz_receive_chars (uap_a, regs);if (r3 & CHATxIP) -pmz_transmit_chars (up_a); +pmz_transmit_chars (uap_a); rc =IRQ_HANDLED ; } -spin_unlock (&up_a->port.lock); - -spin_lock (&up_b->port.lock); +spin_unlock (&uap_a->port.lock); +if (tty !=NULL ) +tty_flip_buffer_push (tty); + +if (uap_b->node ==NULL ) + goto out; + +spin_lock (&uap_b->port.lock); + tty =NULL ;if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { -write_zsreg (up_b,R0 ,RES_H_IUS ); -zssync (up_b); -pmz_debug ("pmz: irq channel B: %x\n" , r3); +write_zsreg (uap_b,R0 ,RES_H_IUS ); +zssync (uap_b);if (r3 &CHBEXT ) -pmz_status_handle (up_b, regs); +pmz_status_handle (uap_b, regs);if (r3 & CHBRxIP) -pmz_receive_chars (up_b, regs); +pmz_receive_chars (uap_b, regs);if (r3 & CHBTxIP) -pmz_transmit_chars (up_b); +pmz_transmit_chars (uap_b); rc =IRQ_HANDLED ; } -spin_unlock (&up_b->port.lock); - +spin_unlock (&uap_b->port.lock); +if (tty !=NULL ) +tty_flip_buffer_push (tty); + out: +#ifdef DEBUG_HARD +pmz_debug ("irq done.\n" ); +#endif return rc; }
@@ -436,8 +519,12 @@*/ static unsigned int pmz_tx_empty (struct uart_port *port) { +struct uart_pmac_port *uap =to_pmz (port);unsigned char status; +if (ZS_IS_ASLEEP (uap) || uap->node ==NULL ) +return TIOCSER_TEMT ; + status =pmz_peek_status (to_pmz (port));if (status & Tx_BUF_EMP)return TIOCSER_TEMT ;
@@ -458,6 +545,10 @@/* Do nothing for irda for now... */ if (ZS_IS_IRDA (uap))return ; +/* We get called during boot with a port not up yet */ +if (ZS_IS_ASLEEP (uap) || + !(ZS_IS_OPEN (uap) ||ZS_IS_CONS (uap))) +return ; set_bits = clear_bits =0 ;
@@ -475,7 +566,11 @@/* NOTE: Not subject to 'transmitter active' rule. */ uap->curregs[R5 ] |= set_bits; uap->curregs[R5 ] &= ~clear_bits; +if (ZS_IS_ASLEEP (uap)) +return ;write_zsreg (uap,R5 , uap->curregs[R5 ]); +pmz_debug ("pmz_set_mctrl: set bits: %x, clear bits: %x -> %x\n" , + set_bits, clear_bits, uap->curregs[R5 ]);zssync (uap); }
@@ -486,9 +581,13 @@*/ static unsigned int pmz_get_mctrl (struct uart_port *port) { +struct uart_pmac_port *uap =to_pmz (port);unsigned char status;unsigned int ret; +if (ZS_IS_ASLEEP (uap) || uap->node ==NULL ) +return 0 ; + status =pmz_peek_status (to_pmz (port)); ret =0 ;
@@ -527,6 +626,9 @@ uap->flags |=PMACZILOG_FLAG_TX_ACTIVE ; uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED ; +if (ZS_IS_ASLEEP (uap) || uap->node ==NULL ) +return ; + status =read_zsreg (uap,R0 );/* TX busy? Just wait for the TX done interrupt. */
@@ -557,14 +659,15 @@/* * Stop Rx side, basically disable emitting of -* Rx interrupts on the port +* Rx interrupts on the port. We don't disable the rx +* side of the chip proper though * The port lock is held. */ static void pmz_stop_rx (struct uart_port *port) {struct uart_pmac_port *uap =to_pmz (port); -if (ZS_IS_CONS (uap)) +if (ZS_IS_ASLEEP (uap) || uap->node ==NULL )return ;pmz_debug ("pmz: stop_rx()()\n" );
@@ -585,10 +688,14 @@struct uart_pmac_port *uap =to_pmz (port);unsigned char new_reg; +if (ZS_IS_IRDA (uap) || uap->node ==NULL ) +return ; new_reg = uap->curregs[R15 ] | (DCDIE |SYNCIE |CTSIE );if (new_reg != uap->curregs[R15 ]) { uap->curregs[R15 ] = new_reg; +if (ZS_IS_ASLEEP (uap)) +return ;/* NOTE: Not subject to 'transmitter active' rule. */ write_zsreg (uap,R15 , uap->curregs[R15 ]); }
@@ -604,6 +711,8 @@unsigned char set_bits, clear_bits, new_reg;unsigned long flags; +if (uap->node ==NULL ) +return ; set_bits = clear_bits =0 ;if (break_state)
@@ -618,6 +727,8 @@ uap->curregs[R5 ] = new_reg;/* NOTE: Not subject to 'transmitter active' rule. */ +if (ZS_IS_ASLEEP (uap)) +return ;write_zsreg (uap,R5 , uap->curregs[R5 ]); }
@@ -633,39 +744,39 @@static int pmz_set_scc_power (struct uart_pmac_port *uap,int state) {int delay =0 ; +int rc;if (state) { -pmac_call_feature ( + rc =pmac_call_feature (PMAC_FTR_SCC_ENABLE , uap->node, uap->port_type,1 ); +pmz_debug ("port power on result: %d\n" , rc);if (ZS_IS_INTMODEM (uap)) { -pmac_call_feature ( + rc =pmac_call_feature (PMAC_FTR_MODEM_ENABLE , uap->node,0 ,1 ); delay =2500 ;/* wait for 2.5s before using */ - }else if (ZS_IS_IRDA (uap)) -mdelay (50 );/* Do better here once the problems -* with blocking have been ironed out -*/ +pmz_debug ("modem power result: %d\n" , rc); + } }else {/* TODO: Make that depend on a timer, don't power down * immediately */ if (ZS_IS_INTMODEM (uap)) { -pmac_call_feature ( + rc =pmac_call_feature (PMAC_FTR_MODEM_ENABLE , uap->node,0 ,0 ); +pmz_debug ("port power off result: %d\n" , rc); } -pmac_call_feature ( -PMAC_FTR_SCC_ENABLE , uap->node, uap->port_type,0 ); +pmac_call_feature (PMAC_FTR_SCC_ENABLE , uap->node, uap->port_type,0 ); }return delay; }/* * FixZeroBug....Works around a bug in the SCC receving channel. -* Taken from Darwin code, 15 Sept. 2000 -DanM +* Inspired from Darwin code, 15 Sept. 2000 -DanM * * The following sequence prevents a problem that is seen with O'Hare ASICs * (most versions -- also with some Heathrow and Hydra ASICs) where a zero -* at the input to the receiver becomes 'stuck' and locks uap the receiver. +* at the input to the receiver becomes 'stuck' and locks up the receiver. * This problem can occur as a result of a zero bit at the receiver input * coincident with any of the following events: *
@@ -687,20 +798,17 @@write_zsreg (uap,9 , (ZS_IS_CHANNEL_A (uap) ?CHRA :CHRB ) |NV );zssync (uap); -write_zsreg (uap,4 , (X1CLK |EXTSYNC )); - -/* I think this is wrong....but, I just copying code.... -*/ -write_zsreg (uap,3 , (8 & ~RxENABLE)); - -write_zsreg (uap,5 , (8 & ~TxENABLE)); +write_zsreg (uap,4 ,X1CLK |MONSYNC ); +write_zsreg (uap,3 , Rx8); +write_zsreg (uap,5 , Tx8 |RTS );write_zsreg (uap,9 ,NV );/* Didn't we already do this? */ -write_zsreg (uap,11 , (RCBR |TCBR )); +write_zsreg (uap,11 ,RCBR |TCBR );write_zsreg (uap,12 ,0 );write_zsreg (uap,13 ,0 ); -write_zsreg (uap,14 , (LOOPBAK |SSBR )); -write_zsreg (uap,14 , (LOOPBAK |SSBR |BRENAB )); -write_zsreg (uap,3 , (8 | RxENABLE)); +write_zsreg (uap,14 , (LOOPBAK |BRSRC )); +write_zsreg (uap,14 , (LOOPBAK |BRSRC |BRENAB )); +write_zsreg (uap,3 , Rx8 | RxENABLE); +write_zsreg (uap,0 ,RES_EXT_INT );write_zsreg (uap,0 ,RES_EXT_INT );write_zsreg (uap,0 ,RES_EXT_INT );/* to kill some time */
@@ -710,8 +818,8 @@* and discard everything in the receive buffer. */ write_zsreg (uap,9 ,NV ); -write_zsreg (uap,4 ,PAR_ENAB ); -write_zsreg (uap,3 , (8 & ~RxENABLE)); +write_zsreg (uap,4 ,X16CLK |SB_MASK ); +write_zsreg (uap,3 , Rx8);while (read_zsreg (uap,0 ) & Rx_CH_AV) { (void )read_zsreg (uap,8 );
@@ -721,7 +829,7 @@ }/* -* Real startup routine, powers uap the hardware and sets uap +* Real startup routine, powers up the hardware and sets up * the SCC. Returns a delay in ms where you need to wait before * actually using the port, this is typically the internal modem * powerup delay. This routine expect the lock to be taken.
@@ -732,13 +840,14 @@memset (&uap->curregs,0 ,sizeof (uap->curregs)); -/* Power uap the SCC & underlying hardware (modem/irda) */ +/* Power up the SCC & underlying hardware (modem/irda) */ pwr_delay =pmz_set_scc_power (uap,1 );/* Nice buggy HW ... */ pmz_fix_zero_bug_scc (uap); -/* Reset the chip */ +/* Reset the channel */ + uap->curregs[R9 ] =0 ;write_zsreg (uap,9 ,ZS_IS_CHANNEL_A (uap) ?CHRA :CHRB );zssync (uap);udelay (10 );
@@ -752,22 +861,47 @@write_zsreg (uap,R0 ,RES_H_IUS );write_zsreg (uap,R0 ,RES_H_IUS ); -/* Remember status for DCD/CTS changes */ - uap->prev_status =read_zsreg (uap,R0 ); +/* Setup some valid baud rate */ + uap->curregs[R4 ] =X16CLK |SB1 ; + uap->curregs[R3 ] = Rx8; + uap->curregs[R5 ] = Tx8 |RTS ; +if (!ZS_IS_IRDA (uap)) + uap->curregs[R5 ] |=DTR ; + uap->curregs[R12 ] =0 ; + uap->curregs[R13 ] =0 ; + uap->curregs[R14 ] =BRENAB ; -/* Enable receiver and transmitter. */ - uap->curregs[R3 ] |= RxENABLE; - uap->curregs[R5 ] |= TxENABLE |RTS |DTR ; +/* Clear handshaking */ + uap->curregs[R15 ] =0 ;/* Master interrupt enable */ uap->curregs[R9 ] |=NV |MIE ; - uap->curregs[R1 ] |=EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; -pmz_maybe_update_regs (uap); +pmz_load_zsregs (uap, uap->curregs); + +/* Enable receiver and transmitter. */ +write_zsreg (uap,R3 , uap->curregs[R3 ] |= RxENABLE); +write_zsreg (uap,R5 , uap->curregs[R5 ] |= TxENABLE); + +/* Remember status for DCD/CTS changes */ + uap->prev_status =read_zsreg (uap,R0 ); +return pwr_delay; } +static void pmz_irda_reset (struct uart_pmac_port *uap) +{ + uap->curregs[R5 ] |=DTR ; +write_zsreg (uap,R5 , uap->curregs[R5 ]); +zssync (uap); +mdelay (110 ); + uap->curregs[R5 ] &= ~DTR ; +write_zsreg (uap,R5 , uap->curregs[R5 ]); +zssync (uap); +mdelay (10 ); +} +/* * This is the "normal" startup routine, using the above one * wrapped with the lock and doing a schedule delay
@@ -780,20 +914,35 @@pmz_debug ("pmz: startup()\n" ); -/* A console is never powered down */ +if (ZS_IS_ASLEEP (uap)) +return -EAGAIN ; +if (uap->node ==NULL ) +return -ENODEV ; + +down (&pmz_irq_sem); + + uap->flags |=PMACZILOG_FLAG_IS_OPEN ; + +/* A console is never powered down. Else, power up and +* initialize the chip +*/ if (!ZS_IS_CONS (uap)) {spin_lock_irqsave (&port->lock, flags); pwr_delay =__pmz_startup (uap);spin_unlock_irqrestore (&port->lock, flags); - } - + } + +pmz_get_port_A (uap)->flags |=PMACZILOG_FLAG_IS_IRQ_ON ;if (request_irq (uap->port.irq, pmz_interrupt,SA_SHIRQ ,"PowerMac Zilog" , uap)) {dev_err (&uap->dev->ofdev.dev,"Unable to register zs interrupt handler.\n" );pmz_set_scc_power (uap,0 ); +up (&pmz_irq_sem);return -ENXIO ; } +up (&pmz_irq_sem); +/* Right now, we deal with delay by blocking here, I'll be * smarter later on */
@@ -803,6 +952,18 @@schedule_timeout ((pwr_delay *HZ )/1000 ); } +/* IrDA reset is done now */ +if (ZS_IS_IRDA (uap)) +pmz_irda_reset (uap); + +/* Enable interrupts emission from the chip */ +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 ]); +spin_unlock_irqrestore (&port->lock, flags); +pmz_debug ("pmz: startup() done.\n" );return 0 ;
@@ -815,20 +976,39 @@pmz_debug ("pmz: shutdown()\n" ); +if (uap->node ==NULL ) +return ; + +down (&pmz_irq_sem); +/* Release interrupt handler */ free_irq (uap->port.irq, uap); -if (ZS_IS_CONS (uap)) -return ; -spin_lock_irqsave (&port->lock, flags); + uap->flags &= ~PMACZILOG_FLAG_IS_OPEN ; + +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); +up (&pmz_irq_sem); +return ; + } +/* 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); uap->curregs[R5 ] &= ~SND_BRK ;pmz_maybe_update_regs (uap);
@@ -837,6 +1017,8 @@spin_unlock_irqrestore (&port->lock, flags); +up (&pmz_irq_sem); +pmz_debug ("pmz: shutdown() done.\n" ); }
@@ -844,26 +1026,43 @@* and local interrupts are disabled. */ static void pmz_convert_to_zs (struct uart_pmac_port *uap,unsigned int cflag, -unsigned int iflag,int baud) +unsigned int iflag,unsigned long baud) {int brg; -switch (baud) { -case ZS_CLOCK /16 :/* 230400 */ - uap->curregs[R4 ] =X16CLK ; - uap->curregs[R11 ] =0 ; -break ; -case ZS_CLOCK /32 :/* 115200 */ - uap->curregs[R4 ] =X32CLK ; - uap->curregs[R11 ] =0 ; -break ; -default : - uap->curregs[R4 ] =X16CLK ; - uap->curregs[R11 ] =TCBR |RCBR ; - brg =BPS_TO_BRG (baud,ZS_CLOCK /16 ); - uap->curregs[R12 ] = (brg &255 ); - uap->curregs[R13 ] = ((brg >>8 ) &255 ); - uap->curregs[R14 ] =BRENAB ; + +/* Switch to external clocking for IrDA high clock rates. That +* code could be re-used for Midi interfaces with different +* multipliers +*/ +if (baud >=115200 &&ZS_IS_IRDA (uap)) { + uap->curregs[R4 ] =X1CLK ; + uap->curregs[R11 ] = RCTRxCP | TCTRxCP; + uap->curregs[R14 ] =0 ;/* BRG off */ + uap->curregs[R12 ] =0 ; + uap->curregs[R13 ] =0 ; + uap->flags |=PMACZILOG_FLAG_IS_EXTCLK ; + }else { +switch (baud) { +case ZS_CLOCK /16 :/* 230400 */ + uap->curregs[R4 ] =X16CLK ; + uap->curregs[R11 ] =0 ; + uap->curregs[R14 ] =0 ; +break ; +case ZS_CLOCK /32 :/* 115200 */ + uap->curregs[R4 ] =X32CLK ; + uap->curregs[R11 ] =0 ; + uap->curregs[R14 ] =0 ; +break ; +default : + uap->curregs[R4 ] =X16CLK ; + uap->curregs[R11 ] =TCBR |RCBR ; + brg =BPS_TO_BRG (baud,ZS_CLOCK /16 ); + uap->curregs[R12 ] = (brg &255 ); + uap->curregs[R13 ] = ((brg >>8 ) &255 ); + uap->curregs[R14 ] =BRENAB ; + } + uap->flags &= ~PMACZILOG_FLAG_IS_EXTCLK ; }/* Character size, stop bits, and parity. */
@@ -926,38 +1125,54 @@ uap->port.ignore_status_mask =0xff ; } -static void pmz_irda_rts_pulses (struct uart_pmac_port *uap,int w) -{ -udelay (w); -write_zsreg (uap,5 , Tx8 | TxENABLE); -zssync (uap); -udelay (2 ); -write_zsreg (uap,5 , Tx8 | TxENABLE |RTS ); -zssync (uap); -udelay (8 ); -write_zsreg (uap,5 , Tx8 | TxENABLE); -zssync (uap); -udelay (4 ); -write_zsreg (uap,5 , Tx8 | TxENABLE |RTS ); -zssync (uap); -}/* * Set the irda codec on the imac to the specified baud rate. */ -static void pmz_irda_setup (struct uart_pmac_port *uap,int cflags) +static void pmz_irda_setup (struct uart_pmac_port *uap,unsigned long *baud) { -int code, speed, t; - - speed = cflags &CBAUD ; -if (speed <B2400 || speed >B115200 ) -return ; - code =0x4d +B115200 - speed; +u8 cmdbyte; +int t, version; -/* disable serial interrupts and receive DMA */ -write_zsreg (uap,1 , uap->curregs[1 ] & ~0x9f ); +switch (*baud) { +/* SIR modes */ +case 2400 : + cmdbyte =0x53 ; +break ; +case 4800 : + cmdbyte =0x52 ; +break ; +case 9600 : + cmdbyte =0x51 ; +break ; +case 19200 : + cmdbyte =0x50 ; +break ; +case 38400 : + cmdbyte =0x4f ; +break ; +case 57600 : + cmdbyte =0x4e ; +break ; +case 115200 : + cmdbyte =0x4d ; +break ; +/* The FIR modes aren't really supported at this point, how +* do we select the speed ? via the FCR on KeyLargo ? +*/ +case 1152000 : + cmdbyte =0 ; +break ; +case 4000000 : + cmdbyte =0 ; +break ; +default :/* 9600 */ + cmdbyte =0x51 ; + *baud =9600 ; +break ; + } -/* wait for transmitter to drain */ +/* Wait for transmitter to drain */ t =10000 ;while ((read_zsreg (uap,R0 ) & Tx_BUF_EMP) ==0 || (read_zsreg (uap,R1 ) &ALL_SNT ) ==0 ) {
@@ -967,113 +1182,170 @@ }udelay (10 ); } -udelay (100 ); - -/* set to 8 bits, no parity, 19200 baud, RTS on, DTR off */ -write_zsreg (uap,R4 ,X16CLK |SB1 ); -write_zsreg (uap,R11 ,TCBR |RCBR ); - t =BPS_TO_BRG (19200 ,ZS_CLOCK /16 ); -write_zsreg (uap,R12 , t); -write_zsreg (uap,R13 , t >>8 ); -write_zsreg (uap,R14 ,BRENAB ); -write_zsreg (uap,R3 , Rx8 | RxENABLE); -write_zsreg (uap,R5 , Tx8 | TxENABLE |RTS ); -zssync (uap); -/* set TxD low for ~104us and pulse RTS */ -udelay (1000 ); -write_zsdata (uap,0xfe ); -pmz_irda_rts_pulses (uap,150 ); -pmz_irda_rts_pulses (uap,180 ); -pmz_irda_rts_pulses (uap,50 ); -udelay (100 ); +/* Drain the receiver too */ + t =100 ; + (void )read_zsdata (uap); + (void )read_zsdata (uap); + (void )read_zsdata (uap); +mdelay (10 ); +while (read_zsreg (uap,R0 ) & Rx_CH_AV) { +read_zsdata (uap); +mdelay (10 ); +if (--t <=0 ) { +dev_err (&uap->dev->ofdev.dev,"receiver didn't drain\n" ); +return ; + } + } -/* assert DTR, wait 30ms, talk to the chip */ -write_zsreg (uap,R5 , Tx8 | TxENABLE |RTS |DTR ); +/* Switch to command mode */ + uap->curregs[R5 ] |=DTR ; +write_zsreg (uap,R5 , uap->curregs[R5 ]);zssync (uap); -mdelay (30 ); -while (read_zsreg (uap,R0 ) & Rx_CH_AV) -read_zsdata (uap); +mdelay (1 ); +/* Switch SCC to 19200 */ +pmz_convert_to_zs (uap,CS8 ,0 ,19200 ); +pmz_load_zsregs (uap, uap->curregs); +mdelay (1 ); + +/* Write get_version command byte */ write_zsdata (uap,1 ); - t =1000 ; + t =5000 ;while ((read_zsreg (uap,R0 ) & Rx_CH_AV) ==0 ) {if (--t <=0 ) {dev_err (&uap->dev->ofdev.dev, -"irda_setup timed out on 1st byte\n" ); +"irda_setup timed out on get_version byte\n" ); goto out; }udelay (10 ); } - t =read_zsdata (uap); -if (t !=4 ) -dev_err (&uap->dev->ofdev.dev,"irda_setup 1st byte = %x\n" , t); + version =read_zsdata (uap); -write_zsdata (uap, code); - t =1000 ; +if (version <4 ) { +dev_info (&uap->dev->ofdev.dev,"IrDA: dongle version %d not supported\n" , + version); + goto out; + } + +/* Send speed mode */ +write_zsdata (uap, cmdbyte); + t =5000 ;while ((read_zsreg (uap,R0 ) & Rx_CH_AV) ==0 ) {if (--t <=0 ) {dev_err (&uap->dev->ofdev.dev, -"irda_setup timed out on 2nd byte\n" ); +"irda_setup timed out on speed mode byte\n" ); goto out; }udelay (10 ); } t =read_zsdata (uap); -if (t != code) +if (t != cmdbyte)dev_err (&uap->dev->ofdev.dev, -"irda_setup 2nd byte = %x (%x)\n" , t, code); +"irda_setup speed mode byte = %x (%x)\n" , t, cmdbyte); + +dev_info (&uap->dev->ofdev.dev,"IrDA setup for %ld bps, dongle version: %d\n" , + *baud, version); + + (void )read_zsdata (uap); + (void )read_zsdata (uap); + (void )read_zsdata (uap); -/* Drop DTR again and do some more RTS pulses */ out: -udelay (100 ); -write_zsreg (uap,R5 , Tx8 | TxENABLE |RTS ); -pmz_irda_rts_pulses (uap,80 ); - -/* We should be right to go now. We assume that load_zsregs -will get called soon to load uap the correct baud rate etc. */ - uap->curregs[R5 ] = (uap->curregs[R5 ] |RTS ) & ~DTR ; +/* Switch back to data mode */ + uap->curregs[R5 ] &= ~DTR ; +write_zsreg (uap,R5 , uap->curregs[R5 ]); +zssync (uap); + + (void )read_zsdata (uap); + (void )read_zsdata (uap); + (void )read_zsdata (uap); } -/* The port lock is not held. */ -static void pmz_set_termios (struct uart_port *port,struct termios *termios, -struct termios *old) + +static void __pmz_set_termios (struct uart_port *port,struct termios *termios, +struct termios *old) {struct uart_pmac_port *uap =to_pmz (port); -unsigned long flags; -int baud; +unsigned long baud;pmz_debug ("pmz: set_termios()\n" ); - baud =uart_get_baud_rate (port, termios, old,1200 ,230400 ); - -spin_lock_irqsave (&uap->port.lock, flags); +if (ZS_IS_ASLEEP (uap)) +return ; -pmz_convert_to_zs (uap, termios->c_cflag, termios->c_iflag, baud); +memcpy (&uap->termios_cache, termios,sizeof (struct termios )); -if (UART_ENABLE_MS (&uap->port, termios->c_cflag)) { - uap->curregs[R15 ] |=DCDIE |SYNCIE |CTSIE ; - uap->flags |=PMACZILOG_FLAG_MODEM_STATUS ; +/* XXX Check which revs of machines actually allow 1 and 4Mb speeds +* on the IR dongle. Note that the IRTTY driver currently doesn't know +* about the FIR mode and high speed modes. So these are unused. For +* implementing proper support for these, we should probably add some +* DMA as well, at least on the Rx side, which isn't a simple thing +* at this point. +*/ +if (ZS_IS_IRDA (uap)) { +/* Calc baud rate */ + baud =uart_get_baud_rate (port, termios, old,1200 ,4000000 ); +pmz_debug ("pmz: switch IRDA to %ld bauds\n" , baud); +/* Cet the irda codec to the right rate */ +pmz_irda_setup (uap, &baud); +/* Set final baud rate */ +pmz_convert_to_zs (uap, termios->c_cflag, termios->c_iflag, baud); +pmz_load_zsregs (uap, uap->curregs); +zssync (uap); }else { - uap->curregs[R15 ] &= ~(DCDIE |SYNCIE |CTSIE ); - uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS ; + baud =uart_get_baud_rate (port, termios, old,1200 ,230400 ); +pmz_convert_to_zs (uap, termios->c_cflag, termios->c_iflag, baud); +/* Make sure modem status interrupts are correctly configured */ +if (UART_ENABLE_MS (&uap->port, termios->c_cflag)) { + uap->curregs[R15 ] |=DCDIE |SYNCIE |CTSIE ; + uap->flags |=PMACZILOG_FLAG_MODEM_STATUS ; + }else { + uap->curregs[R15 ] &= ~(DCDIE |SYNCIE |CTSIE ); + uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS ; + } + +/* Load registers to the chip */ +pmz_maybe_update_regs (uap); } +pmz_debug ("pmz: set_termios() done.\n" ); +} -/* set the irda codec to the right rate */ -if (ZS_IS_IRDA (uap)) -pmz_irda_setup (uap, termios->c_cflag); +/* The port lock is not held. */ +static void pmz_set_termios (struct uart_port *port,struct termios *termios, +struct termios *old) +{ +struct uart_pmac_port *uap =to_pmz (port); +unsigned long flags; -/* Load registers to the chip */ -pmz_maybe_update_regs (uap); +spin_lock_irqsave (&port->lock, flags); -spin_unlock_irqrestore (&uap->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_debug ("pmz: set_termios() done.\n" ); +/* 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 ]); + } +spin_unlock_irqrestore (&port->lock, flags); }static const char *pmz_type (struct uart_port *port) { -return "PowerMac Zilog" ; +struct uart_pmac_port *uap =to_pmz (port); + +if (ZS_IS_IRDA (uap)) +return "Z85c30 ESCC - Infrared port" ; +else if (ZS_IS_INTMODEM (uap)) +return "Z85c30 ESCC - Internal modem" ; +return "Z85c30 ESCC - Serial port" ; }/* We do not request/release mappings of the registers here, this
@@ -1218,6 +1490,12 @@ uap->port.type =PORT_PMAC_ZILOG ; uap->port.flags =0 ; +/* Setup some valid baud rate information in the register +* shadows so we don't write crap there before baud rate is +* first initialized. +*/ +pmz_convert_to_zs (uap,CS8 ,0 ,9600 ); +return 0 ; }
@@ -1250,7 +1528,8 @@ uap->dev = mdev;dev_set_drvdata (&mdev->ofdev.dev, uap);if (macio_request_resources (uap->dev,"pmac_zilog" )) -printk (KERN_WARNING "%s: Failed to request resource, port still active\n" , +printk (KERN_WARNING "%s: Failed to request resource" +", port still active\n" , uap->node->name);else uap->flags |=PMACZILOG_FLAG_RSRC_REQUESTED ;
@@ -1280,6 +1559,123 @@return 0 ; } + +static int pmz_suspend (struct macio_dev *mdev,u32 pm_state) +{ +struct uart_pmac_port *uap =dev_get_drvdata (&mdev->ofdev.dev); +struct uart_state *state = pmz_uart_reg.state + uap->port.line; +unsigned long flags; + +if (uap ==NULL ) +return 0 ; + +if (pm_state == mdev->ofdev.dev.power_state || pm_state <2 ) +return 0 ; + +down (&pmz_irq_sem); +down (&state->sem); + +spin_lock_irqsave (&uap->port.lock, flags); + +if (ZS_IS_OPEN (uap) ||ZS_IS_CONS (uap)) { +/* 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); + uap->curregs[R5 ] &= ~SND_BRK ; +pmz_load_zsregs (uap, uap->curregs); + uap->flags |=PMACZILOG_FLAG_IS_ASLEEP ; +mb (); + } + +spin_unlock_irqrestore (&uap->port.lock, flags); + +if (ZS_IS_OPEN (uap) ||ZS_IS_OPEN (uap->mate)) +if (ZS_IS_ASLEEP (uap->mate) &&ZS_IS_IRQ_ON (pmz_get_port_A (uap))) { +pmz_get_port_A (uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON ; +disable_irq (uap->port.irq); + } + +if (ZS_IS_CONS (uap)) + uap->port.cons->flags &= ~CON_ENABLED ; + +/* Shut the chip down */ +pmz_set_scc_power (uap,0 ); + +up (&state->sem); +up (&pmz_irq_sem); + + mdev->ofdev.dev.power_state = pm_state; + +return 0 ; +} + + +static int pmz_resume (struct macio_dev *mdev) +{ +struct uart_pmac_port *uap =dev_get_drvdata (&mdev->ofdev.dev); +struct uart_state *state = pmz_uart_reg.state + uap->port.line; +unsigned long flags; +int pwr_delay; + +if (uap ==NULL ) +return 0 ; + +if (mdev->ofdev.dev.power_state ==0 ) +return 0 ; + +down (&pmz_irq_sem); +down (&state->sem); + +spin_lock_irqsave (&uap->port.lock, flags); +if (!ZS_IS_OPEN (uap) && !ZS_IS_CONS (uap)) { +spin_unlock_irqrestore (&uap->port.lock, flags); + goto bail; + } + pwr_delay =__pmz_startup (uap); + +/* 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)) + uap->port.cons->flags |=CON_ENABLED ; + +/* Re-enable IRQ on the controller */ +if (ZS_IS_OPEN (uap) && !ZS_IS_IRQ_ON (pmz_get_port_A (uap))) { +pmz_get_port_A (uap)->flags |=PMACZILOG_FLAG_IS_IRQ_ON ; +enable_irq (uap->port.irq); + } + +up (&state->sem); +up (&pmz_irq_sem); + +/* Right now, we deal with delay by blocking here, I'll be +* smarter later on +*/ +if (pwr_delay !=0 ) { +pmz_debug ("pmz: delaying %d ms\n" , pwr_delay); +set_current_state (TASK_UNINTERRUPTIBLE ); +schedule_timeout ((pwr_delay *HZ )/1000 ); + } + + bail: + mdev->ofdev.dev.power_state =0 ; + +return 0 ; +} +/* * Probe all ports in the system and build the ports array, we register * with the serial layer at this point, the macio-type probing is only
@@ -1309,7 +1705,7 @@else if (strncmp (np->name,"ch-b" ,4 ) ==0 ) node_b =of_node_get (np); } -if (!node_a || !node_b) { +if (!node_a && !node_b) {of_node_put (node_a);of_node_put (node_b); printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n",
@@ -1332,7 +1728,7 @@* Setup the ports for real */ rc =pmz_init_port (&pmz_ports[count]); -if (rc ==0 ) +if (rc ==0 && node_b !=NULL ) rc =pmz_init_port (&pmz_ports[count+1 ]);if (rc !=0 ) {of_node_put (node_a);
@@ -1350,14 +1746,6 @@return 0 ; } -static struct uart_driver pmz_uart_reg = { - .owner =THIS_MODULE , - .driver_name ="ttyS" , - .devfs_name ="tts/" , - .dev_name ="ttyS" , - .major =TTY_MAJOR , -}; -#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE static void pmz_console_write (struct console *con,const char *s,unsigned int count);
@@ -1402,6 +1790,7 @@*/ for (i =0 ; i < pmz_ports_count; i++) {struct uart_pmac_port *uport = &pmz_ports[i]; +/* NULL node may happen on wallstreet */ if (uport->node !=NULL )uart_add_one_port (&pmz_uart_reg, &uport->port); }
@@ -1430,17 +1819,17 @@ .match_table = pmz_match, .probe = pmz_attach, .remove = pmz_detach, -// .suspend = pmz_suspend, *** NYI -// .resume = pmz_resume, *** NYI + .suspend = pmz_suspend, + .resume = pmz_resume, };static int __init init_pmz (void ) { -printk (KERN_DEBUG "%s\n" , version); +printk (KERN_INFO "%s\n" , version);/* * First, we need to do a direct OF-based probe pass. We -* do that because we want serial console uap before the +* do that because we want serial console up before the * macio stuffs calls us back, and since that makes it * easier to pass the proper number of channels to * uart_register_driver()
@@ -1526,6 +1915,7 @@*/ static int __init pmz_console_setup (struct console *co,char *options) { +struct uart_pmac_port *uap;struct uart_port *port;int baud =38400 ;int bits =8 ;
@@ -1537,7 +1927,8 @@* XServe's default to 57600 bps */ if (machine_is_compatible ("RackMac1,1" ) - ||machine_is_compatible ("RackMac1,2" )) + ||machine_is_compatible ("RackMac1,2" ) + ||machine_is_compatible ("MacRISC4" )) baud =57600 ;/*
@@ -1547,12 +1938,15 @@*/ if (co->index >= pmz_ports_count) co->index =0 ; - port = &pmz_ports[co->index].port; + uap = &pmz_ports[co->index]; +if (uap->node ==NULL ) +return -ENODEV ; + port = &uap->port;/* * Mark port as beeing a console */ - port->flags |=PMACZILOG_FLAG_IS_CONS ; + uap->flags |=PMACZILOG_FLAG_IS_CONS ;/* * Temporary fix for uart layer who didn't setup the spinlock yet
@@ -1562,7 +1956,7 @@/* * Enable the hardware */ - pwr_delay =__pmz_startup (&pmz_ports[co->index]); + pwr_delay =__pmz_startup (uap);if (pwr_delay)mdelay (pwr_delay);
drivers/serial/pmac_zilog.h +19 -1
@@ -11,7 +11,7 @@/* * We wrap our port structure around the generic uart_port. */ -#define NUM_ZSREGS 16 +#define NUM_ZSREGS 17struct uart_pmac_port {struct uart_port port;
@@ -43,6 +43,10 @@#define PMACZILOG_FLAG_IS_INTMODEM 0x00000200#define PMACZILOG_FLAG_HAS_DMA 0x00000400#define PMACZILOG_FLAG_RSRC_REQUESTED 0x00000800 +#define PMACZILOG_FLAG_IS_ASLEEP 0x00001000 +#define PMACZILOG_FLAG_IS_OPEN 0x00002000 +#define PMACZILOG_FLAG_IS_IRQ_ON 0x00004000 +#define PMACZILOG_FLAG_IS_EXTCLK 0x00008000unsigned char parity_mask;unsigned char prev_status;
@@ -54,10 +58,19 @@unsigned int rx_dma_irq;volatile struct dbdma_regs *tx_dma_regs;volatile struct dbdma_regs *rx_dma_regs; + +struct termios termios_cache; };#define to_pmz (p) ((struct uart_pmac_port *)(p)) +static inline struct uart_pmac_port *pmz_get_port_A (struct uart_pmac_port *uap) +{ +if (uap->flags &PMACZILOG_FLAG_IS_CHANNEL_A ) +return uap; +return uap->mate; +} +/* * Register acessors. Note that we don't need to enforce a recovery * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip,
@@ -122,6 +135,7 @@#define R13 13#define R14 14#define R15 15 +#define R7P 16#define NULLCODE 0/* Null Code */ #define POINT_HIGH 0x8/* Select upper half of registers */
@@ -359,5 +373,9 @@#define ZS_IS_IRDA (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA)#define ZS_IS_INTMODEM (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM)#define ZS_HAS_DMA (UP ) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA) +#define ZS_IS_ASLEEP (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) +#define ZS_IS_OPEN (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) +#define ZS_IS_IRQ_ON (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) +#define ZS_IS_EXTCLK (UP ) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK)#endif /* __PMAC_ZILOG_H__ */