// SPDX-License-Identifier: GPL-2.0+ /* * Driver for OMAP-UART controller. * Based on drivers/serial/8250.c * * Copyright (C) 2010 Texas Instruments. * * Authors: * Govindraj R <govindraj.raja@ti.com> * Thara Gopinath <thara@ti.com> * * Note: This driver is made separate from 8250 driver as we cannot * over load 8250 driver with omap platform specific configuration for * features like DMA, it makes easier to implement features like DMA and * hardware flow control and software flow control configuration with * this driver as required for the omap-platform.
*/
/* WER = 0x7F * Enable module level wakeup in WER reg
*/ #define OMAP_UART_WER_MOD_WKUP 0x7F
/* Enable XON/XOFF flow control on output */ #define OMAP_UART_SW_TX 0x08
/* Enable XON/XOFF flow control on input */ #define OMAP_UART_SW_RX 0x02
#define OMAP_UART_SW_CLR 0xF0
#define OMAP_UART_TCR_TRIG 0x0F
struct uart_omap_dma {
u8 uart_dma_tx;
u8 uart_dma_rx; int rx_dma_channel; int tx_dma_channel;
dma_addr_t rx_buf_dma_phys;
dma_addr_t tx_buf_dma_phys; unsignedint uart_base; /* * Buffer for rx dma. It is not required for tx because the buffer * comes from port structure.
*/ unsignedchar *rx_buf; unsignedint prev_rx_dma_pos; int tx_buf_size; int tx_dma_used; int rx_dma_used;
spinlock_t tx_lock;
spinlock_t rx_lock; /* timer to poll activity on rx dma */ struct timer_list rx_timer; unsignedint rx_buf_size; unsignedint rx_poll_rate; unsignedint rx_timeout;
};
int use_dma; /* * Some bits in registers are cleared on a read, so they must * be saved whenever the register is read, but the bits will not * be immediately processed.
*/ unsignedint lsr_break_flag; unsignedchar msr_saved_flags; char name[20]; unsignedlong port_activity; int context_loss_cnt;
u32 errata;
u32 features;
if (!pdata || !pdata->get_context_loss_count) return -EINVAL;
return pdata->get_context_loss_count(up->dev);
}
/* REVISIT: Remove this when omap3 boots in device tree only mode */ staticvoid serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
{ struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
/* * Calculate the absolute difference between the desired and actual baud * rate for the given mode.
*/ staticinlineint calculate_baud_abs_diff(struct uart_port *port, unsignedint baud, unsignedint mode)
{ unsignedint n = port->uartclk / (mode * baud);
/* * serial_omap_baud_is_mode16 - check if baud rate is MODE16X * @port: uart port info * @baud: baudrate for which mode needs to be determined * * Returns true if baud rate is MODE16X and false if MODE13X * Original table in OMAP TRM named "UART Mode Baud Rates, Divisor Values, * and Error Rates" determines modes not for all common baud rates. * E.g. for 1000000 baud rate mode must be 16x, but according to that * table it's determined as 13x.
*/ staticbool
serial_omap_baud_is_mode16(struct uart_port *port, unsignedint baud)
{ int abs_diff_13 = calculate_baud_abs_diff(port, baud, 13); int abs_diff_16 = calculate_baud_abs_diff(port, baud, 16);
return (abs_diff_13 >= abs_diff_16);
}
/* * serial_omap_get_divisor - calculate divisor value * @port: uart port info * @baud: baudrate for which divisor needs to be calculated.
*/ staticunsignedint
serial_omap_get_divisor(struct uart_port *port, unsignedint baud)
{ unsignedint mode;
/* Handle RS-485 */ if (port->rs485.flags & SER_RS485_ENABLED) { if (up->scr & OMAP_UART_SCR_TX_EMPTY) { /* THR interrupt is fired when both TX FIFO and TX * shift register are empty. This means there's nothing * left to transmit now, so make sure the THR interrupt * is fired when TX FIFO is below the trigger level, * disable THR interrupts and toggle the RS-485 GPIO * data direction pin if needed.
*/
up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
serial_out(up, UART_OMAP_SCR, up->scr);
res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ?
1 : 0; if (gpiod_get_value(up->rts_gpiod) != res) { if (port->rs485.delay_rts_after_send > 0)
mdelay(
port->rs485.delay_rts_after_send);
gpiod_set_value(up->rts_gpiod, res);
}
} else { /* We're asked to stop, but there's still stuff in the * UART FIFO, so make sure the THR interrupt is fired * when both TX FIFO and TX shift register are empty. * The next THR interrupt (if no transmission is started * in the meantime) will indicate the end of a * transmission. Therefore we _don't_ disable THR * interrupts in this situation.
*/
up->scr |= OMAP_UART_SCR_TX_EMPTY;
serial_out(up, UART_OMAP_SCR, up->scr); return;
}
}
/* * Read one data character out to avoid stalling the receiver according * to the table 23-246 of the omap4 TRM.
*/ if (likely(lsr & UART_LSR_DR)) {
serial_in(up, UART_RX); if ((up->port.rs485.flags & SER_RS485_ENABLED) &&
!(up->port.rs485.flags & SER_RS485_RX_DURING_TX) &&
up->rs485_tx_filter_count)
up->rs485_tx_filter_count--;
}
up->port.icount.rx++;
flag = TTY_NORMAL;
if (lsr & UART_LSR_BI) {
flag = TTY_BREAK;
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
up->port.icount.brk++; /* * 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)) return;
}
if (lsr & UART_LSR_PE) {
flag = TTY_PARITY;
up->port.icount.parity++;
}
if (lsr & UART_LSR_FE) {
flag = TTY_FRAME;
up->port.icount.frame++;
}
if (lsr & UART_LSR_OE)
up->port.icount.overrun++;
#ifdef CONFIG_SERIAL_OMAP_CONSOLE if (up->port.line == up->port.cons->index) { /* Recover the break flag from console xmit */
lsr |= up->lsr_break_flag;
} #endif
uart_insert_char(&up->port, lsr, UART_LSR_OE, 0, flag);
}
/** * serial_omap_irq() - This handles the interrupt from one port * @irq: uart port irq number * @dev_id: uart port info
*/ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
{ struct uart_omap_port *up = dev_id; unsignedint iir, lsr; unsignedint type;
irqreturn_t ret = IRQ_NONE; int max_count = 256;
uart_port_lock(&up->port);
do {
iir = serial_in(up, UART_IIR); if (iir & UART_IIR_NO_INT) break;
ret = IRQ_HANDLED;
lsr = serial_in(up, UART_LSR);
/* extract IRQ type from IIR register */
type = iir & 0x3e;
switch (type) { case UART_IIR_MSI:
check_modem_status(up); break; case UART_IIR_THRI:
transmit_chars(up, lsr); break; case UART_IIR_RX_TIMEOUT: case UART_IIR_RDI:
serial_omap_rdi(up, lsr); break; case UART_IIR_RLSI:
serial_omap_rlsi(up, lsr); break; case UART_IIR_CTS_RTS_DSR: /* simply try again */ break; case UART_IIR_XOFF: default: break;
}
} while (max_count--);
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;
}
pm_runtime_get_sync(up->dev); /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios())
*/
serial_omap_clear_fifos(up);
/* * Now, initialize the UART
*/
serial_out(up, UART_LCR, UART_LCR_WLEN8);
uart_port_lock_irqsave(&up->port, &flags); /* * Most PC uarts need OUT2 raised to enable interrupts.
*/
up->port.mctrl |= TIOCM_OUT2;
serial_omap_set_mctrl(&up->port, up->port.mctrl);
uart_port_unlock_irqrestore(&up->port, flags);
up->msr_saved_flags = 0; /* * 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);
/* Enable module level wake up */
up->wer = OMAP_UART_WER_MOD_WKUP; if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP)
up->wer |= OMAP_UART_TX_WAKEUP_EN;
/* FCR can be changed only when the * baud clock is not running * DLL_REG and DLH_REG set to 0.
*/
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
serial_out(up, UART_DLL, 0);
serial_out(up, UART_DLM, 0);
serial_out(up, UART_LCR, 0);
up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; /* * NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK * sets Enables the granularity of 1 for TRIGGER RX * level. Along with setting RX FIFO trigger level * to 1 (as noted below, 16 characters) and TLR[3:0] * to zero this will result RX FIFO threshold level * to 1 character, instead of 16 as noted in comment * below.
*/
/* Set receive FIFO threshold to 16 characters and * transmit FIFO threshold to 32 spaces
*/
up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
up->fcr |= UART_FCR6_R_TRIGGER_16 | UART_FCR6_T_TRIGGER_24 |
UART_FCR_ENABLE_FIFO;
/* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
serial_out(up, UART_MCR, up->mcr);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, up->efr);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
/* Protocol, Baud Rate, and Interrupt Settings */
if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
serial_omap_mdr1_errataset(up, up->mdr1); else
serial_out(up, UART_OMAP_MDR1, up->mdr1);
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
up->efr |= UART_EFR_CTS;
} else { /* Disable AUTORTS and AUTOCTS */
up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
}
if (up->port.flags & UPF_SOFT_FLOW) { /* clear SW control mode bits */
up->efr &= OMAP_UART_SW_CLR;
/* * IXON Flag: * Enable XON/XOFF flow control on input. * Receiver compares XON1, XOFF1.
*/ if (termios->c_iflag & IXON)
up->efr |= OMAP_UART_SW_RX;
/* * IXOFF Flag: * Enable XON/XOFF flow control on output. * Transmit XON1, XOFF1
*/ if (termios->c_iflag & IXOFF) {
up->port.status |= UPSTAT_AUTOXOFF;
up->efr |= OMAP_UART_SW_TX;
}
/* * IXANY Flag: * Enable any character to restart output. * Operation resumes after receiving any * character after recognition of the XOFF character
*/ if (termios->c_iflag & IXANY)
up->mcr |= UART_MCR_XONANY; else
up->mcr &= ~UART_MCR_XONANY;
}
serial_out(up, UART_MCR, up->mcr);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, up->efr);
serial_out(up, UART_LCR, up->lcr);
/* 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) { for (tmout = 1000000; tmout; tmout--) { unsignedint msr = serial_in(up, UART_MSR);
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; if (msr & UART_MSR_CTS) break;
/* * Finally, wait for transmitter to become empty * and restore the IER
*/
wait_for_xmitr(up);
serial_out(up, UART_IER, ier); /* * The receive handling will happen properly because the * receive ready bit will still be set; it is not cleared * on read. However, modem control will not, we must * call it if we have saved something in the saved flags * while processing with interrupts off.
*/ if (up->msr_saved_flags)
check_modem_status(up);
if (locked)
uart_port_unlock_irqrestore(&up->port, flags);
}
staticint __init
serial_omap_console_setup(struct console *co, char *options)
{ struct uart_omap_port *up; int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n';
if (serial_omap_console_ports[co->index] == NULL) return -ENODEV;
up = serial_omap_console_ports[co->index];
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
/* If RS-485 is disabled, make sure the THR interrupt is fired when * TX FIFO is below the trigger level.
*/ if (!(rs485->flags & SER_RS485_ENABLED) &&
(up->scr & OMAP_UART_SCR_TX_EMPTY)) {
up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
serial_out(up, UART_OMAP_SCR, up->scr);
}
if (pdev->dev.of_node)
ret = of_alias_get_id(pdev->dev.of_node, "serial"); else
ret = pdev->id;
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
ret); goto err_port_line;
}
up->port.line = ret;
if (up->port.line >= OMAP_MAX_HSUART_PORTS) {
dev_err(&pdev->dev, "uart ID %d > MAX %d.\n", up->port.line,
OMAP_MAX_HSUART_PORTS);
ret = -ENXIO; goto err_port_line;
}
up->wakeirq = wakeirq; if (!up->wakeirq)
dev_info(up->port.dev, "no wakeirq for uart%d\n",
up->port.line);
/* * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460) * The access to uart register after MDR1 Access * causes UART to corrupt data. * * Need a delay = * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS) * give 10 times as much
*/ staticvoid serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1)
{
u8 timeout = 255;
serial_out(up, UART_OMAP_MDR1, mdr1);
udelay(2);
serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT |
UART_FCR_CLEAR_RCVR); /* * Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and * TX_FIFO_E bit is 1.
*/ while (UART_LSR_THRE != (serial_in(up, UART_LSR) &
(UART_LSR_THRE | UART_LSR_DR))) {
timeout--; if (!timeout) { /* Should *never* happen. we warn and carry on */
dev_crit(up->dev, "Errata i202: timedout %x\n",
serial_in(up, UART_LSR)); break;
}
udelay(1);
}
}
/* * When using 'no_console_suspend', the console UART must not be * suspended. Since driver suspend is managed by runtime suspend, * preventing runtime suspend (by returning error) will keep device * active during suspend.
*/ if (up->is_suspending && !console_suspend_enabled &&
uart_console(&up->port)) return -EBUSY;
staticint __init serial_omap_init(void)
{ int ret;
ret = uart_register_driver(&serial_omap_reg); if (ret != 0) return ret;
ret = platform_driver_register(&serial_omap_driver); if (ret != 0)
uart_unregister_driver(&serial_omap_reg); return ret;
}
MODULE_DESCRIPTION("OMAP High Speed UART driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Texas Instruments Inc");
Messung V0.5
¤ 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.0.16Bemerkung:
(vorverarbeitet)
¤
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.