root/drivers/tty/serial/dz.c
// SPDX-License-Identifier: GPL-2.0
/*
 * dz.c: Serial port driver for DECstations equipped
 *       with the DZ chipset.
 *
 * Copyright (C) 1998 Olivier A. D. Lebaillif
 *
 * Email: olivier.lebaillif@ifrsys.com
 *
 * Copyright (C) 2004, 2006, 2007  Maciej W. Rozycki
 *
 * [31-AUG-98] triemer
 * Changed IRQ to use Harald's dec internals interrupts.h
 * removed base_addr code - moving address assignment to setup.c
 * Changed name of dz_init to rs_init to be consistent with tc code
 * [13-NOV-98] triemer fixed code to receive characters
 *    after patches by harald to irq code.
 * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout
 *            field from "current" - somewhere between 2.1.121 and 2.1.131
 Qua Jun 27 15:02:26 BRT 2001
 * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups
 *
 * Parts (C) 1999 David Airlie, airlied@linux.ie
 * [07-SEP-99] Bugfixes
 *
 * [06-Jan-2002] Russell King <rmk@arm.linux.org.uk>
 * Converted to new serial core
 */

#undef DEBUG_DZ

#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>

#include <linux/atomic.h>
#include <linux/io.h>
#include <asm/bootinfo.h>

#include <asm/dec/interrupts.h>
#include <asm/dec/kn01.h>
#include <asm/dec/kn02.h>
#include <asm/dec/machtype.h>
#include <asm/dec/prom.h>
#include <asm/dec/system.h>

#include "dz.h"


MODULE_DESCRIPTION("DECstation DZ serial driver");
MODULE_LICENSE("GPL");


static char dz_name[] __initdata = "DECstation DZ serial driver version ";
static char dz_version[] __initdata = "1.04";

struct dz_port {
        struct dz_mux           *mux;
        struct uart_port        port;
        unsigned int            cflag;
};

struct dz_mux {
        struct dz_port          dport[DZ_NB_PORT];
        atomic_t                map_guard;
        atomic_t                irq_guard;
        int                     initialised;
};

static struct dz_mux dz_mux;

static inline struct dz_port *to_dport(struct uart_port *uport)
{
        return container_of(uport, struct dz_port, port);
}

/*
 * ------------------------------------------------------------
 * dz_in () and dz_out ()
 *
 * These routines are used to access the registers of the DZ
 * chip, hiding relocation differences between implementation.
 * ------------------------------------------------------------
 */

static u16 dz_in(struct dz_port *dport, unsigned offset)
{
        void __iomem *addr = dport->port.membase + offset;

        return readw(addr);
}

static void dz_out(struct dz_port *dport, unsigned offset, u16 value)
{
        void __iomem *addr = dport->port.membase + offset;

        writew(value, addr);
}

/*
 * ------------------------------------------------------------
 * rs_stop () and rs_start ()
 *
 * These routines are called before setting or resetting
 * tty->flow.stopped. They enable or disable transmitter interrupts,
 * as necessary.
 * ------------------------------------------------------------
 */

static void dz_stop_tx(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);
        u16 tmp, mask = 1 << dport->port.line;

        tmp = dz_in(dport, DZ_TCR);     /* read the TX flag */
        tmp &= ~mask;                   /* clear the TX flag */
        dz_out(dport, DZ_TCR, tmp);
}

static void dz_start_tx(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);
        u16 tmp, mask = 1 << dport->port.line;

        tmp = dz_in(dport, DZ_TCR);     /* read the TX flag */
        tmp |= mask;                    /* set the TX flag */
        dz_out(dport, DZ_TCR, tmp);
}

static void dz_stop_rx(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);

        dport->cflag &= ~DZ_RXENAB;
        dz_out(dport, DZ_LPR, dport->cflag);
}

/*
 * ------------------------------------------------------------
 *
 * Here start the interrupt handling routines.  All of the following
 * subroutines are declared as inline and are folded into
 * dz_interrupt.  They were separated out for readability's sake.
 *
 * Note: dz_interrupt() is a "fast" interrupt, which means that it
 * runs with interrupts turned off.  People who may want to modify
 * dz_interrupt() should try to keep the interrupt handler as fast as
 * possible.  After you are done making modifications, it is not a bad
 * idea to do:
 *
 *      make drivers/serial/dz.s
 *
 * and look at the resulting assemble code in dz.s.
 *
 * ------------------------------------------------------------
 */

