// 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
*/
/* * ------------------------------------------------------------ * dz_in () and dz_out () * * These routines are used to access the registers of the DZ * chip, hiding relocation differences between implementation. * ------------------------------------------------------------
*/
/* * ------------------------------------------------------------ * rs_stop () and rs_start () * * These routines are called before setting or resetting * tty->flow.stopped. They enable or disable transmitter interrupts, * as necessary. * ------------------------------------------------------------
*/
/* * ------------------------------------------------------------ * * 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. * ------------------------------------------------------------
*/ staticinlinevoid 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;
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;
} elseif (status & DZ_FERR)
icount->frame++; elseif (status & DZ_PERR)
icount->parity++; if (status & DZ_OERR)
icount->overrun++;
status &= uport->read_status_mask; if (status & DZ_BREAK)
flag = TTY_BREAK; elseif (status & DZ_FERR)
flag = TTY_FRAME; elseif (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. * ------------------------------------------------------------
*/ staticinlinevoid dz_transmit_chars(struct dz_mux *mux)
{ struct dz_port *dport = &mux->dport[0]; struct tty_port *tport; unsignedchar 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. * ------------------------------------------------------------
*/ staticinlinevoid 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);
/* * ------------------------------------------------------------------- * shutdown () * * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. * -------------------------------------------------------------------
*/ staticvoid dz_shutdown(struct uart_port *uport)
{ struct dz_port *dport = to_dport(uport); struct dz_mux *mux = dport->mux; unsignedlong flags; int irq_guard;
u16 tmp;
/* * ------------------------------------------------------------------- * 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. * -------------------------------------------------------------------
*/ staticunsignedint dz_tx_empty(struct uart_port *uport)
{ struct dz_port *dport = to_dport(uport); unsignedshort tmp, mask = 1 << dport->port.line;
tmp = dz_in(dport, DZ_TCR);
tmp &= mask;
return tmp ? 0 : TIOCSER_TEMT;
}
staticvoid 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); unsignedlong flags; unsignedshort tmp, mask = 1 << dport->port.line;
staticint dz_encode_baud_rate(unsignedint 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;
}
}
#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! * -------------------------------------------------------------------
*/ staticvoid dz_console_putchar(struct uart_port *uport, unsignedchar ch)
{ struct dz_port *dport = to_dport(uport); unsignedlong flags; unsignedshort csr, tcr, trdy, mask; int loops = 10000;
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.