// SPDX-License-Identifier: GPL-2.0 /* * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1998-1999 Pete Zaitcev (zaitcev@yahoo.com) * * This is mainly a variation of 8250.c, credits go to authors mentioned * therein. In fact this driver should be merged into the generic 8250.c * infrastructure perhaps using a 8250_sparc.c module. * * Fixed to use tty_get_baud_rate(). * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * * Converted to new 2.5.x UART layer. * David S. Miller (davem@davemloft.net), 2002-Jul-29
*/
staticvoid serial_out(struct uart_sunsu_port *up, int offset, int value)
{ #ifndef CONFIG_SPARC64 /* * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are * connected with a gate then go to SlavIO. When IRQ4 goes tristated * gate outputs a logical one. Since we use level triggered interrupts * we have lockup and watchdog reset. We cannot mask IRQ because * keyboard shares IRQ with us (Word has it as Bob Smelik's design). * This problem is similar to what Alpha people suffer, see * 8250_alpha.c.
*/ if (offset == UART_MCR)
value |= UART_MCR_OUT2; #endif
offset <<= up->port.regshift;
/* * For the 16C950
*/ staticvoid serial_icr_write(struct uart_sunsu_port *up, int offset, int value)
{
serial_out(up, UART_SCR, offset);
serial_out(up, UART_ICR, value);
}
#ifdef CONFIG_SERIAL_8250_RSA /* * Attempts to turn on the RSA FIFO. Returns zero on failure. * We set the port uart clock rate if we succeed.
*/ staticint __enable_rsa(struct uart_sunsu_port *up)
{ unsignedchar mode; int result;
mode = serial_in(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
if (!result) {
serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
mode = serial_in(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
}
if (result)
up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
/* * Attempts to turn off the RSA FIFO. Returns zero on failure. * It is unknown why interrupts were disabled in here. However, * the caller is expected to preserve this behaviour by grabbing * the spinlock before calling this function.
*/ staticvoid disable_rsa(struct uart_sunsu_port *up)
{ unsignedchar mode; int result;
/* * We really want to stop the transmitter from sending.
*/ if (up->port.type == PORT_16C950) {
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
}
do {
ch = serial_in(up, UART_RX);
flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
UART_LSR_FE | UART_LSR_OE))) { /* * For statistics only
*/ if (*status & UART_LSR_BI) {
*status &= ~(UART_LSR_FE | UART_LSR_PE);
up->port.icount.brk++; if (up->port.cons != NULL &&
up->port.line == up->port.cons->index)
saw_console_brk = 1; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask.
*/ if (uart_handle_break(&up->port)) goto ignore_char;
} elseif (*status & UART_LSR_PE)
up->port.icount.parity++; elseif (*status & UART_LSR_FE)
up->port.icount.frame++; if (*status & UART_LSR_OE)
up->port.icount.overrun++;
/* * Mask off conditions which should be ingored.
*/
*status &= up->port.read_status_mask;
if (up->port.cons != NULL &&
up->port.line == up->port.cons->index) { /* Recover the break flag from console xmit */
*status |= up->lsr_break_flag;
up->lsr_break_flag = 0;
}
if (*status & UART_LSR_BI) {
flag = TTY_BREAK;
} elseif (*status & UART_LSR_PE)
flag = TTY_PARITY; elseif (*status & UART_LSR_FE)
flag = TTY_FRAME;
} if (uart_handle_sysrq_char(&up->port, ch)) goto ignore_char; if ((*status & up->port.ignore_status_mask) == 0)
tty_insert_flip_char(port, ch, flag); if (*status & UART_LSR_OE) /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character.
*/
tty_insert_flip_char(port, 0, TTY_OVERRUN);
ignore_char:
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
if (up->port.x_char) {
serial_out(up, UART_TX, up->port.x_char);
up->port.icount.tx++;
up->port.x_char = 0; return;
} if (uart_tx_stopped(&up->port)) {
sunsu_stop_tx(&up->port); return;
} if (kfifo_is_empty(&tport->xmit_fifo)) {
__stop_tx(up); return;
}
count = up->port.fifosize; do { if (!uart_fifo_get(&up->port, &ch)) break;
serial_out(up, UART_TX, ch);
} while (--count > 0);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
if (kfifo_is_empty(&tport->xmit_fifo))
__stop_tx(up);
}
staticvoid check_modem_status(struct uart_sunsu_port *up)
{ int status;
status = serial_in(up, UART_MSR);
if ((status & UART_MSR_ANY_DELTA) == 0) return;
if (status & UART_MSR_TERI)
up->port.icount.rng++; if (status & UART_MSR_DDSR)
up->port.icount.dsr++; if (status & UART_MSR_DDCD)
uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); if (status & UART_MSR_DCTS)
uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
do {
status = serial_in(up, UART_LSR); if (status & UART_LSR_DR)
receive_chars(up, &status);
check_modem_status(up); if (status & UART_LSR_THRE)
transmit_chars(up);
tty_flip_buffer_push(&up->port.state->port);
} while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT));
uart_port_unlock_irqrestore(&up->port, flags);
return IRQ_HANDLED;
}
/* Separate interrupt handling path for keyboard/mouse ports. */
ret = 0; if (status & UART_MSR_DCD)
ret |= TIOCM_CAR; if (status & UART_MSR_RI)
ret |= TIOCM_RNG; if (status & UART_MSR_DSR)
ret |= TIOCM_DSR; if (status & UART_MSR_CTS)
ret |= TIOCM_CTS; return ret;
}
/* * At this point, there's no way the LSR could still be 0xff; * if it is, then bail out, because there's likely no UART * here.
*/ if (!(up->port.flags & UPF_BUGGY_UART) &&
(serial_in(up, UART_LSR) == 0xff)) {
printk("ttyS%d: LSR safety check engaged!\n", up->port.line); return -ENODEV;
}
/* * Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here.
*/
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
if (up->port.flags & UPF_FOURPORT) { unsignedint icp; /* * Enable interrupts on the AST Fourport board
*/
icp = (up->port.iobase & 0xfe0) | 0x01f;
outb_p(0x80, icp);
(void) inb_p(icp);
}
/* * And clear the interrupt registers again for luck.
*/
(void) serial_in(up, UART_LSR);
(void) serial_in(up, UART_RX);
(void) serial_in(up, UART_IIR);
(void) serial_in(up, UART_MSR);
switch (cflag & CSIZE) { case CS5:
cval = 0x00; break; case CS6:
cval = 0x01; break; case CS7:
cval = 0x02; break; default: case CS8:
cval = 0x03; break;
}
if (cflag & CSTOPB)
cval |= 0x04; if (cflag & PARENB)
cval |= UART_LCR_PARITY; if (!(cflag & PARODD))
cval |= UART_LCR_EPAR; if (cflag & CMSPAR)
cval |= UART_LCR_SPAR;
/* * Work around a bug in the Oxford Semiconductor 952 rev B * chip which causes it to seriously miscalculate baud rates * when DLL is 0.
*/ if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
up->rev == 0x5201)
quot ++;
if (flags & UART_CONFIG_TYPE) { /* * We are supposed to call autoconfig here, but this requires * splitting all the OBP probing crap from the UART probing. * We'll do it when we kill sunsu.c altogether.
*/
port->type = up->type_probed; /* XXX */
}
}
if (!(up->port.flags & UPF_BUGGY_UART)) { /* * Do a simple existence test first; if we fail this, there's * no point trying anything else. * * 0x80 is used as a nonsense port to prevent against false * positives due to ISA bus float. The assumption is that * 0x80 is a non-existent port; which should be safe since * include/asm/io.h also makes this assumption.
*/
scratch = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0); #ifdef __i386__
outb(0xff, 0x080); #endif
scratch2 = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0x0f); #ifdef __i386__
outb(0, 0x080); #endif
scratch3 = serial_in(up, UART_IER);
serial_out(up, UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0F) goto out; /* We failed; there's nothing here */
}
/* * Check to see if a UART is really there. Certain broken * internal modems based on the Rockwell chipset fail this * test, because they apparently don't implement the loopback * test mode. So this test is skipped on the COM 1 through * COM 4 ports. This *should* be safe, since no board * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope!
*/ if (!(up->port.flags & UPF_SKIP_TEST)) {
serial_out(up, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_in(up, UART_MSR) & 0xF0;
serial_out(up, UART_MCR, save_mcr); if (status1 != 0x90) goto out; /* We failed loopback test */
}
serial_out(up, UART_LCR, 0xBF); /* set up for StarTech test */
serial_out(up, UART_EFR, 0); /* EFR is the same as FCR */
serial_out(up, UART_LCR, 0);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6; switch (scratch) { case 0:
up->port.type = PORT_16450; break; case 1:
up->port.type = PORT_UNKNOWN; break; case 2:
up->port.type = PORT_16550; break; case 3:
up->port.type = PORT_16550A; break;
} if (up->port.type == PORT_16550A) { /* Check for Startech UART's */
serial_out(up, UART_LCR, UART_LCR_DLAB); if (serial_in(up, UART_EFR) == 0) {
up->port.type = PORT_16650;
} else {
serial_out(up, UART_LCR, 0xBF); if (serial_in(up, UART_EFR) == 0)
up->port.type = PORT_16650V2;
}
} if (up->port.type == PORT_16550A) { /* Check for TI 16750 */
serial_out(up, UART_LCR, save_lcr | UART_LCR_DLAB);
serial_out(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5; if (scratch == 7) { /* * If this is a 16750, and not a cheap UART * clone, then it should only go into 64 byte * mode if the UART_FCR7_64BYTE bit was set * while UART_LCR_DLAB was latched.
*/
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_out(up, UART_LCR, 0);
serial_out(up, UART_FCR,
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
scratch = serial_in(up, UART_IIR) >> 5; if (scratch == 6)
up->port.type = PORT_16750;
}
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
}
serial_out(up, UART_LCR, save_lcr); if (up->port.type == PORT_16450) {
scratch = serial_in(up, UART_SCR);
serial_out(up, UART_SCR, 0xa5);
status1 = serial_in(up, UART_SCR);
serial_out(up, UART_SCR, 0x5a);
status2 = serial_in(up, UART_SCR);
serial_out(up, UART_SCR, scratch);
/* Wait up to 10ms for the character(s) to be sent. */ do {
status = serial_in(up, UART_LSR);
if (status & UART_LSR_BI)
up->lsr_break_flag = UART_LSR_BI;
if (--tmout == 0) break;
udelay(1);
} while (!uart_lsr_tx_empty(status));
/* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) {
tmout = 1000000; while (--tmout &&
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
udelay(1);
}
}
/* * Print a string to the serial port trying not to disturb * any possible real use of the port...
*/ staticvoid sunsu_console_write(struct console *co, constchar *s, unsignedint count)
{ struct uart_sunsu_port *up = &sunsu_ports[co->index]; unsignedlong flags; unsignedint ier; int locked = 1;
/* * Finally, wait for transmitter to become empty * and restore the IER
*/
wait_for_xmitr(up);
serial_out(up, UART_IER, ier);
if (locked)
uart_port_unlock_irqrestore(&up->port, flags);
}
/* * Setup initial baud/bits/parity. We do two things here: * - construct a cflag setting for the first su_open() * - initialize the serial port * Return non-zero if we didn't find a serial port.
*/ staticint __init sunsu_console_setup(struct console *co, char *options)
{ staticstruct ktermios dummy; struct ktermios termios; struct uart_port *port;
type = su_get_type(dp); if (type == SU_PORT_PORT) { if (nr_inst >= UART_NR) return -EINVAL;
up = &sunsu_ports[nr_inst];
} else {
up = kzalloc(sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM;
}
up->port.line = nr_inst;
spin_lock_init(&up->port.lock);
up->su_type = type;
rp = &op->resource[0];
up->port.mapbase = rp->start;
up->reg_size = resource_size(rp);
up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); if (!up->port.membase) { if (type != SU_PORT_PORT)
kfree(up); return -ENOMEM;
}
staticint __init sunsu_init(void)
{ struct device_node *dp; int err; int num_uart = 0;
for_each_node_by_name(dp, "su") { if (su_get_type(dp) == SU_PORT_PORT)
num_uart++;
}
for_each_node_by_name(dp, "su_pnp") { if (su_get_type(dp) == SU_PORT_PORT)
num_uart++;
}
for_each_node_by_name(dp, "serial") { if (of_device_is_compatible(dp, "su")) { if (su_get_type(dp) == SU_PORT_PORT)
num_uart++;
}
}
for_each_node_by_type(dp, "serial") { if (of_device_is_compatible(dp, "su")) { if (su_get_type(dp) == SU_PORT_PORT)
num_uart++;
}
}
if (num_uart) {
err = sunserial_register_minors(&sunsu_reg, num_uart); if (err) return err;
}
err = platform_driver_register(&su_driver); if (err && num_uart)
sunserial_unregister_minors(&sunsu_reg, num_uart);
return err;
}
staticvoid __exit sunsu_exit(void)
{
platform_driver_unregister(&su_driver); if (sunsu_reg.nr)
sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr);
}
module_init(sunsu_init);
module_exit(sunsu_exit);
MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller");
MODULE_DESCRIPTION("Sun SU serial port driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-04-29)
¤
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.