/*
 * ------------------------------------------------------------
 * receive_char ()
 *
 * This routine deals with inputs from any lines.
 * ------------------------------------------------------------
 */
static inline void dz_receive_chars(struct dz_mux *mux)
{
        struct uart_port *uport;
        struct dz_port *dport = &mux->dport[0];
        struct uart_icount *icount;
        int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 };
        u16 status;
        u8 ch, flag;
        int i;

        while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) {
                dport = &mux->dport[LINE(status)];
                uport = &dport->port;

                ch = UCHAR(status);             /* grab the char */
                flag = TTY_NORMAL;

                icount = &uport->icount;
                icount->rx++;

                if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) {

                        /*
                         * There is no separate BREAK status bit, so treat
                         * null characters with framing errors as BREAKs;
                         * normally, otherwise.  For this move the Framing
                         * Error bit to a simulated BREAK bit.
                         */
                        if (!ch) {
                                status |= (status & DZ_FERR) >>
                                          (ffs(DZ_FERR) - ffs(DZ_BREAK));
                                status &= ~DZ_FERR;
                        }

                        /* Handle SysRq/SAK & keep track of the statistics. */
                        if (status & DZ_BREAK) {
                                icount->brk++;
                                if (uart_handle_break(uport))
                                        continue;
                        } else if (status & DZ_FERR)
                                icount->frame++;
                        else if (status & DZ_PERR)
                                icount->parity++;
                        if (status & DZ_OERR)
                                icount->overrun++;

                        status &= uport->read_status_mask;
                        if (status & DZ_BREAK)
                                flag = TTY_BREAK;
                        else if (status & DZ_FERR)
                                flag = TTY_FRAME;
                        else if (status & DZ_PERR)
                                flag = TTY_PARITY;

                }

                if (uart_handle_sysrq_char(uport, ch))
                        continue;

                uart_insert_char(uport, status, DZ_OERR, ch, flag);
                lines_rx[LINE(status)] = 1;
        }
        for (i = 0; i < DZ_NB_PORT; i++)
                if (lines_rx[i])
                        tty_flip_buffer_push(&mux->dport[i].port.state->port);
}

/*
 * ------------------------------------------------------------
 * transmit_char ()
 *
 * This routine deals with outputs to any lines.
 * ------------------------------------------------------------
 */
static inline void dz_transmit_chars(struct dz_mux *mux)
{
        struct dz_port *dport = &mux->dport[0];
        struct tty_port *tport;
        unsigned char tmp;
        u16 status;

        status = dz_in(dport, DZ_CSR);
        dport = &mux->dport[LINE(status)];
        tport = &dport->port.state->port;

        if (dport->port.x_char) {               /* XON/XOFF chars */
                dz_out(dport, DZ_TDR, dport->port.x_char);
                dport->port.icount.tx++;
                dport->port.x_char = 0;
                return;
        }
        /* If nothing to do or stopped or hardware stopped. */
        if (uart_tx_stopped(&dport->port) ||
                        !uart_fifo_get(&dport->port, &tmp)) {
                uart_port_lock(&dport->port);
                dz_stop_tx(&dport->port);
                uart_port_unlock(&dport->port);
                return;
        }

        /*
         * If something to do... (remember the dz has no output fifo,
         * so we go one char at a time) :-<
         */
        dz_out(dport, DZ_TDR, tmp);

        if (kfifo_len(&tport->xmit_fifo) < DZ_WAKEUP_CHARS)
                uart_write_wakeup(&dport->port);

        /* Are we are done. */
        if (kfifo_is_empty(&tport->xmit_fifo)) {
                uart_port_lock(&dport->port);
                dz_stop_tx(&dport->port);
                uart_port_unlock(&dport->port);
        }
}

/*
 * ------------------------------------------------------------
 * check_modem_status()
 *
 * DS 3100 & 5100: Only valid for the MODEM line, duh!
 * DS 5000/200: Valid for the MODEM and PRINTER line.
 * ------------------------------------------------------------
 */
static inline void check_modem_status(struct dz_port *dport)
{
        /*
         * FIXME:
         * 1. No status change interrupt; use a timer.
         * 2. Handle the 3100/5000 as appropriate. --macro
         */
        u16 status;

        /* If not the modem line just return.  */
        if (dport->port.line != DZ_MODEM)
                return;

        status = dz_in(dport, DZ_MSR);

        /* it's easy, since DSR2 is the only bit in the register */
        if (status)
                dport->port.icount.dsr++;
}

