// SPDX-License-Identifier: GPL-2.0+ /* * Driver for CPM (SCC/SMC) serial ports; core driver * * Based on arch/ppc/cpm2_io/uart.c by Dan Malek * Based on ppc8xx.c by Thomas Gleixner * Based on drivers/serial/amba.c by Russell King * * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) * Pantelis Antoniou (panto@intracom.gr) (CPM1) * * Copyright (C) 2004, 2007 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. * (C) 2005-2006 MontaVista Software, Inc. * Vitaly Bordug <vbordug@ru.mvista.com>
*/
/* Just loop through the closed BDs and copy the characters into * the buffer.
*/
bdp = pinfo->rx_cur; for (;;) { #ifdef CONFIG_CONSOLE_POLL if (unlikely(serial_polled)) {
serial_polled = 0; return;
} #endif /* get status */
status = in_be16(&bdp->cbd_sc); /* If this one is empty, return happy */ if (status & BD_SC_EMPTY) break;
/* get number of characters, and check spce in flip-buffer */
i = in_be16(&bdp->cbd_datlen);
/* If we have not enough room in tty flip buffer, then we try * later, which will be the next rx-interrupt or a timeout
*/ if (tty_buffer_request_room(tport, i) < i) {
printk(KERN_WARNING "No room in flip buffer\n"); return;
}
/* get pointer */
cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
/* loop through the buffer */ while (i-- > 0) {
ch = *cp++;
port->icount.rx++;
flg = TTY_NORMAL;
/* This BD is ready to be used again. Clear status. get next */
clrbits16(&bdp->cbd_sc, BD_SC_BR | BD_SC_FR | BD_SC_PR |
BD_SC_OV | BD_SC_ID);
setbits16(&bdp->cbd_sc, BD_SC_EMPTY);
if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
bdp = pinfo->rx_bd_base; else
bdp++;
} /* End for (;;) */
/* Write back buffer pointer */
pinfo->rx_cur = bdp;
/* overrun does not affect the current character ! */ if (status & BD_SC_OV) {
ch = 0;
flg = TTY_OVERRUN; /* We skip this buffer */ /* CHECK: Is really nothing senseful there */ /* ASSUMPTION: it contains nothing valid */
i = 0;
}
port->sysrq = 0; goto error_return;
}
/* If the port is not the console, disable Rx and Tx. */ if (!(pinfo->flags & FLAG_CONSOLE)) { /* Wait for all the BDs marked sent */ while(!cpm_uart_tx_empty(port)) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(2);
}
if (pinfo->wait_closing)
cpm_uart_wait_until_send(pinfo);
/* MAXIDL is the timeout after which a receive buffer is closed * when not full if no more characters are received. * We calculate it from the baudrate so that the duration is * always the same at standard rates: about 4ms.
*/
maxidl = baud / 2400; if (maxidl < 1)
maxidl = 1; if (maxidl > 0x10)
maxidl = 0x10;
cval = 0;
scval = 0;
if (termios->c_cflag & CSTOPB) {
cval |= SMCMR_SL; /* Two stops */
scval |= SCU_PSMR_SL;
}
/* * Update the timeout
*/
uart_update_timeout(port, termios->c_cflag, baud);
/* * Set up parity check flag
*/
port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); if (termios->c_iflag & INPCK)
port->read_status_mask |= BD_SC_FR | BD_SC_PR; if ((termios->c_iflag & BRKINT) || (termios->c_iflag & PARMRK))
port->read_status_mask |= BD_SC_BR;
/* * Characters to ignore
*/
port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BD_SC_PR | BD_SC_FR; if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= BD_SC_BR; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support).
*/ if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= BD_SC_OV;
} /* * !!! ignore all characters if CREAD is not set
*/ if ((termios->c_cflag & CREAD) == 0)
port->read_status_mask &= ~BD_SC_EMPTY;
uart_port_lock_irqsave(port, &flags);
if (IS_SMC(pinfo)) { unsignedint bits = tty_get_frame_size(termios->c_cflag);
/* * MRBLR can be changed while an SMC/SCC is operating only * if it is done in a single bus cycle with one 16-bit move * (not two 8-bit bus cycles back-to-back). This occurs when * the cp shifts control to the next RxBD, so the change does * not take effect immediately. To guarantee the exact RxBD * on which the change occurs, change MRBLR only while the * SMC/SCC receiver is disabled.
*/
out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
out_be16(&pinfo->smcup->smc_maxidl, maxidl);
/* Set the mode register. We want to keep a copy of the * enables, because we want to put them back if they were * present.
*/
prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN); /* Output in *one* operation, so we don't interrupt RX/TX if they * were already enabled. * Character length programmed into the register is frame bits minus 1.
*/
out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits - 1) | cval |
SMCMR_SM_UART | prev_mode);
} else { unsignedint bits = tty_get_char_size(termios->c_cflag);
/* Set the physical address of the host memory * buffers in the buffer descriptors, and the * virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr;
bdp = pinfo->rx_cur = pinfo->rx_bd_base; for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
out_be16(&bdp->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT);
mem_addr += pinfo->rx_fifosize;
}
/* Set the physical address of the host memory * buffers in the buffer descriptors, and the * virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
bdp = pinfo->tx_cur = pinfo->tx_bd_base; for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
out_be16(&bdp->cbd_sc, BD_SC_INTRPT);
mem_addr += pinfo->tx_fifosize;
}
/* * In case SMC is being relocated...
*/
out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase));
out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase));
out_be32(&up->smc_rstate, 0);
out_be32(&up->smc_tstate, 0);
out_be16(&up->smc_brkcr, 1); /* number of break chars */
out_be16(&up->smc_brkec, 0);
/* Set up the uart parameters in the * parameter ram.
*/
out_8(&up->smc_rfcr, CPMFCR_GBL | CPMFCR_EB);
out_8(&up->smc_tfcr, CPMFCR_GBL | CPMFCR_EB);
/* Using idle character time requires some additional tuning. */
out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
out_be16(&up->smc_maxidl, 0x10);
out_be16(&up->smc_brklen, 0);
out_be16(&up->smc_brkec, 0);
out_be16(&up->smc_brkcr, 1);
/* Set UART mode, 8 bit, no parity, one stop. * Enable receive and transmit.
*/
out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART);
/* Enable only rx interrupts clear all pending events. */
out_8(&sp->smc_smcm, 0);
out_8(&sp->smc_smce, 0xff);
/* * Allocate DP-Ram and memory buffers. We need to allocate a transmit and * receive buffer descriptors from dual port ram, and a character * buffer area from host mem. If we are allocating for the console we need * to do it from bootmem
*/ staticint cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsignedint is_con)
{ int dpmemsz, memsz;
u8 __iomem *dp_mem; unsignedlong dp_offset;
u8 *mem_addr;
dma_addr_t dma_addr = 0;
/* * Initialize port. This is called from early_console stuff * so we have to be careful here !
*/ staticint cpm_uart_request_port(struct uart_port *port)
{ struct uart_cpm_port *pinfo =
container_of(port, struct uart_cpm_port, port); int ret;
#ifdefined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE) /* * Write a string to the serial port * Note that this is called with interrupts already disabled
*/ staticvoid cpm_uart_early_write(struct uart_cpm_port *pinfo, constchar *string, u_int count, bool handle_linefeed)
{ unsignedint i;
cbd_t __iomem *bdp, *bdbase; unsignedchar *cpm_outp_addr;
/* Get the address of the host memory buffer.
*/
bdp = pinfo->tx_cur;
bdbase = pinfo->tx_bd_base;
/* * Now, do each character. This is not as bad as it looks * since this is a holding FIFO and not a transmitting FIFO. * We could add the complexity of filling the entire transmit * buffer, but we would just wait longer between accesses......
*/ for (i = 0; i < count; i++, string++) { /* Wait for transmitter fifo to empty. * Ready indicates output is ready, and xmt is doing * that, not that it is ready for us to send.
*/ while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0)
;
/* Send the character out. * If the buffer address is in the CPM DPRAM, don't * convert it.
*/
cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr),
pinfo);
*cpm_outp_addr = *string;
/* Don't remap parameter RAM if it has already been initialized * during console setup.
*/ if (IS_SMC(port) && port->smcup) return port->smcup; elseif (!IS_SMC(port) && port->sccup) return port->sccup;
if (of_address_to_resource(np, 1, &res)) return NULL;
len = resource_size(&res);
pram = ioremap(res.start, len); if (!pram) return NULL;
if (!IS_ENABLED(CONFIG_CPM2) || !IS_SMC(port)) return pram;
if (len != 2) {
pr_warn("cpm_uart[%d]: device tree references " "SMC pram, using boot loader/wrapper pram mapping. " "Please fix your device tree to reference the pram " "base register instead.\n",
port->port.line); return pram;
}
for (i = 0; i < NUM_GPIOS; i++) { struct gpio_desc *gpiod;
pinfo->gpios[i] = NULL;
gpiod = devm_gpiod_get_index_optional(dev, NULL, i, GPIOD_ASIS);
if (IS_ERR(gpiod)) {
ret = PTR_ERR(gpiod); goto out_pram;
}
if (gpiod) { if (i == GPIO_RTS || i == GPIO_DTR)
ret = gpiod_direction_output(gpiod, 0); else
ret = gpiod_direction_input(gpiod); if (ret) {
pr_err("can't set direction for gpio #%d: %d\n",
i, ret); continue;
}
pinfo->gpios[i] = gpiod;
}
}
#ifdef CONFIG_SERIAL_CPM_CONSOLE /* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * Note that this is called with interrupts already disabled
*/ staticvoid cpm_uart_console_write(struct console *co, constchar *s,
u_int count)
{ struct uart_cpm_port *pinfo = &cpm_uart_ports[co->index]; unsignedlong flags;
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.