// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SA11x0 serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * Copyright (C) 2000 Deep Blue Solutions Ltd.
*/
/* * This is the size of our serial port register set.
*/ #define UART_PORT_SIZE 0x24
/* * This determines how often we check the modem status signals * for any change. They generally aren't connected to an IRQ * so we have to poll them. We also check immediately before * filling the TX fifo incase CTS has been dropped.
*/ #define MCTRL_TIMEOUT (250*HZ/1000)
/* * Handle any change of modem status signal since we were last called.
*/ staticvoid sa1100_mctrl_check(struct sa1100_port *sport)
{ unsignedint status, changed;
status = sport->port.ops->get_mctrl(&sport->port);
changed = status ^ sport->old_status;
if (changed == 0) return;
sport->old_status = status;
if (changed & TIOCM_RI)
sport->port.icount.rng++; if (changed & TIOCM_DSR)
sport->port.icount.dsr++; if (changed & TIOCM_CAR)
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); if (changed & TIOCM_CTS)
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
/* * Set the modem control timer to fire immediately.
*/ staticvoid sa1100_enable_ms(struct uart_port *port)
{ struct sa1100_port *sport =
container_of(port, struct sa1100_port, port);
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
UTSR0_TO_SM(UART_GET_UTSR0(sport)); while (status & UTSR1_TO_SM(UTSR1_RNE)) {
ch = UART_GET_CHAR(sport);
sport->port.icount.rx++;
flg = TTY_NORMAL;
/* * note that the error handling code is * out of the main execution path
*/ if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) { if (status & UTSR1_TO_SM(UTSR1_PRE))
sport->port.icount.parity++; elseif (status & UTSR1_TO_SM(UTSR1_FRE))
sport->port.icount.frame++; if (status & UTSR1_TO_SM(UTSR1_ROR))
sport->port.icount.overrun++;
uart_port_lock(&sport->port);
status = UART_GET_UTSR0(sport);
status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; do { if (status & (UTSR0_RFS | UTSR0_RID)) { /* Clear the receiver idle bit, if set */ if (status & UTSR0_RID)
UART_PUT_UTSR0(sport, UTSR0_RID);
sa1100_rx_chars(sport);
}
/* Clear the relevant break bits */ if (status & (UTSR0_RBB | UTSR0_REB))
UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB));
if (status & UTSR0_RBB)
sport->port.icount.brk++;
if (status & UTSR0_REB)
uart_handle_break(&sport->port);
if (status & UTSR0_TFS)
sa1100_tx_chars(sport); if (pass_counter++ > SA1100_ISR_PASS_LIMIT) break;
status = UART_GET_UTSR0(sport);
status &= SM_TO_UTSR0(sport->port.read_status_mask) |
~UTSR0_TFS;
} while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));
uart_port_unlock(&sport->port);
return IRQ_HANDLED;
}
/* * Return TIOCSER_TEMT when transmitter is not busy.
*/ staticunsignedint sa1100_tx_empty(struct uart_port *port)
{ struct sa1100_port *sport =
container_of(port, struct sa1100_port, port);
/* * Verify the new serial_struct (for TIOCSSERIAL). * The only change we allow are to the flags and type, and * even then only between PORT_SA1100 and PORT_UNKNOWN
*/ staticint
sa1100_verify_port(struct uart_port *port, struct serial_struct *ser)
{ struct sa1100_port *sport =
container_of(port, struct sa1100_port, port); int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
ret = -EINVAL; if (sport->port.irq != ser->irq)
ret = -EINVAL; if (ser->io_type != SERIAL_IO_MEM)
ret = -EINVAL; if (sport->port.uartclk / 16 != ser->baud_base)
ret = -EINVAL; if ((void *)sport->port.mapbase != ser->iomem_base)
ret = -EINVAL; if (sport->port.iobase != ser->port)
ret = -EINVAL; if (ser->hub6 != 0)
ret = -EINVAL; return ret;
}
/* * Setup the SA1100 serial ports. Note that we don't include the IrDA * port here since we have our own SIR/FIR driver (see drivers/net/irda) * * Note also that we support "console=ttySAx" where "x" is either 0 or 1. * Which serial port this ends up being depends on the machine you're * running this kernel on. I'm not convinced that this is a good idea, * but that's the way it traditionally works. * * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer * used here.
*/ staticvoid __init sa1100_init_ports(void)
{ staticint first = 1; int i;
if (!first) return;
first = 0;
for (i = 0; i < NR_PORTS; i++) {
sa1100_ports[i].port.uartclk = 3686400;
sa1100_ports[i].port.ops = &sa1100_pops;
sa1100_ports[i].port.fifosize = 8;
sa1100_ports[i].port.line = i;
sa1100_ports[i].port.iotype = UPIO_MEM;
timer_setup(&sa1100_ports[i].timer, sa1100_timeout, 0);
}
/* * make transmit lines outputs, so that when the port * is closed, the output is in the MARK state.
*/
PPDR |= PPC_TXD1 | PPC_TXD3;
PPSR |= PPC_TXD1 | PPC_TXD3;
}
void sa1100_register_uart_fns(struct sa1100_port_fns *fns)
{ if (fns->get_mctrl)
sa1100_pops.get_mctrl = fns->get_mctrl; if (fns->set_mctrl)
sa1100_pops.set_mctrl = fns->set_mctrl;
sa1100_pops.pm = fns->pm; /* * FIXME: fns->set_wake is unused - this should be called from * the suspend() callback if device_may_wakeup(dev)) is set.
*/
}
void __init sa1100_register_uart(int idx, int port)
{ if (idx >= NR_PORTS) {
printk(KERN_ERR "%s: bad index number %d\n", __func__, idx); return;
}
/* * Finally, wait for transmitter to become empty * and restore UTCR3
*/ do {
status = UART_GET_UTSR1(sport);
} while (status & UTSR1_TBY);
UART_PUT_UTCR3(sport, old_utcr3);
}
/* * If the port was already initialised (eg, by a boot loader), * try to determine the current setup.
*/ staticvoid __init
sa1100_console_get_options(struct sa1100_port *sport, int *baud, int *parity, int *bits)
{ unsignedint utcr3;
utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { /* ok, the port was enabled */ unsignedint utcr0, quot;
utcr0 = UART_GET_UTCR0(sport);
*parity = 'n'; if (utcr0 & UTCR0_PE) { if (utcr0 & UTCR0_OES)
*parity = 'e'; else
*parity = 'o';
}
staticint __init
sa1100_console_setup(struct console *co, char *options)
{ struct sa1100_port *sport; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n';
/* * 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 == -1 || co->index >= NR_PORTS)
co->index = 0;
sport = &sa1100_ports[co->index];
// mctrl_gpio_init() requires that the GPIO driver supports interrupts, // but we need to support GPIO drivers for hardware that has no such // interrupts. Use mctrl_gpio_init_noauto() instead.
sport->gpios = mctrl_gpio_init_noauto(sport->port.dev, 0); if (IS_ERR(sport->gpios)) { int err = PTR_ERR(sport->gpios);
dev_err(sport->port.dev, "failed to get mctrl gpios: %d\n",
err);
staticint __init sa1100_serial_init(void)
{ int ret;
printk(KERN_INFO "Serial: SA11x0 driver\n");
sa1100_init_ports();
ret = uart_register_driver(&sa1100_reg); if (ret == 0) {
ret = platform_driver_register(&sa11x0_serial_driver); if (ret)
uart_unregister_driver(&sa1100_reg);
} return ret;
}
MODULE_AUTHOR("Deep Blue Solutions Ltd");
MODULE_DESCRIPTION("SA1100 generic serial port driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_SA1100_MAJOR);
MODULE_ALIAS("platform:sa11x0-uart");
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.