/*
 * ------------------------------------------------------------
 * dz_interrupt ()
 *
 * this is the main interrupt routine for the DZ chip.
 * It deals with the multiple ports.
 * ------------------------------------------------------------
 */
static irqreturn_t dz_interrupt(int irq, void *dev_id)
{
        struct dz_mux *mux = dev_id;
        struct dz_port *dport = &mux->dport[0];
        u16 status;

        /* get the reason why we just got an irq */
        status = dz_in(dport, DZ_CSR);

        if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE))
                dz_receive_chars(mux);

        if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE))
                dz_transmit_chars(mux);

        return IRQ_HANDLED;
}

/*
 * -------------------------------------------------------------------
 * Here ends the DZ interrupt routines.
 * -------------------------------------------------------------------
 */

static unsigned int dz_get_mctrl(struct uart_port *uport)
{
        /*
         * FIXME: Handle the 3100/5000 as appropriate. --macro
         */
        struct dz_port *dport = to_dport(uport);
        unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;

        if (dport->port.line == DZ_MODEM) {
                if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR)
                        mctrl &= ~TIOCM_DSR;
        }

        return mctrl;
}

static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl)
{
        /*
         * FIXME: Handle the 3100/5000 as appropriate. --macro
         */
        struct dz_port *dport = to_dport(uport);
        u16 tmp;

        if (dport->port.line == DZ_MODEM) {
                tmp = dz_in(dport, DZ_TCR);
                if (mctrl & TIOCM_DTR)
                        tmp &= ~DZ_MODEM_DTR;
                else
                        tmp |= DZ_MODEM_DTR;
                dz_out(dport, DZ_TCR, tmp);
        }
}

/*
 * -------------------------------------------------------------------
 * startup ()
 *
 * various initialization tasks
 * -------------------------------------------------------------------
 */
static int dz_startup(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);
        struct dz_mux *mux = dport->mux;
        unsigned long flags;
        int irq_guard;
        int ret;
        u16 tmp;

        irq_guard = atomic_add_return(1, &mux->irq_guard);
        if (irq_guard != 1)
                return 0;

        ret = request_irq(dport->port.irq, dz_interrupt,
                          IRQF_SHARED, "dz", mux);
        if (ret) {
                atomic_add(-1, &mux->irq_guard);
                printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq);
                return ret;
        }

        uart_port_lock_irqsave(&dport->port, &flags);

        /* Enable interrupts.  */
        tmp = dz_in(dport, DZ_CSR);
        tmp |= DZ_RIE | DZ_TIE;
        dz_out(dport, DZ_CSR, tmp);

        uart_port_unlock_irqrestore(&dport->port, flags);

        return 0;
}

/*
 * -------------------------------------------------------------------
 * shutdown ()
 *
 * This routine will shutdown a serial port; interrupts are disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 * -------------------------------------------------------------------
 */
static void dz_shutdown(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);
        struct dz_mux *mux = dport->mux;
        unsigned long flags;
        int irq_guard;
        u16 tmp;

        uart_port_lock_irqsave(&dport->port, &flags);
        dz_stop_tx(&dport->port);
        uart_port_unlock_irqrestore(&dport->port, flags);

        irq_guard = atomic_add_return(-1, &mux->irq_guard);
        if (!irq_guard) {
                /* Disable interrupts.  */
                tmp = dz_in(dport, DZ_CSR);
                tmp &= ~(DZ_RIE | DZ_TIE);
                dz_out(dport, DZ_CSR, tmp);

                free_irq(dport->port.irq, mux);
        }
}

/*
 * -------------------------------------------------------------------
 * dz_tx_empty() -- get the transmitter empty status
 *
 * Purpose: Let user call ioctl() to get info when the UART physically
 *          is emptied.  On bus types like RS485, the transmitter must
 *          release the bus after transmitting. This must be done when
 *          the transmit shift register is empty, not be done when the
 *          transmit holding register is empty.  This functionality
 *          allows an RS485 driver to be written in user space.
 * -------------------------------------------------------------------
 */
