// SPDX-License-Identifier: GPL-2.0 /* * ARC On-Chip(fpga) UART Driver * * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com) * * vineetg: July 10th 2012 * -Decoupled the driver from arch/arc * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c) * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx) * * Vineetg: Aug 21st 2010 * -Is uart_tx_stopped() not done in tty write path as it has already been * taken care of, in serial core * * Vineetg: Aug 18th 2010 * -New Serial Core based ARC UART driver * -Derived largely from blackfin driver albiet with some major tweaks * * TODO: * -check if sysreq works
*/
/* * UART Register set (this is not a Standards Compliant IP) * Also each reg is Word aligned, but only 8 bits wide
*/ #define R_ID0 0 #define R_ID1 4 #define R_ID2 8 #define R_ID3 12 #define R_DATA 16 #define R_STS 20 #define R_BAUDL 24 #define R_BAUDH 28
staticvoid arc_serial_stop_tx(struct uart_port *port)
{ while (!(UART_GET_STATUS(port) & TXEMPTY))
cpu_relax();
UART_TX_IRQ_DISABLE(port);
}
/* * Return TIOCSER_TEMT when transmitter is not busy.
*/ staticunsignedint arc_serial_tx_empty(struct uart_port *port)
{ unsignedint stat;
stat = UART_GET_STATUS(port); if (stat & TXEMPTY) return TIOCSER_TEMT;
return 0;
}
/* * Driver internal routine, used by both tty(serial core) as well as tx-isr * -Called under spinlock in either cases * -also tty->flow.stopped has already been checked * = by uart_start( ) before calling us * = tx_ist checks that too before calling
*/ staticvoid arc_serial_tx_chars(struct uart_port *port)
{ struct tty_port *tport = &port->state->port; int sent = 0; unsignedchar ch;
if (unlikely(port->x_char)) {
UART_SET_DATA(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
sent = 1;
} elseif (uart_fifo_get(port, &ch)) { while (!(UART_GET_STATUS(port) & TXEMPTY))
cpu_relax();
UART_SET_DATA(port, ch);
sent = 1;
}
/* * If num chars in xmit buffer are too few, ask tty layer for more. * By Hard ISR to schedule processing in software interrupt part
*/ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (sent)
UART_TX_IRQ_ENABLE(port);
}
/* * port is locked and interrupts are disabled * uart_start( ) calls us under the port spinlock irqsave
*/ staticvoid arc_serial_start_tx(struct uart_port *port)
{
arc_serial_tx_chars(port);
}
staticvoid arc_serial_rx_chars(struct uart_port *port, unsignedint status)
{ /* * UART has 4 deep RX-FIFO. Driver's recongnition of this fact * is very subtle. Here's how ... * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available, * driver reads the DATA Reg and keeps doing that in a loop, until * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt, * before RX-EMPTY=0, implies some sort of buffering going on in the * controller, which is indeed the Rx-FIFO.
*/ do {
u8 ch, flg = TTY_NORMAL;
/* * This could be an Rx Intr for err (no data), * so check err and clear that Intr first
*/ if (status & RXOERR) {
port->icount.overrun++;
flg = TTY_OVERRUN;
UART_CLR_STATUS(port, RXOERR);
}
if (!(uart_handle_sysrq_char(port, ch)))
uart_insert_char(port, status, RXOERR, ch, flg);
tty_flip_buffer_push(&port->state->port);
} while (!((status = UART_GET_STATUS(port)) & RXEMPTY));
}
/* * A note on the Interrupt handling state machine of this driver * * kernel printk writes funnel thru the console driver framework and in order * to keep things simple as well as efficient, it writes to UART in polled * mode, in one shot, and exits. * * OTOH, Userland output (via tty layer), uses interrupt based writes as there * can be undeterministic delay between char writes. * * Thus Rx-interrupts are always enabled, while tx-interrupts are by default * disabled. * * When tty has some data to send out, serial core calls driver's start_tx * which * -checks-if-tty-buffer-has-char-to-send * -writes-data-to-uart * -enable-tx-intr * * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt. * The first thing Tx ISR does is disable further Tx interrupts (as this could * be the last char to send, before settling down into the quiet polled mode). * It then calls the exact routine used by tty layer write to send out any * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case * of no data, it remains disabled. * This is how the transmit state machine is dynamically switched on/off
*/
/* * Single IRQ for both Rx (data available) Tx (room available) Interrupt * notifications from the UART Controller. * To demultiplex between the two, we check the relevant bits
*/ if (status & RXIENB) {
/* already in ISR, no need of xx_irqsave */
uart_port_lock(port);
arc_serial_rx_chars(port, status);
uart_port_unlock(port);
}
if ((status & TXIENB) && (status & TXEMPTY)) {
/* Unconditionally disable further Tx-Interrupts. * will be enabled by tx_chars() if needed.
*/
UART_TX_IRQ_DISABLE(port);
uart_port_lock(port);
if (!uart_tx_stopped(port))
arc_serial_tx_chars(port);
uart_port_unlock(port);
}
return IRQ_HANDLED;
}
staticunsignedint arc_serial_get_mctrl(struct uart_port *port)
{ /* * Pretend we have a Modem status reg and following bits are * always set, to satify the serial core state machine * (DSR) Data Set Ready * (CTS) Clear To Send * (CAR) Carrier Detect
*/ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
/* * Use the generic handler so that any specially encoded baud rates * such as SPD_xx flags or "%B0" can be handled * Max Baud I suppose will not be more than current 115K * 4 * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1) * spread over two 8-bit registers
*/
baud = uart_get_baud_rate(port, new, old, 0, 460800);
staticint arc_serial_console_setup(struct console *co, char *options)
{ struct uart_port *port; int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n';
if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS) return -ENODEV;
/* * The uart port backing the console (e.g. ttyARC1) might not have been * init yet. If so, defer the console setup to after the port.
*/
port = &arc_uart_ports[co->index].port; if (!port->membase) return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
/* * Serial core will call port->ops->set_termios( ) * which will set the baud reg
*/ return uart_set_options(port, co, baud, parity, bits, flow);
}
if (of_property_read_u32(np, "current-speed", &val)) {
dev_err(&pdev->dev, "current-speed property NOT set\n"); return -EINVAL;
}
uart->baud = val;
port->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(port->membase)) { /* No point of dev_err since UART itself is hosed here */ return PTR_ERR(port->membase);
}
/* * uart_insert_char( ) uses it in decideding whether to ignore a * char or not. Explicitly setting it here, removes the subtelty
*/
port->ignore_status_mask = 0;
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.