2005-04-17 02:20:36 +04:00
/*
* linux / drivers / serial / pmac_zilog . c
*
* Driver for PowerMac Z85c30 based ESCC cell found in the
* " macio " ASICs of various PowerMac models
*
* Copyright ( C ) 2003 Ben . Herrenschmidt ( benh @ kernel . crashing . org )
*
* Derived from drivers / macintosh / macserial . c by Paul Mackerras
* and drivers / serial / sunzilog . c by David S . Miller
*
* Hrm . . . actually , I ripped most of sunzilog ( Thanks David ! ) and
* adapted special tweaks needed for us . I don ' t think it ' s worth
* merging back those though . The DMA code still has to get in
* and once done , I expect that driver to remain fairly stable in
* the long term , unless we change the driver model again . . .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* 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
*/
# undef DEBUG
# undef DEBUG_HARD
# undef USE_CTRL_O_SYSRQ
# include <linux/config.h>
# include <linux/module.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/major.h>
# include <linux/string.h>
# include <linux/fcntl.h>
# include <linux/mm.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/console.h>
# include <linux/slab.h>
# 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>
# include <asm/prom.h>
# include <asm/machdep.h>
# include <asm/pmac_feature.h>
# include <asm/dbdma.h>
# 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>
# include "pmac_zilog.h"
/* Not yet implemented */
# undef HAS_DBDMA
static char version [ ] __initdata = " pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>) " ;
MODULE_AUTHOR ( " Benjamin Herrenschmidt <benh@kernel.crashing.org> " ) ;
MODULE_DESCRIPTION ( " Driver for the PowerMac serial ports. " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg)
/*
* For the sake of early serial console , we can do a pre - probe
* ( optional ) of the ports at rather early boot time .
*/
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 ,
} ;
/*
* Load all registers to reprogram the port
* This function must only be called when the TX is not busy . The UART
* port lock must be held and local interrupts disabled .
*/
static void pmz_load_zsregs ( struct uart_pmac_port * uap , u8 * regs )
{
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 ) ;
if ( stat & ALL_SNT )
break ;
udelay ( 100 ) ;
}
ZS_CLEARERR ( uap ) ;
zssync ( uap ) ;
ZS_CLEARFIFO ( uap ) ;
zssync ( uap ) ;
ZS_CLEARERR ( uap ) ;
/* Disable all interrupts. */
write_zsreg ( uap , R1 ,
regs [ R1 ] & ~ ( RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB ) ) ;
/* Set parity, sync config, stop bits, and clock divisor. */
write_zsreg ( uap , R4 , regs [ R4 ] ) ;
/* Set misc. TX/RX control bits. */
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 ) ;
/* 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 ] ) ;
write_zsreg ( uap , R7 , regs [ R7 ] ) ;
/* Disable baud generator. */
write_zsreg ( uap , R14 , regs [ R14 ] & ~ BRENAB ) ;
/* Clock mode control. */
write_zsreg ( uap , R11 , regs [ R11 ] ) ;
/* Lower and upper byte of baud rate generator divisor. */
write_zsreg ( uap , R12 , regs [ R12 ] ) ;
write_zsreg ( uap , R13 , regs [ R13 ] ) ;
/* Now rewrite R14, with BRENAB (if set). */
write_zsreg ( uap , R14 , regs [ R14 ] ) ;
/* Reset external status interrupts. */
write_zsreg ( uap , R0 , RES_EXT_INT ) ;
write_zsreg ( uap , R0 , RES_EXT_INT ) ;
/* Rewrite R3/R5, this time without enables masked. */
write_zsreg ( uap , R3 , regs [ R3 ] ) ;
write_zsreg ( uap , R5 , regs [ R5 ] ) ;
/* Rewrite R1, this time without IRQ enabled masked. */
write_zsreg ( uap , R1 , regs [ R1 ] ) ;
/* Enable interrupts */
write_zsreg ( uap , R9 , regs [ R9 ] ) ;
}
/*
* We do like sunzilog to avoid disrupting pending Tx
* Reprogram the Zilog channel HW registers with the copies found in the
* software state struct . If the transmitter is busy , we defer this update
* until the next TX complete interrupt . Else , we do it right now .
*
* The UART port lock must be held and local interrupts disabled .
*/
static void pmz_maybe_update_regs ( struct uart_pmac_port * uap )
{
if ( ! ZS_REGS_HELD ( uap ) ) {
if ( ZS_TX_ACTIVE ( uap ) ) {
uap - > flags | = PMACZILOG_FLAG_REGS_HELD ;
} else {
pmz_debug ( " pmz: maybe_update_regs: updating \n " ) ;
pmz_load_zsregs ( uap , uap - > curregs ) ;
}
}
}
static struct tty_struct * pmz_receive_chars ( struct uart_pmac_port * uap ,
struct pt_regs * regs )
{
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 ) {
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 )
drop = 1 ;
if ( ZS_IS_ASLEEP ( uap ) )
return NULL ;
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 & = uap - > parity_mask ;
if ( ch = = 0 & & uap - > flags & PMACZILOG_FLAG_BREAK ) {
uap - > flags & = ~ PMACZILOG_FLAG_BREAK ;
}
# if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_SERIAL_CORE_CONSOLE)
# 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 && CONFIG_SERIAL_CORE_CONSOLE */
/* 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 ) )
goto next_char ;
}
else if ( r1 & PAR_ERR )
uap - > port . icount . parity + + ;
else if ( r1 & CRC_ERR )
uap - > port . icount . frame + + ;
if ( r1 & Rx_OVR )
uap - > port . icount . overrun + + ;
r1 & = uap - > port . read_status_mask ;
if ( r1 & BRK_ABRT )
* tty - > flip . flag_buf_ptr = TTY_BREAK ;
else if ( r1 & PAR_ERR )
* tty - > flip . flag_buf_ptr = TTY_PARITY ;
else if ( r1 & CRC_ERR )
* tty - > flip . flag_buf_ptr = TTY_FRAME ;
}
if ( uap - > port . ignore_status_mask = = 0xff | |
( r1 & uap - > port . ignore_status_mask ) = = 0 ) {
tty - > flip . flag_buf_ptr + + ;
tty - > flip . char_buf_ptr + + ;
tty - > flip . count + + ;
}
if ( ( r1 & Rx_OVR ) & &
tty - > flip . count < TTY_FLIPBUF_SIZE ) {
* tty - > flip . flag_buf_ptr = TTY_OVERRUN ;
tty - > flip . flag_buf_ptr + + ;
tty - > flip . char_buf_ptr + + ;
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 ;
}
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 )
{
unsigned char status ;
status = read_zsreg ( uap , R0 ) ;
write_zsreg ( uap , R0 , RES_EXT_INT ) ;
zssync ( uap ) ;
if ( ZS_IS_OPEN ( uap ) & & ZS_WANTS_MODEM_STATUS ( uap ) ) {
if ( status & SYNC_HUNT )
uap - > port . icount . dsr + + ;
/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
* But it does not tell us which bit has changed , we have to keep
* track of this ourselves .
* The CTS input is inverted for some reason . - - paulus
*/
if ( ( status ^ uap - > prev_status ) & DCD )
uart_handle_dcd_change ( & uap - > port ,
( status & DCD ) ) ;
if ( ( status ^ uap - > prev_status ) & CTS )
uart_handle_cts_change ( & uap - > port ,
! ( status & CTS ) ) ;
wake_up_interruptible ( & uap - > port . info - > delta_msr_wait ) ;
}
if ( status & BRK_ABRT )
uap - > flags | = PMACZILOG_FLAG_BREAK ;
uap - > prev_status = status ;
}
static void pmz_transmit_chars ( struct uart_pmac_port * uap )
{
struct circ_buf * xmit ;
if ( ZS_IS_ASLEEP ( uap ) )
return ;
if ( ZS_IS_CONS ( uap ) ) {
unsigned char status = read_zsreg ( uap , R0 ) ;
/* TX still busy? Just wait for the next TX done interrupt.
*
* It can occur because of how we do serial console writes . It would
* be nice to transmit console writes just like we normally would for
* a TTY line . ( ie . buffered and TX interrupt driven ) . That is not
* easy because console writes cannot sleep . One solution might be
* to poll on enough port - > xmit space becomming free . - DaveM
*/
if ( ! ( status & Tx_BUF_EMP ) )
return ;
}
uap - > flags & = ~ PMACZILOG_FLAG_TX_ACTIVE ;
if ( ZS_REGS_HELD ( uap ) ) {
pmz_load_zsregs ( uap , uap - > curregs ) ;
uap - > flags & = ~ PMACZILOG_FLAG_REGS_HELD ;
}
if ( ZS_TX_STOPPED ( uap ) ) {
uap - > flags & = ~ PMACZILOG_FLAG_TX_STOPPED ;
goto ack_tx_int ;
}
if ( uap - > port . x_char ) {
uap - > flags | = PMACZILOG_FLAG_TX_ACTIVE ;
write_zsdata ( uap , uap - > port . x_char ) ;
zssync ( uap ) ;
uap - > port . icount . tx + + ;
uap - > port . x_char = 0 ;
return ;
}
if ( uap - > port . info = = NULL )
goto ack_tx_int ;
xmit = & uap - > port . info - > xmit ;
if ( uart_circ_empty ( xmit ) ) {
uart_write_wakeup ( & uap - > port ) ;
goto ack_tx_int ;
}
if ( uart_tx_stopped ( & uap - > port ) )
goto ack_tx_int ;
uap - > flags | = PMACZILOG_FLAG_TX_ACTIVE ;
write_zsdata ( uap , xmit - > buf [ xmit - > tail ] ) ;
zssync ( uap ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
uap - > port . icount . tx + + ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( & uap - > port ) ;
return ;
ack_tx_int :
write_zsreg ( uap , R0 , RES_Tx_P ) ;
zssync ( uap ) ;
}
/* Hrm... we register that twice, fixme later.... */
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 * uap_a ;
struct uart_pmac_port * uap_b ;
int rc = IRQ_NONE ;
struct tty_struct * tty ;
u8 r3 ;
uap_a = pmz_get_port_A ( uap ) ;
uap_b = uap_a - > mate ;
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 ( uap_a , R0 , RES_H_IUS ) ;
zssync ( uap_a ) ;
if ( r3 & CHAEXT )
pmz_status_handle ( uap_a , regs ) ;
if ( r3 & CHARxIP )
tty = pmz_receive_chars ( uap_a , regs ) ;
if ( r3 & CHATxIP )
pmz_transmit_chars ( uap_a ) ;
rc = IRQ_HANDLED ;
}
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 ( uap_b , R0 , RES_H_IUS ) ;
zssync ( uap_b ) ;
if ( r3 & CHBEXT )
pmz_status_handle ( uap_b , regs ) ;
if ( r3 & CHBRxIP )
tty = pmz_receive_chars ( uap_b , regs ) ;
if ( r3 & CHBTxIP )
pmz_transmit_chars ( uap_b ) ;
rc = IRQ_HANDLED ;
}
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 ;
}
/*
* Peek the status register , lock not held by caller
*/
static inline u8 pmz_peek_status ( struct uart_pmac_port * uap )
{
unsigned long flags ;
u8 status ;
spin_lock_irqsave ( & uap - > port . lock , flags ) ;
status = read_zsreg ( uap , R0 ) ;
spin_unlock_irqrestore ( & uap - > port . lock , flags ) ;
return status ;
}
/*
* Check if transmitter is empty
* The port lock is not held .
*/
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 ;
return 0 ;
}
/*
* Set Modem Control ( RTS & DTR ) bits
* The port lock is held and interrupts are disabled .
* Note : Shall we really filter out RTS on external ports or
* should that be dealt at higher level only ?
*/
static void pmz_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
struct uart_pmac_port * uap = to_pmz ( port ) ;
unsigned char set_bits , clear_bits ;
/* 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 ;
if ( ZS_IS_INTMODEM ( uap ) ) {
if ( mctrl & TIOCM_RTS )
set_bits | = RTS ;
else
clear_bits | = RTS ;
}
if ( mctrl & TIOCM_DTR )
set_bits | = DTR ;
else
clear_bits | = DTR ;
/* 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 ) ;
}
/*
* Get Modem Control bits ( only the input ones , the core will
* or that with a cached value of the control ones )
2005-06-29 12:42:38 +04:00
* The port lock is held and interrupts are disabled .
2005-04-17 02:20:36 +04:00
*/
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 ;
2005-06-29 12:42:38 +04:00
status = read_zsreg ( uap , R0 ) ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
if ( status & DCD )
ret | = TIOCM_CAR ;
if ( status & SYNC_HUNT )
ret | = TIOCM_DSR ;
if ( ! ( status & CTS ) )
ret | = TIOCM_CTS ;
return ret ;
}
/*
* Stop TX side . Dealt like sunzilog at next Tx interrupt ,
* though for DMA , we will have to do a bit more . What is
* the meaning of the tty_stop bit ? XXX
* The port lock is held and interrupts are disabled .
*/
static void pmz_stop_tx ( struct uart_port * port , unsigned int tty_stop )
{
to_pmz ( port ) - > flags | = PMACZILOG_FLAG_TX_STOPPED ;
}
/*
* Kick the Tx side .
* The port lock is held and interrupts are disabled .
*/
static void pmz_start_tx ( struct uart_port * port , unsigned int tty_start )
{
struct uart_pmac_port * uap = to_pmz ( port ) ;
unsigned char status ;
pmz_debug ( " pmz: start_tx() \n " ) ;
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. */
if ( ! ( status & Tx_BUF_EMP ) )
return ;
/* Send the first character to jump-start the TX done
* IRQ sending engine .
*/
if ( port - > x_char ) {
write_zsdata ( uap , port - > x_char ) ;
zssync ( uap ) ;
port - > icount . tx + + ;
port - > x_char = 0 ;
} else {
struct circ_buf * xmit = & port - > info - > xmit ;
write_zsdata ( uap , xmit - > buf [ xmit - > tail ] ) ;
zssync ( uap ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( & uap - > port ) ;
}
pmz_debug ( " pmz: start_tx() done. \n " ) ;
}
/*
* Stop Rx side , basically disable emitting of
* 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_ASLEEP ( uap ) | | uap - > node = = NULL )
return ;
pmz_debug ( " pmz: stop_rx()() \n " ) ;
/* Disable all RX interrupts. */
uap - > curregs [ R1 ] & = ~ RxINT_MASK ;
pmz_maybe_update_regs ( uap ) ;
pmz_debug ( " pmz: stop_rx() done. \n " ) ;
}
/*
* Enable modem status change interrupts
* The port lock is held .
*/
static void pmz_enable_ms ( struct uart_port * port )
{
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 ] ) ;
}
}
/*
* Control break state emission
* The port lock is not held .
*/
static void pmz_break_ctl ( struct uart_port * port , int break_state )
{
struct uart_pmac_port * uap = to_pmz ( port ) ;
unsigned char set_bits , clear_bits , new_reg ;
unsigned long flags ;
if ( uap - > node = = NULL )
return ;
set_bits = clear_bits = 0 ;
if ( break_state )
set_bits | = SND_BRK ;
else
clear_bits | = SND_BRK ;
spin_lock_irqsave ( & port - > lock , flags ) ;
new_reg = ( uap - > curregs [ R5 ] | set_bits ) & ~ clear_bits ;
if ( new_reg ! = uap - > curregs [ R5 ] ) {
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 ] ) ;
}
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
/*
* Turn power on or off to the SCC and associated stuff
* ( port drivers , modem , IR port , etc . )
* Returns the number of milliseconds we should wait before
* trying to use the port .
*/
static int pmz_set_scc_power ( struct uart_pmac_port * uap , int state )
{
int delay = 0 ;
int rc ;
if ( state ) {
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 ) ) {
rc = pmac_call_feature (
PMAC_FTR_MODEM_ENABLE , uap - > node , 0 , 1 ) ;
delay = 2500 ; /* wait for 2.5s before using */
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 ) ) {
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 ) ;
}
return delay ;
}
/*
* FixZeroBug . . . . Works around a bug in the SCC receving channel .
* 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 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 :
*
* The SCC is initialized ( hardware or software ) .
* A framing error is detected .
* The clocking option changes from synchronous or X1 asynchronous
* clocking to X16 , X32 , or X64 asynchronous clocking .
* The decoding mode is changed among NRZ , NRZI , FM0 , or FM1 .
*
* This workaround attempts to recover from the lockup condition by placing
* the SCC in synchronous loopback mode with a fast clock before programming
* any of the asynchronous modes .
*/
static void pmz_fix_zero_bug_scc ( struct uart_pmac_port * uap )
{
write_zsreg ( uap , 9 , ZS_IS_CHANNEL_A ( uap ) ? CHRA : CHRB ) ;
zssync ( uap ) ;
udelay ( 10 ) ;
write_zsreg ( uap , 9 , ( ZS_IS_CHANNEL_A ( uap ) ? CHRA : CHRB ) | NV ) ;
zssync ( uap ) ;
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 , 12 , 0 ) ;
write_zsreg ( uap , 13 , 0 ) ;
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 */
/* The channel should be OK now, but it is probably receiving
* loopback garbage .
* Switch to asynchronous mode , disable the receiver ,
* and discard everything in the receive buffer .
*/
write_zsreg ( uap , 9 , NV ) ;
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 ) ;
write_zsreg ( uap , 0 , RES_EXT_INT ) ;
write_zsreg ( uap , 0 , ERR_RES ) ;
}
}
/*
* 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 .
*/
static int __pmz_startup ( struct uart_pmac_port * uap )
{
int pwr_delay = 0 ;
memset ( & uap - > curregs , 0 , sizeof ( uap - > curregs ) ) ;
/* 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 channel */
uap - > curregs [ R9 ] = 0 ;
write_zsreg ( uap , 9 , ZS_IS_CHANNEL_A ( uap ) ? CHRA : CHRB ) ;
zssync ( uap ) ;
udelay ( 10 ) ;
write_zsreg ( uap , 9 , 0 ) ;
zssync ( uap ) ;
/* Clear the interrupt registers */
write_zsreg ( uap , R1 , 0 ) ;
write_zsreg ( uap , R0 , ERR_RES ) ;
write_zsreg ( uap , R0 , ERR_RES ) ;
write_zsreg ( uap , R0 , RES_H_IUS ) ;
write_zsreg ( uap , R0 , RES_H_IUS ) ;
/* 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 ;
/* Clear handshaking, enable BREAK interrupts */
uap - > curregs [ R15 ] = BRKIE ;
/* Master interrupt enable */
uap - > curregs [ R9 ] | = NV | MIE ;
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
*/
static int pmz_startup ( struct uart_port * port )
{
struct uart_pmac_port * uap = to_pmz ( port ) ;
unsigned long flags ;
int pwr_delay = 0 ;
pmz_debug ( " pmz: startup() \n " ) ;
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
*/
if ( pwr_delay ! = 0 ) {
pmz_debug ( " pmz: delaying %d ms \n " , pwr_delay ) ;
msleep ( pwr_delay ) ;
}
/* 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 ;
}
static void pmz_shutdown ( struct uart_port * port )
{
struct uart_pmac_port * uap = to_pmz ( port ) ;
unsigned long flags ;
pmz_debug ( " pmz: shutdown() \n " ) ;
if ( uap - > node = = NULL )
return ;
down ( & pmz_irq_sem ) ;
/* Release interrupt handler */
free_irq ( uap - > port . irq , uap ) ;
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 [ R5 ] & = ~ SND_BRK ;
pmz_maybe_update_regs ( uap ) ;
/* Shut the chip down */
pmz_set_scc_power ( uap , 0 ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
up ( & pmz_irq_sem ) ;
pmz_debug ( " pmz: shutdown() done. \n " ) ;
}
/* Shared by TTY driver and serial console setup. The port lock is held
* and local interrupts are disabled .
*/
static void pmz_convert_to_zs ( struct uart_pmac_port * uap , unsigned int cflag ,
unsigned int iflag , unsigned long baud )
{
int brg ;
/* 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. */
uap - > curregs [ 3 ] & = ~ RxN_MASK ;
uap - > curregs [ 5 ] & = ~ TxN_MASK ;
switch ( cflag & CSIZE ) {
case CS5 :
uap - > curregs [ 3 ] | = Rx5 ;
uap - > curregs [ 5 ] | = Tx5 ;
uap - > parity_mask = 0x1f ;
break ;
case CS6 :
uap - > curregs [ 3 ] | = Rx6 ;
uap - > curregs [ 5 ] | = Tx6 ;
uap - > parity_mask = 0x3f ;
break ;
case CS7 :
uap - > curregs [ 3 ] | = Rx7 ;
uap - > curregs [ 5 ] | = Tx7 ;
uap - > parity_mask = 0x7f ;
break ;
case CS8 :
default :
uap - > curregs [ 3 ] | = Rx8 ;
uap - > curregs [ 5 ] | = Tx8 ;
uap - > parity_mask = 0xff ;
break ;
} ;
uap - > curregs [ 4 ] & = ~ ( SB_MASK ) ;
if ( cflag & CSTOPB )
uap - > curregs [ 4 ] | = SB2 ;
else
uap - > curregs [ 4 ] | = SB1 ;
if ( cflag & PARENB )
uap - > curregs [ 4 ] | = PAR_ENAB ;
else
uap - > curregs [ 4 ] & = ~ PAR_ENAB ;
if ( ! ( cflag & PARODD ) )
uap - > curregs [ 4 ] | = PAR_EVEN ;
else
uap - > curregs [ 4 ] & = ~ PAR_EVEN ;
uap - > port . read_status_mask = Rx_OVR ;
if ( iflag & INPCK )
uap - > port . read_status_mask | = CRC_ERR | PAR_ERR ;
if ( iflag & ( BRKINT | PARMRK ) )
uap - > port . read_status_mask | = BRK_ABRT ;
uap - > port . ignore_status_mask = 0 ;
if ( iflag & IGNPAR )
uap - > port . ignore_status_mask | = CRC_ERR | PAR_ERR ;
if ( iflag & IGNBRK ) {
uap - > port . ignore_status_mask | = BRK_ABRT ;
if ( iflag & IGNPAR )
uap - > port . ignore_status_mask | = Rx_OVR ;
}
if ( ( cflag & CREAD ) = = 0 )
uap - > port . ignore_status_mask = 0xff ;
}
/*
* Set the irda codec on the imac to the specified baud rate .
*/
static void pmz_irda_setup ( struct uart_pmac_port * uap , unsigned long * baud )
{
u8 cmdbyte ;
int t , version ;
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 */
t = 10000 ;
while ( ( read_zsreg ( uap , R0 ) & Tx_BUF_EMP ) = = 0
| | ( read_zsreg ( uap , R1 ) & ALL_SNT ) = = 0 ) {
if ( - - t < = 0 ) {
dev_err ( & uap - > dev - > ofdev . dev , " transmitter didn't drain \n " ) ;
return ;
}
udelay ( 10 ) ;
}
/* 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 ;
}
}
/* Switch to command mode */
uap - > curregs [ R5 ] | = DTR ;
write_zsreg ( uap , R5 , uap - > curregs [ R5 ] ) ;
zssync ( 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 = 5000 ;
while ( ( read_zsreg ( uap , R0 ) & Rx_CH_AV ) = = 0 ) {
if ( - - t < = 0 ) {
dev_err ( & uap - > dev - > ofdev . dev ,
" irda_setup timed out on get_version byte \n " ) ;
goto out ;
}
udelay ( 10 ) ;
}
version = read_zsdata ( uap ) ;
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 speed mode byte \n " ) ;
goto out ;
}
udelay ( 10 ) ;
}
t = read_zsdata ( uap ) ;
if ( t ! = cmdbyte )
dev_err ( & uap - > dev - > ofdev . dev ,
" 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 ) ;
out :
/* 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 ) ;
}
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 baud ;
pmz_debug ( " pmz: set_termios() \n " ) ;
if ( ZS_IS_ASLEEP ( uap ) )
return ;
memcpy ( & uap - > termios_cache , termios , sizeof ( struct termios ) ) ;
/* 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 {
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 ) ;
}
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
pmz_debug ( " pmz: set_termios() done. \n " ) ;
}
/* 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 ;
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 ] ) ;
/* 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 )
{
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
* happens at early serial probe time .
*/
static void pmz_release_port ( struct uart_port * port )
{
}
static int pmz_request_port ( struct uart_port * port )
{
return 0 ;
}
/* These do not need to do anything interesting either. */
static void pmz_config_port ( struct uart_port * port , int flags )
{
}
/* We do not support letting the user mess with the divisor, IRQ, etc. */
static int pmz_verify_port ( struct uart_port * port , struct serial_struct * ser )
{
return - EINVAL ;
}
static struct uart_ops pmz_pops = {
. tx_empty = pmz_tx_empty ,
. set_mctrl = pmz_set_mctrl ,
. get_mctrl = pmz_get_mctrl ,
. stop_tx = pmz_stop_tx ,
. start_tx = pmz_start_tx ,
. stop_rx = pmz_stop_rx ,
. enable_ms = pmz_enable_ms ,
. break_ctl = pmz_break_ctl ,
. startup = pmz_startup ,
. shutdown = pmz_shutdown ,
. set_termios = pmz_set_termios ,
. type = pmz_type ,
. release_port = pmz_release_port ,
. request_port = pmz_request_port ,
. config_port = pmz_config_port ,
. verify_port = pmz_verify_port ,
} ;
/*
* Setup one port structure after probing , HW is down at this point ,
* Unlike sunzilog , we don ' t need to pre - init the spinlock as we don ' t
* register our console before uart_add_one_port ( ) is called
*/
static int __init pmz_init_port ( struct uart_pmac_port * uap )
{
struct device_node * np = uap - > node ;
char * conn ;
struct slot_names_prop {
int count ;
char name [ 1 ] ;
} * slots ;
int len ;
/*
* Request & map chip registers
*/
uap - > port . mapbase = np - > addrs [ 0 ] . address ;
uap - > port . membase = ioremap ( uap - > port . mapbase , 0x1000 ) ;
uap - > control_reg = uap - > port . membase ;
uap - > data_reg = uap - > control_reg + 0x10 ;
/*
* Request & map DBDMA registers
*/
# ifdef HAS_DBDMA
if ( np - > n_addrs > = 3 & & np - > n_intrs > = 3 )
uap - > flags | = PMACZILOG_FLAG_HAS_DMA ;
# endif
if ( ZS_HAS_DMA ( uap ) ) {
uap - > tx_dma_regs = ioremap ( np - > addrs [ np - > n_addrs - 2 ] . address , 0x1000 ) ;
if ( uap - > tx_dma_regs = = NULL ) {
uap - > flags & = ~ PMACZILOG_FLAG_HAS_DMA ;
goto no_dma ;
}
uap - > rx_dma_regs = ioremap ( np - > addrs [ np - > n_addrs - 1 ] . address , 0x1000 ) ;
if ( uap - > rx_dma_regs = = NULL ) {
iounmap ( uap - > tx_dma_regs ) ;
uap - > tx_dma_regs = NULL ;
uap - > flags & = ~ PMACZILOG_FLAG_HAS_DMA ;
goto no_dma ;
}
uap - > tx_dma_irq = np - > intrs [ 1 ] . line ;
uap - > rx_dma_irq = np - > intrs [ 2 ] . line ;
}
no_dma :
/*
* Detect port type
*/
if ( device_is_compatible ( np , " cobalt " ) )
uap - > flags | = PMACZILOG_FLAG_IS_INTMODEM ;
conn = get_property ( np , " AAPL,connector " , & len ) ;
if ( conn & & ( strcmp ( conn , " infrared " ) = = 0 ) )
uap - > flags | = PMACZILOG_FLAG_IS_IRDA ;
uap - > port_type = PMAC_SCC_ASYNC ;
/* 1999 Powerbook G3 has slot-names property instead */
slots = ( struct slot_names_prop * ) get_property ( np , " slot-names " , & len ) ;
if ( slots & & slots - > count > 0 ) {
if ( strcmp ( slots - > name , " IrDA " ) = = 0 )
uap - > flags | = PMACZILOG_FLAG_IS_IRDA ;
else if ( strcmp ( slots - > name , " Modem " ) = = 0 )
uap - > flags | = PMACZILOG_FLAG_IS_INTMODEM ;
}
if ( ZS_IS_IRDA ( uap ) )
uap - > port_type = PMAC_SCC_IRDA ;
if ( ZS_IS_INTMODEM ( uap ) ) {
struct device_node * i2c_modem = find_devices ( " i2c-modem " ) ;
if ( i2c_modem ) {
char * mid = get_property ( i2c_modem , " modem-id " , NULL ) ;
if ( mid ) switch ( * mid ) {
case 0x04 :
case 0x05 :
case 0x07 :
case 0x08 :
case 0x0b :
case 0x0c :
uap - > port_type = PMAC_SCC_I2S1 ;
}
printk ( KERN_INFO " pmac_zilog: i2c-modem detected, id: %d \n " ,
mid ? ( * mid ) : 0 ) ;
} else {
printk ( KERN_INFO " pmac_zilog: serial modem detected \n " ) ;
}
}
/*
* Init remaining bits of " port " structure
*/
uap - > port . iotype = SERIAL_IO_MEM ;
uap - > port . irq = np - > intrs [ 0 ] . line ;
uap - > port . uartclk = ZS_CLOCK ;
uap - > port . fifosize = 1 ;
uap - > port . ops = & pmz_pops ;
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 ;
}
/*
* Get rid of a port on module removal
*/
static void pmz_dispose_port ( struct uart_pmac_port * uap )
{
struct device_node * np ;
np = uap - > node ;
iounmap ( uap - > rx_dma_regs ) ;
iounmap ( uap - > tx_dma_regs ) ;
iounmap ( uap - > control_reg ) ;
uap - > node = NULL ;
of_node_put ( np ) ;
memset ( uap , 0 , sizeof ( struct uart_pmac_port ) ) ;
}
/*
* Called upon match with an escc node in the devive - tree .
*/
static int pmz_attach ( struct macio_dev * mdev , const struct of_match * match )
{
int i ;
/* Iterate the pmz_ports array to find a matching entry
*/
for ( i = 0 ; i < MAX_ZS_PORTS ; i + + )
if ( pmz_ports [ i ] . node = = mdev - > ofdev . node ) {
struct uart_pmac_port * uap = & pmz_ports [ i ] ;
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 " ,
uap - > node - > name ) ;
else
uap - > flags | = PMACZILOG_FLAG_RSRC_REQUESTED ;
return 0 ;
}
return - ENODEV ;
}
/*
* That one should not be called , macio isn ' t really a hotswap device ,
* we don ' t expect one of those serial ports to go away . . .
*/
static int pmz_detach ( struct macio_dev * mdev )
{
struct uart_pmac_port * uap = dev_get_drvdata ( & mdev - > ofdev . dev ) ;
if ( ! uap )
return - ENODEV ;
if ( uap - > flags & PMACZILOG_FLAG_RSRC_REQUESTED ) {
macio_release_resources ( uap - > dev ) ;
uap - > flags & = ~ PMACZILOG_FLAG_RSRC_REQUESTED ;
}
dev_set_drvdata ( & mdev - > ofdev . dev , NULL ) ;
uap - > dev = NULL ;
return 0 ;
}
2005-04-17 02:25:35 +04:00
static int pmz_suspend ( struct macio_dev * mdev , pm_message_t pm_state )
2005-04-17 02:20:36 +04:00
{
struct uart_pmac_port * uap = dev_get_drvdata ( & mdev - > ofdev . dev ) ;
struct uart_state * state ;
unsigned long flags ;
if ( uap = = NULL ) {
printk ( " HRM... pmz_suspend with NULL uap \n " ) ;
return 0 ;
}
if ( pm_state = = mdev - > ofdev . dev . power . power_state | | pm_state < 2 )
return 0 ;
pmz_debug ( " suspend, switching to state %d \n " , pm_state ) ;
state = pmz_uart_reg . state + uap - > port . line ;
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 ) ;
pmz_debug ( " suspend, switching complete \n " ) ;
mdev - > ofdev . dev . power . 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 ;
unsigned long flags ;
int pwr_delay = 0 ;
if ( uap = = NULL )
return 0 ;
if ( mdev - > ofdev . dev . power . power_state = = 0 )
return 0 ;
pmz_debug ( " resume, switching to state 0 \n " ) ;
state = pmz_uart_reg . state + uap - > port . line ;
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 ) ;
}
bail :
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 ) ;
msleep ( pwr_delay ) ;
}
pmz_debug ( " resume, switching complete \n " ) ;
mdev - > ofdev . dev . power . 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
* used later to " attach " to the sysfs tree so we get power management
* events
*/
static int __init pmz_probe ( void )
{
struct device_node * node_p , * node_a , * node_b , * np ;
int count = 0 ;
int rc ;
/*
* Find all escc chips in the system
*/
node_p = of_find_node_by_name ( NULL , " escc " ) ;
while ( node_p ) {
/*
* First get channel A / B node pointers
*
* TODO : Add routines with proper locking to do that . . .
*/
node_a = node_b = NULL ;
for ( np = NULL ; ( np = of_get_next_child ( node_p , np ) ) ! = NULL ; ) {
if ( strncmp ( np - > name , " ch-a " , 4 ) = = 0 )
node_a = of_node_get ( np ) ;
else if ( strncmp ( np - > name , " ch-b " , 4 ) = = 0 )
node_b = of_node_get ( np ) ;
}
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 " ,
( ! node_a ) ? ' a ' : ' b ' , node_p - > full_name ) ;
goto next ;
}
/*
* Fill basic fields in the port structures
*/
pmz_ports [ count ] . mate = & pmz_ports [ count + 1 ] ;
pmz_ports [ count + 1 ] . mate = & pmz_ports [ count ] ;
pmz_ports [ count ] . flags = PMACZILOG_FLAG_IS_CHANNEL_A ;
pmz_ports [ count ] . node = node_a ;
pmz_ports [ count + 1 ] . node = node_b ;
pmz_ports [ count ] . port . line = count ;
pmz_ports [ count + 1 ] . port . line = count + 1 ;
/*
* Setup the ports for real
*/
rc = pmz_init_port ( & pmz_ports [ count ] ) ;
if ( rc = = 0 & & node_b ! = NULL )
rc = pmz_init_port ( & pmz_ports [ count + 1 ] ) ;
if ( rc ! = 0 ) {
of_node_put ( node_a ) ;
of_node_put ( node_b ) ;
memset ( & pmz_ports [ count ] , 0 , sizeof ( struct uart_pmac_port ) ) ;
memset ( & pmz_ports [ count + 1 ] , 0 , sizeof ( struct uart_pmac_port ) ) ;
goto next ;
}
count + = 2 ;
next :
node_p = of_find_node_by_name ( node_p , " escc " ) ;
}
pmz_ports_count = count ;
return 0 ;
}
# ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
static void pmz_console_write ( struct console * con , const char * s , unsigned int count ) ;
static int __init pmz_console_setup ( struct console * co , char * options ) ;
static struct console pmz_console = {
. name = " ttyS " ,
. write = pmz_console_write ,
. device = uart_console_device ,
. setup = pmz_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & pmz_uart_reg ,
} ;
# define PMACZILOG_CONSOLE &pmz_console
# else /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
# define PMACZILOG_CONSOLE (NULL)
# endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
/*
* Register the driver , console driver and ports with the serial
* core
*/
static int __init pmz_register ( void )
{
int i , rc ;
pmz_uart_reg . nr = pmz_ports_count ;
pmz_uart_reg . cons = PMACZILOG_CONSOLE ;
pmz_uart_reg . minor = 64 ;
/*
* Register this driver with the serial core
*/
rc = uart_register_driver ( & pmz_uart_reg ) ;
if ( rc )
return rc ;
/*
* Register each port with the serial core
*/
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 )
rc = uart_add_one_port ( & pmz_uart_reg , & uport - > port ) ;
if ( rc )
goto err_out ;
}
return 0 ;
err_out :
while ( i - - > 0 ) {
struct uart_pmac_port * uport = & pmz_ports [ i ] ;
uart_remove_one_port ( & pmz_uart_reg , & uport - > port ) ;
}
uart_unregister_driver ( & pmz_uart_reg ) ;
return rc ;
}
static struct of_match pmz_match [ ] =
{
{
. name = " ch-a " ,
. type = OF_ANY_MATCH ,
. compatible = OF_ANY_MATCH
} ,
{
. name = " ch-b " ,
. type = OF_ANY_MATCH ,
. compatible = OF_ANY_MATCH
} ,
{ } ,
} ;
static struct macio_driver pmz_driver =
{
. name = " pmac_zilog " ,
. match_table = pmz_match ,
. probe = pmz_attach ,
. remove = pmz_detach ,
. suspend = pmz_suspend ,
. resume = pmz_resume ,
} ;
static int __init init_pmz ( void )
{
int rc , i ;
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 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 ( )
*/
if ( pmz_ports_count = = 0 )
pmz_probe ( ) ;
/*
* Bail early if no port found
*/
if ( pmz_ports_count = = 0 )
return - ENODEV ;
/*
* Now we register with the serial layer
*/
rc = pmz_register ( ) ;
if ( rc ) {
printk ( KERN_ERR
" pmac_zilog: Error registering serial device, disabling pmac_zilog. \n "
" pmac_zilog: Did another serial driver already claim the minors? \n " ) ;
/* effectively "pmz_unprobe()" */
for ( i = 0 ; i < pmz_ports_count ; i + + )
pmz_dispose_port ( & pmz_ports [ i ] ) ;
return rc ;
}
/*
* Then we register the macio driver itself
*/
return macio_register_driver ( & pmz_driver ) ;
}
static void __exit exit_pmz ( void )
{
int i ;
/* Get rid of macio-driver (detach from macio) */
macio_unregister_driver ( & pmz_driver ) ;
for ( i = 0 ; i < pmz_ports_count ; i + + ) {
struct uart_pmac_port * uport = & pmz_ports [ i ] ;
if ( uport - > node ! = NULL ) {
uart_remove_one_port ( & pmz_uart_reg , & uport - > port ) ;
pmz_dispose_port ( uport ) ;
}
}
/* Unregister UART driver */
uart_unregister_driver ( & pmz_uart_reg ) ;
}
# ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port . . .
*/
static void pmz_console_write ( struct console * con , const char * s , unsigned int count )
{
struct uart_pmac_port * uap = & pmz_ports [ con - > index ] ;
unsigned long flags ;
int i ;
if ( ZS_IS_ASLEEP ( uap ) )
return ;
spin_lock_irqsave ( & uap - > port . lock , flags ) ;
/* Turn of interrupts and enable the transmitter. */
write_zsreg ( uap , R1 , uap - > curregs [ 1 ] & ~ TxINT_ENAB ) ;
write_zsreg ( uap , R5 , uap - > curregs [ 5 ] | TxENABLE | RTS | DTR ) ;
for ( i = 0 ; i < count ; i + + ) {
/* Wait for the transmit buffer to empty. */
while ( ( read_zsreg ( uap , R0 ) & Tx_BUF_EMP ) = = 0 )
udelay ( 5 ) ;
write_zsdata ( uap , s [ i ] ) ;
if ( s [ i ] = = 10 ) {
while ( ( read_zsreg ( uap , R0 ) & Tx_BUF_EMP ) = = 0 )
udelay ( 5 ) ;
write_zsdata ( uap , R13 ) ;
}
}
/* Restore the values in the registers. */
write_zsreg ( uap , R1 , uap - > curregs [ 1 ] ) ;
/* Don't disable the transmitter. */
spin_unlock_irqrestore ( & uap - > port . lock , flags ) ;
}
/*
* Setup the serial console
*/
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 ;
int parity = ' n ' ;
int flow = ' n ' ;
unsigned long pwr_delay ;
/*
* XServe ' s default to 57600 bps
*/
if ( machine_is_compatible ( " RackMac1,1 " )
| | machine_is_compatible ( " RackMac1,2 " )
| | machine_is_compatible ( " MacRISC4 " ) )
baud = 57600 ;
/*
* Check whether an invalid uart number has been specified , and
* if so , search for the first available port that does have
* console support .
*/
if ( co - > index > = pmz_ports_count )
co - > index = 0 ;
uap = & pmz_ports [ co - > index ] ;
if ( uap - > node = = NULL )
return - ENODEV ;
port = & uap - > port ;
/*
* Mark port as beeing a console
*/
uap - > flags | = PMACZILOG_FLAG_IS_CONS ;
/*
* Temporary fix for uart layer who didn ' t setup the spinlock yet
*/
spin_lock_init ( & port - > lock ) ;
/*
* Enable the hardware
*/
pwr_delay = __pmz_startup ( uap ) ;
if ( pwr_delay )
mdelay ( pwr_delay ) ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
static int __init pmz_console_init ( void )
{
/* Probe ports */
pmz_probe ( ) ;
/* TODO: Autoprobe console based on OF */
/* pmz_console.index = i; */
register_console ( & pmz_console ) ;
return 0 ;
}
console_initcall ( pmz_console_init ) ;
# endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
module_init ( init_pmz ) ;
module_exit ( exit_pmz ) ;