static unsigned int dz_tx_empty(struct uart_port *uport)
{
        struct dz_port *dport = to_dport(uport);
        unsigned short tmp, mask = 1 << dport->port.line;

        tmp = dz_in(dport, DZ_TCR);
        tmp &= mask;

        return tmp ? 0 : TIOCSER_TEMT;
}

static void dz_break_ctl(struct uart_port *uport, int break_state)
{
        /*
         * FIXME: Can't access BREAK bits in TDR easily;
         * reuse the code for polled TX. --macro
         */
        struct dz_port *dport = to_dport(uport);
        unsigned long flags;
        unsigned short tmp, mask = 1 << dport->port.line;

        uart_port_lock_irqsave(uport, &flags);
        tmp = dz_in(dport, DZ_TCR);
        if (break_state)
                tmp |= mask;
        else
                tmp &= ~mask;
        dz_out(dport, DZ_TCR, tmp);
        uart_port_unlock_irqrestore(uport, flags);
}

static int dz_encode_baud_rate(unsigned int baud)
{
        switch (baud) {
        case 50:
                return DZ_B50;
        case 75:
                return DZ_B75;
        case 110:
                return DZ_B110;
        case 134:
                return DZ_B134;
        case 150:
                return DZ_B150;
        case 300:
                return DZ_B300;
        case 600:
                return DZ_B600;
        case 1200:
                return DZ_B1200;
        case 1800:
                return DZ_B1800;
        case 2000:
                return DZ_B2000;
        case 2400:
                return DZ_B2400;
        case 3600:
                return DZ_B3600;
        case 4800:
                return DZ_B4800;
        case 7200:
                return DZ_B7200;
        case 9600:
                return DZ_B9600;
        default:
                return -1;
        }
}


static void dz_reset(struct dz_port *dport)
{
        struct dz_mux *mux = dport->mux;

        if (mux->initialised)
                return;

        dz_out(dport, DZ_CSR, DZ_CLR);
        while (dz_in(dport, DZ_CSR) & DZ_CLR);
        iob();

        /* Enable scanning.  */
        dz_out(dport, DZ_CSR, DZ_MSE);

        mux->initialised = 1;
}

static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
                           const struct ktermios *old_termios)
{
        struct dz_port *dport = to_dport(uport);
        unsigned long flags;
        unsigned int cflag, baud;
        int bflag;

        cflag = dport->port.line;

        switch (termios->c_cflag & CSIZE) {
        case CS5:
                cflag |= DZ_CS5;
                break;
        case CS6:
                cflag |= DZ_CS6;
                break;
        case CS7:
                cflag |= DZ_CS7;
                break;
        case CS8:
        default:
                cflag |= DZ_CS8;
        }

        if (termios->c_cflag & CSTOPB)
                cflag |= DZ_CSTOPB;
        if (termios->c_cflag & PARENB)
                cflag |= DZ_PARENB;
        if (termios->c_cflag & PARODD)
                cflag |= DZ_PARODD;

        baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600);
        bflag = dz_encode_baud_rate(baud);
        if (bflag < 0)  {
                if (old_termios) {
                        /* Keep unchanged. */
                        baud = tty_termios_baud_rate(old_termios);
                        bflag = dz_encode_baud_rate(baud);
                }
                if (bflag < 0)  {               /* Resort to 9600.  */
                        baud = 9600;
                        bflag = DZ_B9600;
                }
                tty_termios_encode_baud_rate(termios, baud, baud);
        }
        cflag |= bflag;

        if (termios->c_cflag & CREAD)
                cflag |= DZ_RXENAB;

        uart_port_lock_irqsave(&dport->port, &flags);

        uart_update_timeout(uport, termios->c_cflag, baud);

        dz_out(dport, DZ_LPR, cflag);
        dport->cflag = cflag;

        /* setup accept flag */
        dport->port.read_status_mask = DZ_OERR;
        if (termios->c_iflag & INPCK)
                dport->port.read_status_mask |= DZ_FERR | DZ_PERR;
        if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
                dport->port.read_status_mask |= DZ_BREAK;

        /* characters to ignore */
        uport->ignore_status_mask = 0;
        if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
                dport->port.ignore_status_mask |= DZ_OERR;
        if (termios->c_iflag & IGNPAR)
                dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR;
        if (termios->c_iflag & IGNBRK)
                dport->port.ignore_status_mask |= DZ_BREAK;

        uart_port_unlock_irqrestore(&dport->port, flags);
}

