// SPDX-License-Identifier: GPL-2.0 /* * Driver for Zilog serial chips found on SGI workstations and * servers. This driver could actually be made more generic. * * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the * old drivers/sgi/char/sgiserial.c code which itself is based of the original * drivers/sbus/char/zs.c code. A lot of code has been simply moved over * directly from there but much has been rewritten. Credits therefore go out * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell * for their work there. * * Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org) * Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/major.h> #include <linux/string.h> #include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/circ_buf.h> #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> #include <linux/spinlock.h> #include <linux/init.h> #include <linux/platform_device.h>
/* * On IP22 we need to delay after register accesses but we do not need to * flush writes.
*/ #define ZSDELAY() udelay(5) #define ZSDELAY_LONG() udelay(20) #define ZS_WSYNC(channel) do { } while (0)
/* Reading and writing Zilog8530 registers. The delays are to make this * driver work on the IP22 which needs a settling delay after each chip * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. * * The port lock must be held and local IRQs must be disabled * when {read,write}_zsreg is invoked.
*/ staticunsignedchar read_zsreg(struct zilog_channel *channel, unsignedchar reg)
{ unsignedchar retval;
/* This function must only be called when the TX is not busy. The UART * port lock must be held and local interrupts disabled.
*/ staticvoid __load_zsregs(struct zilog_channel *channel, unsignedchar *regs)
{ int i;
/* Let pending transmits finish. */ for (i = 0; i < 1000; i++) { unsignedchar stat = read_zsreg(channel, R1); if (stat & ALL_SNT) break;
udelay(100);
}
/* Don't mess with the interrupt vector (R2, unused by us) and * master interrupt control (R9). We make sure this is setup * properly at probe time then never touch it again.
*/
/* Rewrite R3/R5, this time without enables masked. */
write_zsreg(channel, R3, regs[R3]);
write_zsreg(channel, R5, regs[R5]);
/* Rewrite R1, this time without IRQ enabled masked. */
write_zsreg(channel, R1, regs[R1]);
}
/* 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.
*/ staticvoid ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up, struct zilog_channel *channel)
{ if (!ZS_REGS_HELD(up)) { if (ZS_TX_ACTIVE(up)) {
up->flags |= IP22ZILOG_FLAG_REGS_HELD;
} else {
__load_zsregs(channel, up->curregs);
}
}
}
/* Handle the null char got when BREAK is removed. */ if (!ch)
r1 |= up->tty_break;
/* A real serial line, record the character and status. */
flag = TTY_NORMAL;
up->port.icount.rx++; if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {
up->tty_break = 0;
if (r1 & (Rx_SYS | Rx_BRK)) {
up->port.icount.brk++; if (r1 & Rx_SYS) continue;
r1 &= ~(PAR_ERR | CRC_ERR);
} elseif (r1 & PAR_ERR)
up->port.icount.parity++; elseif (r1 & CRC_ERR)
up->port.icount.frame++; if (r1 & Rx_OVR)
up->port.icount.overrun++;
r1 &= up->port.read_status_mask; if (r1 & Rx_BRK)
flag = TTY_BREAK; elseif (r1 & PAR_ERR)
flag = TTY_PARITY; elseif (r1 & CRC_ERR)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch)) continue;
if (up->curregs[R15] & BRKIE) { if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) { if (uart_handle_break(&up->port))
up->tty_break = Rx_SYS; else
up->tty_break = Rx_BRK;
}
}
if (ZS_WANTS_MODEM_STATUS(up)) { if (status & SYNC)
up->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.
*/ if ((status ^ up->prev_status) ^ DCD)
uart_handle_dcd_change(&up->port,
(status & DCD)); if ((status ^ up->prev_status) ^ CTS)
uart_handle_cts_change(&up->port,
(status & CTS));
if (ZS_IS_CONS(up)) { unsignedchar status = readb(&channel->control);
ZSDELAY();
/* 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 becoming free. -DaveM
*/ if (!(status & Tx_BUF_EMP)) return;
}
up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE;
if (ZS_REGS_HELD(up)) {
__load_zsregs(channel, up->curregs);
up->flags &= ~IP22ZILOG_FLAG_REGS_HELD;
}
if (ZS_TX_STOPPED(up)) {
up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; goto ack_tx_int;
}
if (up->port.x_char) {
up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
writeb(up->port.x_char, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHBRxIP)
push = ip22zilog_receive_chars(up, channel); if (r3 & CHBEXT)
ip22zilog_status_handle(up, channel); if (r3 & CHBTxIP)
ip22zilog_transmit_chars(up, channel);
}
uart_port_unlock(&up->port);
if (push)
tty_flip_buffer_push(&up->port.state->port);
return IRQ_HANDLED;
}
/* A convenient way to quickly get R0 status. The caller must _not_ hold the * port lock, it is acquired here.
*/ static __inline__ unsignedchar ip22zilog_read_channel_status(struct uart_port *port)
{ struct zilog_channel *channel; unsignedchar status;
channel = ZILOG_CHANNEL_FROM_PORT(port);
status = readb(&channel->control);
ZSDELAY();
return status;
}
/* The port lock is not held. */ staticunsignedint ip22zilog_tx_empty(struct uart_port *port)
{ unsignedlong flags; unsignedchar status; unsignedint ret;
uart_port_lock_irqsave(port, &flags);
status = ip22zilog_read_channel_status(port);
uart_port_unlock_irqrestore(port, flags);
if (status & Tx_BUF_EMP)
ret = TIOCSER_TEMT; else
ret = 0;
return ret;
}
/* The port lock is held and interrupts are disabled. */ staticunsignedint ip22zilog_get_mctrl(struct uart_port *port)
{ unsignedchar status; unsignedint ret;
status = ip22zilog_read_channel_status(port);
ret = 0; if (status & DCD)
ret |= TIOCM_CAR; if (status & SYNC)
ret |= TIOCM_DSR; if (status & CTS)
ret |= TIOCM_CTS;
return ret;
}
/* The port lock is held and interrupts are disabled. */ staticvoid ip22zilog_set_mctrl(struct uart_port *port, unsignedint mctrl)
{ struct uart_ip22zilog_port *up =
container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsignedchar set_bits, clear_bits;
/* NOTE: Not subject to 'transmitter active' rule. */
up->curregs[R5] |= set_bits;
up->curregs[R5] &= ~clear_bits;
write_zsreg(channel, R5, up->curregs[R5]);
}
/* The port lock is held and interrupts are disabled. */ staticvoid ip22zilog_stop_tx(struct uart_port *port)
{ struct uart_ip22zilog_port *up =
container_of(port, struct uart_ip22zilog_port, port);
up->flags |= IP22ZILOG_FLAG_TX_STOPPED;
}
/* The port lock is held and interrupts are disabled. */ staticvoid ip22zilog_start_tx(struct uart_port *port)
{ struct uart_ip22zilog_port *up =
container_of(port, struct uart_ip22zilog_port, port); struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); unsignedchar status;
/* 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) {
writeb(port->x_char, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
if (!uart_fifo_get(port, &c)) return;
writeb(c, &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
}
}
/* The port lock is held and interrupts are disabled. */ staticvoid ip22zilog_stop_rx(struct uart_port *port)
{ struct uart_ip22zilog_port *up = UART_ZILOG(port); struct zilog_channel *channel;
/* NOTE: Not subject to 'transmitter active' rule. */
write_zsreg(channel, R5, up->curregs[R5]);
}
uart_port_unlock_irqrestore(port, flags);
}
staticvoid __ip22zilog_reset(struct uart_ip22zilog_port *up)
{ struct zilog_channel *channel; int i;
if (up->flags & IP22ZILOG_FLAG_RESET_DONE) return;
/* Let pending transmits finish. */
channel = ZILOG_CHANNEL_FROM_PORT(&up->port); for (i = 0; i < 1000; i++) { unsignedchar stat = read_zsreg(channel, R1); if (stat & ALL_SNT) break;
udelay(100);
}
up = &ip22zilog_port_table[CHANNEL_A];
channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
/* * The test for ZS_IS_CONS is explained by the following e-mail: ***** * From: Russell King <rmk@arm.linux.org.uk> * Date: Sun, 8 Dec 2002 10:18:38 +0000 * * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, * > and I noticed that something is not right with reference * > counting in this case. It seems that when the console * > is open by kernel initially, this is not accounted * > as an open, and uart_startup is not called. * * That is correct. We are unable to call uart_startup when the serial * console is initialised because it may need to allocate memory (as * request_irq does) and the memory allocators may not have been * initialised. * * 1. initialise the port into a state where it can send characters in the * console write method. * * 2. don't do the actual hardware shutdown in your shutdown() method (but * do the normal software shutdown - ie, free irqs etc) *****
*/ staticvoid ip22zilog_shutdown(struct uart_port *port)
{ struct uart_ip22zilog_port *up = UART_ZILOG(port); struct zilog_channel *channel; unsignedlong flags;
/* Disable all interrupts and BRK assertion. */
up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
up->curregs[R5] &= ~SND_BRK;
ip22zilog_maybe_update_regs(up, channel);
uart_port_unlock_irqrestore(port, flags);
}
/* Shared by TTY driver and serial console setup. The port lock is held * and local interrupts are disabled.
*/ staticvoid
ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsignedint cflag, unsignedint iflag, int brg)
{
/* We do not request/release mappings of the registers here, this * happens at early serial probe time.
*/ staticvoid ip22zilog_release_port(struct uart_port *port)
{
}
/* These do not need to do anything interesting either. */ staticvoid ip22zilog_config_port(struct uart_port *port, int flags)
{
}
/* We do not support letting the user mess with the divisor, IRQ, etc. */ staticint ip22zilog_verify_port(struct uart_port *port, struct serial_struct *ser)
{ return -EINVAL;
}
/* This is a timed polling loop so do not switch the explicit * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM
*/ do { unsignedchar val = readb(&channel->control); if (val & Tx_BUF_EMP) {
ZSDELAY(); break;
}
udelay(5);
} while (--loops);
/* David wrote it but I'm to blame for the bugs ... */
MODULE_AUTHOR("Ralf Baechle ");
MODULE_DESCRIPTION("SGI Zilog serial port driver");
MODULE_LICENSE("GPL");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.