/*
 * Hack alert!
 * Required solely so that the initial PROM-based console
 * works undisturbed in parallel with this one.
 */
static void dz_pm(struct uart_port *uport, unsigned int state,
                  unsigned int oldstate)
{
        struct dz_port *dport = to_dport(uport);
        unsigned long flags;

        uart_port_lock_irqsave(&dport->port, &flags);
        if (state < 3)
                dz_start_tx(&dport->port);
        else
                dz_stop_tx(&dport->port);
        uart_port_unlock_irqrestore(&dport->port, flags);
}


static const char *dz_type(struct uart_port *uport)
{
        return "DZ";
}

static void dz_release_port(struct uart_port *uport)
{
        struct dz_mux *mux = to_dport(uport)->mux;
        int map_guard;

        iounmap(uport->membase);
        uport->membase = NULL;

        map_guard = atomic_add_return(-1, &mux->map_guard);
        if (!map_guard)
                release_mem_region(uport->mapbase, dec_kn_slot_size);
}

static int dz_map_port(struct uart_port *uport)
{
        if (!uport->membase)
                uport->membase = ioremap(uport->mapbase,
                                                 dec_kn_slot_size);
        if (!uport->membase) {
                printk(KERN_ERR "dz: Cannot map MMIO\n");
                return -ENOMEM;
        }
        return 0;
}

static int dz_request_port(struct uart_port *uport)
{
        struct dz_mux *mux = to_dport(uport)->mux;
        int map_guard;
        int ret;

        map_guard = atomic_add_return(1, &mux->map_guard);
        if (map_guard == 1) {
                if (!request_mem_region(uport->mapbase, dec_kn_slot_size,
                                        "dz")) {
                        atomic_add(-1, &mux->map_guard);
                        printk(KERN_ERR
                               "dz: Unable to reserve MMIO resource\n");
                        return -EBUSY;
                }
        }
        ret = dz_map_port(uport);
        if (ret) {
                map_guard = atomic_add_return(-1, &mux->map_guard);
                if (!map_guard)
                        release_mem_region(uport->mapbase, dec_kn_slot_size);
                return ret;
        }
        return 0;
}

static void dz_config_port(struct uart_port *uport, int flags)
{
        struct dz_port *dport = to_dport(uport);

        if (flags & UART_CONFIG_TYPE) {
                if (dz_request_port(uport))
                        return;

                uport->type = PORT_DZ;

                dz_reset(dport);
        }
}

/*
 * Verify the new serial_struct (for TIOCSSERIAL).
 */
static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser)
{
        int ret = 0;

        if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ)
                ret = -EINVAL;
        if (ser->irq != uport->irq)
                ret = -EINVAL;
        return ret;
}

static const struct uart_ops dz_ops = {
        .tx_empty       = dz_tx_empty,
        .get_mctrl      = dz_get_mctrl,
        .set_mctrl      = dz_set_mctrl,
        .stop_tx        = dz_stop_tx,
        .start_tx       = dz_start_tx,
        .stop_rx        = dz_stop_rx,
        .break_ctl      = dz_break_ctl,
        .startup        = dz_startup,
        .shutdown       = dz_shutdown,
        .set_termios    = dz_set_termios,
        .pm             = dz_pm,
        .type           = dz_type,
        .release_port   = dz_release_port,
        .request_port   = dz_request_port,
        .config_port    = dz_config_port,
        .verify_port    = dz_verify_port,
};

static void __init dz_init_ports(void)
{
        static int first = 1;
        unsigned long base;
        int line;

        if (!first)
                return;
        first = 0;

        if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100)
                base = dec_kn_slot_base + KN01_DZ11;
        else
                base = dec_kn_slot_base + KN02_DZ11;

        for (line = 0; line < DZ_NB_PORT; line++) {
                struct dz_port *dport = &dz_mux.dport[line];
                struct uart_port *uport = &dport->port;

                dport->mux      = &dz_mux;

                uport->irq      = dec_interrupt[DEC_IRQ_DZ11];
                uport->fifosize = 1;
                uport->iotype   = UPIO_MEM;
                uport->flags    = UPF_BOOT_AUTOCONF;
                uport->ops      = &dz_ops;
                uport->line     = line;
                uport->mapbase  = base;
                uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE);
        }
}

#ifdef CONFIG_SERIAL_DZ_CONSOLE
/*
 * -------------------------------------------------------------------
 * dz_console_putchar() -- transmit a character
 *
 * Polled transmission.  This is tricky.  We need to mask transmit
 * interrupts so that they do not interfere, enable the transmitter
 * for the line requested and then wait till the transmit scanner
 * requests data for this line.  But it may request data for another
 * line first, in which case we have to disable its transmitter and
 * repeat waiting till our line pops up.  Only then the character may
 * be transmitted.  Finally, the state of the transmitter mask is
 * restored.  Welcome to the world of PDP-11!
 * -------------------------------------------------------------------
 */
static void dz_console_putchar(struct uart_port *uport, unsigned char ch)
{
        struct dz_port *dport = to_dport(uport);
        unsigned long flags;
        unsigned short csr, tcr, trdy, mask;
        int loops = 10000;

        uart_port_lock_irqsave(&dport->port, &flags);
        csr = dz_in(dport, DZ_CSR);
        dz_out(dport, DZ_CSR, csr & ~DZ_TIE);
        tcr = dz_in(dport, DZ_TCR);
        tcr |= 1 << dport->port.line;
        mask = tcr;
        dz_out(dport, DZ_TCR, mask);
        iob();
        uart_port_unlock_irqrestore(&dport->port, flags);

        do {
                trdy = dz_in(dport, DZ_CSR);
                if (!(trdy & DZ_TRDY))
                        continue;
                trdy = (trdy & DZ_TLINE) >> 8;
                if (trdy == dport->port.line)
                        break;
                mask &= ~(1 << trdy);
                dz_out(dport, DZ_TCR, mask);
                iob();
                udelay(2);
        } while (--loops);

        if (loops)                              /* Cannot send otherwise. */
                dz_out(dport, DZ_TDR, ch);

        dz_out(dport, DZ_TCR, tcr);
        dz_out(dport, DZ_CSR, csr);
}

/*
 * -------------------------------------------------------------------
 * dz_console_print ()
 *
 * dz_console_print is registered for printk.
 * The console must be locked when we get here.
 * -------------------------------------------------------------------
 */
static void dz_console_print(struct console *co,
                             const char *str,
                             unsigned int count)
{
        struct dz_port *dport = &dz_mux.dport[co->index];
#ifdef DEBUG_DZ
        prom_printf((char *) str);
#endif
        uart_console_write(&dport->port, str, count, dz_console_putchar);
}

static int __init dz_console_setup(struct console *co, char *options)
{
        struct dz_port *dport = &dz_mux.dport[co->index];
        struct uart_port *uport = &dport->port;
        int baud = 9600;
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
        int ret;

        ret = dz_map_port(uport);
        if (ret)
                return ret;

        spin_lock_init(&dport->port.lock);      /* For dz_pm().  */

        dz_reset(dport);
        dz_pm(uport, 0, -1);

        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);

        return uart_set_options(&dport->port, co, baud, parity, bits, flow);
}

static struct uart_driver dz_reg;
static struct console dz_console = {
        .name   = "ttyS",
        .write  = dz_console_print,
        .device = uart_console_device,
        .setup  = dz_console_setup,
        .flags  = CON_PRINTBUFFER,
        .index  = -1,
        .data   = &dz_reg,
};

static int __init dz_serial_console_init(void)
{
        if (!IOASIC) {
                dz_init_ports();
                register_console(&dz_console);
                return 0;
        } else
                return -ENXIO;
}

console_initcall(dz_serial_console_init);

#define SERIAL_DZ_CONSOLE       &dz_console
#else
#define SERIAL_DZ_CONSOLE       NULL
#endif /* CONFIG_SERIAL_DZ_CONSOLE */

static struct uart_driver dz_reg = {
        .owner                  = THIS_MODULE,
        .driver_name            = "serial",
        .dev_name               = "ttyS",
        .major                  = TTY_MAJOR,
        .minor                  = 64,
        .nr                     = DZ_NB_PORT,
        .cons                   = SERIAL_DZ_CONSOLE,
};

static int __init dz_init(void)
{
        int ret, i;

        if (IOASIC)
                return -ENXIO;

        printk("%s%s\n", dz_name, dz_version);

        dz_init_ports();

        ret = uart_register_driver(&dz_reg);
        if (ret)
                return ret;

        for (i = 0; i < DZ_NB_PORT; i++)
                uart_add_one_port(&dz_reg, &dz_mux.dport[i].port);

        return 0;
}

module_init(dz_init);