// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Atmel AT91 Serial ports * Copyright (C) 2003 Rick Bronson * * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * DMA support added by Chip Coldwell.
*/ #include <linux/circ_buf.h> #include <linux/tty.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/serial.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/console.h> #include <linux/sysrq.h> #include <linux/tty_flip.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/atmel_pdc.h> #include <linux/uaccess.h> #include <linux/platform_data/atmel.h> #include <linux/timer.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/suspend.h> #include <linux/mm.h> #include <linux/io.h>
#include <asm/div64.h> #include <asm/ioctls.h>
#define PDC_BUFFER_SIZE 512 /* Revisit: We should calculate this based on the actual port settings */ #define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */
/* The minium number of data FIFOs should be able to contain */ #define ATMEL_MIN_FIFO_SIZE 8 /* * These two offsets are substracted from the RX FIFO size to define the RTS * high and low thresholds
*/ #define ATMEL_RTS_HIGH_OFFSET 16 #define ATMEL_RTS_LOW_OFFSET 20
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we * should coexist with the 8250 driver, such as if we have an external 16C550
* UART. */ #define SERIAL_ATMEL_MAJOR 204 #define MINOR_START 154 #define ATMEL_DEVICENAME "ttyAT"
#else
/* Use device name ttyS, major 4, minor 64-68. This is the usual serial port
* name, but it is legally reserved for the 8250 driver. */ #define SERIAL_ATMEL_MAJOR TTY_MAJOR #define MINOR_START 64 #define ATMEL_DEVICENAME "ttyS"
/* * Be careful, the real size of the ring buffer is * sizeof(atmel_uart_char) * ATMEL_SERIAL_RINGSIZE. It means that ring buffer * can contain up to 1024 characters in PIO mode and up to 4096 characters in * DMA mode.
*/ #define ATMEL_SERIAL_RINGSIZE 1024 #define ATMEL_SERIAL_RX_SIZE array_size(sizeof(struct atmel_uart_char), \
ATMEL_SERIAL_RINGSIZE)
/* * at91: 6 USARTs and one DBGU port (SAM9260) * samx7: 3 USARTs and 5 UARTs
*/ #define ATMEL_MAX_UART 8
/* * We wrap our port structure around the generic uart_port.
*/ struct atmel_uart_port { struct uart_port uart; /* uart */ struct clk *clk; /* uart clock */ struct clk *gclk; /* uart generic clock */ int may_wakeup; /* cached value of device_may_wakeup for times we need to disable it */
u32 backup_imr; /* IMR saved during suspend */ int break_active; /* break being received */
/* select mck clock, and output */
mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; /* set parity for normal/inverse mode + max iterations */
mode |= ATMEL_US_PAR_EVEN | ATMEL_US_NBSTOP_1 | ATMEL_US_MAX_ITER(3);
cd = atmel_calc_cd(port, iso7816conf);
fidi = atmel_calc_fidi(port, iso7816conf); if (fidi == 0) {
dev_warn(port->dev, "ISO7816 fidi = 0, Generator generates no signal\n");
} elseif (fidi < atmel_port->fidi_min
|| fidi > atmel_port->fidi_max) {
dev_err(port->dev, "ISO7816 fidi = %u, value not supported\n", fidi);
memset(iso7816conf, 0, sizeof(struct serial_iso7816));
ret = -EINVAL; goto err_out;
}
if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) { /* port not yet in iso7816 mode: store configuration */
atmel_port->backup_mode = atmel_uart_readl(port, ATMEL_US_MR);
atmel_port->backup_brgr = atmel_uart_readl(port, ATMEL_US_BRGR);
}
/* * Set state of the modem control output lines
*/ staticvoid atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{ unsignedint control = 0; unsignedint mode = atmel_uart_readl(port, ATMEL_US_MR); unsignedint rts_paused, rts_ready; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* override mode to RS485 if needed, otherwise keep the current mode */ if (port->rs485.flags & SER_RS485_ENABLED) {
atmel_uart_writel(port, ATMEL_US_TTGR,
port->rs485.delay_rts_after_send);
mode &= ~ATMEL_US_USMODE;
mode |= ATMEL_US_USMODE_RS485;
}
/* set the RTS line state according to the mode */ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) { /* force RTS line to high level */
rts_paused = ATMEL_US_RTSEN;
/* give the control of the RTS line back to the hardware */
rts_ready = ATMEL_US_RTSDIS;
} else { /* force RTS line to high level */
rts_paused = ATMEL_US_RTSDIS;
/* force RTS line to low level */
rts_ready = ATMEL_US_RTSEN;
}
if (mctrl & TIOCM_RTS)
control |= rts_ready; else
control |= rts_paused;
if (mctrl & TIOCM_DTR)
control |= ATMEL_US_DTREN; else
control |= ATMEL_US_DTRDIS;
atmel_uart_writel(port, ATMEL_US_CR, control);
mctrl_gpio_set(atmel_port->gpios, mctrl);
/* Local loopback mode? */
mode &= ~ATMEL_US_CHMODE; if (mctrl & TIOCM_LOOP)
mode |= ATMEL_US_CHMODE_LOC_LOOP; else
mode |= ATMEL_US_CHMODE_NORMAL;
atmel_uart_writel(port, ATMEL_US_MR, mode);
}
/* * Get state of the modem control input lines
*/ static u_int atmel_get_mctrl(struct uart_port *port)
{ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsignedint ret = 0, status;
status = atmel_uart_readl(port, ATMEL_US_CSR);
/* * The control signals are active low.
*/ if (!(status & ATMEL_US_DCD))
ret |= TIOCM_CD; if (!(status & ATMEL_US_CTS))
ret |= TIOCM_CTS; if (!(status & ATMEL_US_DSR))
ret |= TIOCM_DSR; if (!(status & ATMEL_US_RI))
ret |= TIOCM_RI;
if (is_dma) { /* * Disable the transmitter. * This is mandatory when DMA is used, otherwise the DMA buffer * is fully transmitted.
*/
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
atmel_port->tx_stopped = true;
}
if (is_dma) { /* re-enable the transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
atmel_port->tx_stopped = false;
}
}
/* * start receiving - port is in process of being opened.
*/ staticvoid atmel_start_rx(struct uart_port *port)
{ /* reset status and receiver */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA);
/* * Stop receiving - port is in process of being closed.
*/ staticvoid atmel_stop_rx(struct uart_port *port)
{
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RXDIS);
status = atmel_uart_readl(port, ATMEL_US_CSR); while (status & ATMEL_US_RXRDY) {
ch = atmel_uart_read_char(port);
/* * note that the error handling code is * out of the main execution path
*/ if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
| ATMEL_US_OVRE | ATMEL_US_RXBRK)
|| atmel_port->break_active)) {
if (status & ATMEL_US_RXBRK
&& !atmel_port->break_active) {
atmel_port->break_active = 1;
atmel_uart_writel(port, ATMEL_US_IER,
ATMEL_US_RXBRK);
} else { /* * This is either the end-of-break * condition or we've received at * least one character without RXBRK * being set. In both cases, the next * RXBRK will indicate start-of-break.
*/
atmel_uart_writel(port, ATMEL_US_IDR,
ATMEL_US_RXBRK);
status &= ~ATMEL_US_RXBRK;
atmel_port->break_active = 0;
}
}
atmel_buffer_rx_char(port, status, ch);
status = atmel_uart_readl(port, ATMEL_US_CSR);
}
pending = uart_port_tx(port, ch,
atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY,
atmel_uart_write_char(port, ch)); if (pending) { /* we still have characters to transmit, so we should continue * transmitting them when TX is ready, regardless of * mode or duplexity
*/
atmel_port->tx_done_mask |= ATMEL_US_TXRDY;
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
/* * xmit is a circular buffer so, if we have just send data from the * tail to the end, now we have to transmit the remaining data from the * beginning to the head.
*/ if (!kfifo_is_empty(&tport->xmit_fifo))
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); elseif (atmel_uart_is_half_duplex(port)) { /* * DMA done, re-enable TXEMPTY and signal that we can stop * TX and start RX for RS485
*/
atmel_port->hd_start_rx = true;
atmel_uart_writel(port, ATMEL_US_IER,
atmel_port->tx_done_mask);
}
/* Make sure we have an idle channel */ if (atmel_port->desc_tx != NULL) return;
if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) { /* * DMA is idle now. * Port xmit buffer is already mapped, * and it is one page... Just adjust * offsets and lengths. Since it is a circular buffer, * we have to transmit till the end, and then the rest. * Take the port lock to get a * consistent xmit buffer state.
*/
tx_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
UART_XMIT_SIZE);
if (atmel_port->fifo_size) { /* multi data mode */
part1_len = (tx_len & ~0x3); /* DWORD access */
part2_len = (tx_len & 0x3); /* BYTE access */
} else { /* single data (legacy) mode */
part1_len = 0;
part2_len = tx_len; /* BYTE access only */
}
/* Reset the UART timeout early so that we don't miss one */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_STTTO);
dmastat = dmaengine_tx_status(chan,
atmel_port->cookie_rx,
&state); /* Restart a new tasklet if DMA status is error */ if (dmastat == DMA_ERROR) {
dev_dbg(port->dev, "Get residue error, restart tasklet\n");
atmel_uart_writel(port, ATMEL_US_IER, ATMEL_US_TIMEOUT);
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_rx); return;
}
/* CPU claims ownership of RX DMA buffer */
dma_sync_single_for_cpu(port->dev, atmel_port->rx_phys,
ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE);
/* * ring->head points to the end of data already written by the DMA. * ring->tail points to the beginning of data to be read by the * framework. * The current transfer size should not be larger than the dma buffer * length.
*/
ring->head = ATMEL_SERIAL_RX_SIZE - state.residue;
BUG_ON(ring->head > ATMEL_SERIAL_RX_SIZE); /* * At this point ring->head may point to the first byte right after the * last byte of the dma buffer: * 0 <= ring->head <= sg_dma_len(&atmel_port->sg_rx) * * However ring->tail must always points inside the dma buffer: * 0 <= ring->tail <= sg_dma_len(&atmel_port->sg_rx) - 1 * * Since we use a ring buffer, we have to handle the case * where head is lower than tail. In such a case, we first read from * tail to the end of the buffer then reset tail.
*/ if (ring->head < ring->tail) {
count = ATMEL_SERIAL_RX_SIZE - ring->tail;
if (atmel_use_pdc_rx(port)) { /* * PDC receive. Just schedule the tasklet and let it * figure out the details. * * TODO: We're not handling error flags correctly at * the moment.
*/ if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) {
atmel_uart_writel(port, ATMEL_US_IDR,
(ATMEL_US_ENDRX | ATMEL_US_TIMEOUT));
atmel_tasklet_schedule(atmel_port,
&atmel_port->tasklet_rx);
}
if (atmel_use_dma_rx(port)) { if (pending & ATMEL_US_TIMEOUT) {
atmel_uart_writel(port, ATMEL_US_IDR,
ATMEL_US_TIMEOUT);
atmel_tasklet_schedule(atmel_port,
&atmel_port->tasklet_rx);
}
}
/* Interrupt receive */ if (pending & ATMEL_US_RXRDY)
atmel_rx_chars(port); elseif (pending & ATMEL_US_RXBRK) { /* * End of break detected. If it came along with a * character, atmel_rx_chars will handle it.
*/
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA);
atmel_uart_writel(port, ATMEL_US_IDR, ATMEL_US_RXBRK);
atmel_port->break_active = 0;
}
}
if (pending & atmel_port->tx_done_mask) {
atmel_uart_writel(port, ATMEL_US_IDR,
atmel_port->tx_done_mask);
/* Start RX if flag was set and FIFO is empty */ if (atmel_port->hd_start_rx) { if (!(atmel_uart_readl(port, ATMEL_US_CSR)
& ATMEL_US_TXEMPTY))
dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n");
port->icount.rx++;
status = c.status;
flg = TTY_NORMAL;
/* * note that the error handling code is * out of the main execution path
*/ if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
| ATMEL_US_OVRE | ATMEL_US_RXBRK))) { if (status & ATMEL_US_RXBRK) { /* ignore side-effect */
status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
port->icount.brk++; if (uart_handle_break(port)) continue;
} if (status & ATMEL_US_PARE)
port->icount.parity++; if (status & ATMEL_US_FRAME)
port->icount.frame++; if (status & ATMEL_US_OVRE)
port->icount.overrun++;
/* If the PDC has switched buffers, RPR won't contain * any address within the current buffer. Since head * is unsigned, we just need a one-way comparison to * find out. * * In this case, we just need to consume the entire * buffer and resubmit it for DMA. This will clear the * ENDRX bit as well, so that we can safely re-enable * all interrupts below.
*/
head = min(head, pdc->dma_size);
if (likely(head != tail)) {
dma_sync_single_for_cpu(port->dev, pdc->dma_addr,
pdc->dma_size, DMA_FROM_DEVICE);
/* * head will only wrap around when we recycle * the DMA buffer, and when that happens, we * explicitly set tail to 0. So head will * always be greater than tail.
*/
count = head - tail;
/* * If the current buffer is full, we need to check if * the next one contains any additional data.
*/ if (head >= pdc->dma_size) {
pdc->ofs = 0;
atmel_uart_writel(port, ATMEL_PDC_RNPR, pdc->dma_addr);
atmel_uart_writel(port, ATMEL_PDC_RNCR, pdc->dma_size);
/* * Get ip name usart or uart
*/ staticvoid atmel_get_ip_name(struct uart_port *port)
{ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); int name = atmel_uart_readl(port, ATMEL_US_NAME);
u32 version;
u32 usart, dbgu_uart, new_uart; /* ASCII decoding for IP version */
usart = 0x55534152; /* USAR(T) */
dbgu_uart = 0x44424755; /* DBGU */
new_uart = 0x55415254; /* UART */
/* * Only USART devices from at91sam9260 SOC implement fractional * baudrate. It is available for all asynchronous modes, with the * following restriction: the sampling clock's duty cycle is not * constant.
*/
atmel_port->has_frac_baudrate = false;
atmel_port->has_hw_timer = false;
atmel_port->is_usart = false;
if (name == new_uart) {
dev_dbg(port->dev, "Uart with hw timer");
atmel_port->has_hw_timer = true;
atmel_port->rtor = ATMEL_UA_RTOR;
} elseif (name == usart) {
dev_dbg(port->dev, "Usart\n");
atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
atmel_port->is_usart = true;
atmel_port->rtor = ATMEL_US_RTOR;
version = atmel_uart_readl(port, ATMEL_US_VERSION); switch (version) { case 0x814: /* sama5d2 */
fallthrough; case 0x701: /* sama5d4 */
atmel_port->fidi_min = 3;
atmel_port->fidi_max = 65535; break; case 0x502: /* sam9x5, sama5d3 */
atmel_port->fidi_min = 3;
atmel_port->fidi_max = 2047; break; default:
atmel_port->fidi_min = 1;
atmel_port->fidi_max = 2047;
}
} elseif (name == dbgu_uart) {
dev_dbg(port->dev, "Dbgu or uart without hw timer\n");
} else { /* fallback for older SoCs: use version field */
version = atmel_uart_readl(port, ATMEL_US_VERSION); switch (version) { case 0x302: case 0x10213: case 0x10302:
dev_dbg(port->dev, "This version is usart\n");
atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
atmel_port->is_usart = true;
atmel_port->rtor = ATMEL_US_RTOR; break; case 0x203: case 0x10202:
dev_dbg(port->dev, "This version is uart\n"); break; default:
dev_err(port->dev, "Not supported ip name nor version, set to uart\n");
}
}
}
/* * Perform initialization and enable port for reception
*/ staticint atmel_startup(struct uart_port *port)
{ struct platform_device *pdev = to_platform_device(port->dev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); int retval;
/* * Ensure that no interrupts are enabled otherwise when * request_irq() is called we could get stuck trying to * handle an unexpected interrupt
*/
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_port->ms_irq_enabled = false;
/* * Allocate the IRQ
*/
retval = request_irq(port->irq, atmel_interrupt,
IRQF_SHARED | IRQF_COND_SUSPEND,
dev_name(&pdev->dev), port); if (retval) {
dev_err(port->dev, "atmel_startup - Can't get irq\n"); return retval;
}
/* * Flush any TX data submitted for DMA. Called when the TX circular * buffer is reset.
*/ staticvoid atmel_flush_buffer(struct uart_port *port)
{ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
if (atmel_use_pdc_tx(port)) {
atmel_uart_writel(port, ATMEL_PDC_TCR, 0);
atmel_port->pdc_tx.ofs = 0;
} /* * in uart_flush_buffer(), the xmit circular buffer has just * been cleared, so we have to reset tx_len accordingly.
*/
atmel_port->tx_len = 0;
}
/* * Disable the port
*/ staticvoid atmel_shutdown(struct uart_port *port)
{ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* Disable modem control lines interrupts */
atmel_disable_ms(port);
/* Disable interrupts at device level */
atmel_uart_writel(port, ATMEL_US_IDR, -1);
/* Prevent spurious interrupts from scheduling the tasklet */
atomic_inc(&atmel_port->tasklet_shutdown);
/* * Prevent any tasklets being scheduled during * cleanup
*/
timer_delete_sync(&atmel_port->uart_timer);
/* Make sure that no interrupt is on the fly */
synchronize_irq(port->irq);
/* * Clear out any scheduled tasklets before * we destroy the buffers
*/
tasklet_kill(&atmel_port->tasklet_rx);
tasklet_kill(&atmel_port->tasklet_tx);
/* * Ensure everything is stopped and * disable port and break condition.
*/
atmel_stop_rx(port);
atmel_stop_tx(port);
switch (state) { case UART_PM_STATE_ON: /* * Enable the peripheral clock for this serial port. * This is called on uart_open() or a resume event.
*/
clk_prepare_enable(atmel_port->clk);
/* re-enable interrupts if we disabled some on suspend */
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->backup_imr); break; case UART_PM_STATE_OFF: /* Back up the interrupt mask and disable all interrupts */
atmel_port->backup_imr = atmel_uart_readl(port, ATMEL_US_IMR);
atmel_uart_writel(port, ATMEL_US_IDR, -1);
/* * Disable the peripheral clock for this serial port. * This is called on uart_close() or a suspend event.
*/
clk_disable_unprepare(atmel_port->clk); if (__clk_is_enabled(atmel_port->gclk))
clk_disable_unprepare(atmel_port->gclk); break; default:
dev_err(port->dev, "atmel_serial: unknown pm %d\n", state);
}
}
if (atmel_use_pdc_rx(port)) /* need to enable error interrupts */
atmel_uart_writel(port, ATMEL_US_IER, port->read_status_mask);
/* * Characters to ignore
*/
port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= ATMEL_US_RXBRK; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support).
*/ if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= ATMEL_US_OVRE;
} /* TODO: Ignore all characters if CREAD is set.*/
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
/* * save/disable interrupts. The tty layer will ensure that the * transmitter is empty if requested by the caller, so there's * no need to wait for it here.
*/
imr = atmel_uart_readl(port, ATMEL_US_IMR);
atmel_uart_writel(port, ATMEL_US_IDR, -1);
/* mode */ if (port->rs485.flags & SER_RS485_ENABLED) {
atmel_uart_writel(port, ATMEL_US_TTGR,
port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} elseif (port->iso7816.flags & SER_ISO7816_ENABLED) {
atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg); /* select mck clock, and output */
mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO; /* set max iterations */
mode |= ATMEL_US_MAX_ITER(3); if ((port->iso7816.flags & SER_ISO7816_T_PARAM)
== SER_ISO7816_T(0))
mode |= ATMEL_US_USMODE_ISO7816_T0; else
mode |= ATMEL_US_USMODE_ISO7816_T1;
} elseif (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ if (atmel_use_fifo(port) &&
!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) { /* * with ATMEL_US_USMODE_HWHS set, the controller will * be able to drive the RTS pin high/low when the RX * FIFO is above RXFTHRES/below RXFTHRES2. * It will also disable the transmitter when the CTS * pin is high. * This mode is not activated if CTS pin is a GPIO * because in this case, the transmitter is always * disabled (there must be an internal pull-up * responsible for this behaviour). * If the RTS pin is a GPIO, the controller won't be * able to drive it according to the FIFO thresholds, * but it will be handled by the driver.
*/
mode |= ATMEL_US_USMODE_HWHS;
} else { /* * For platforms without FIFO, the flow control is * handled by the driver.
*/
mode |= ATMEL_US_USMODE_NORMAL;
}
} else { /* RS232 without hadware handshake */
mode |= ATMEL_US_USMODE_NORMAL;
}
/* * Set the baud rate: * Fractional baudrate allows to setup output frequency more * accurately. This feature is enabled only when using normal mode. * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8)) * Currently, OVER is always set to 0 so we get * baudrate = selected clock / (16 * (CD + FP / 8)) * then * 8 CD + FP = selected clock / (2 * baudrate)
*/ if (atmel_port->has_frac_baudrate) {
div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2);
cd = div >> 3;
fp = div & ATMEL_US_FP_MASK;
} else {
cd = uart_get_divisor(port, baud);
}
/* * If the current value of the Clock Divisor surpasses the 16 bit * ATMEL_US_CD mask and the IP is USART, switch to the Peripheral * Clock implicitly divided by 8. * If the IP is UART however, keep the highest possible value for * the CD and avoid needless division of CD, since UART IP's do not * support implicit division of the Peripheral Clock.
*/ if (atmel_port->is_usart && cd > ATMEL_US_CD) {
cd /= 8;
mode |= ATMEL_US_USCLKS_MCK_DIV8;
} else {
cd = min_t(unsignedint, cd, ATMEL_US_CD);
}
/* * If there is no Fractional Part, there is a high chance that * we may be able to generate a baudrate closer to the desired one * if we use the GCLK as the clock source driving the baudrate * generator.
*/ if (!atmel_port->has_frac_baudrate) { if (__clk_is_enabled(atmel_port->gclk))
clk_disable_unprepare(atmel_port->gclk);
gclk_rate = clk_round_rate(atmel_port->gclk, 16 * baud);
actual_baud = clk_get_rate(atmel_port->clk) / (16 * cd); if (gclk_rate && abs(atmel_error_rate(baud, actual_baud)) >
abs(atmel_error_rate(baud, gclk_rate / 16))) {
clk_set_rate(atmel_port->gclk, 16 * baud);
ret = clk_prepare_enable(atmel_port->gclk); if (ret) goto gclk_fail;
/* * Set the Clock Divisor for GCLK to 1. * Since we were able to generate the smallest * multiple of the desired baudrate times 16, * then we surely can generate a bigger multiple * with the exact error rate for an equally increased * CD. Thus no need to take into account * a higher value for CD.
*/
cd = 1;
}
}
gclk_fail:
quot = cd | fp << ATMEL_US_FP_OFFSET;
if (!(port->iso7816.flags & SER_ISO7816_ENABLED))
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
/* set the mode, clock divisor, parity, stop bits and data size */
atmel_uart_writel(port, ATMEL_US_MR, mode);
/* * when switching the mode, set the RTS line state according to the * new mode, otherwise keep the former state
*/ if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) { unsignedint rts_state;
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) { /* let the hardware control the RTS line */
rts_state = ATMEL_US_RTSDIS;
} else { /* force RTS line to low level */
rts_state = ATMEL_US_RTSEN;
}
/* * Release the memory region(s) being used by 'port'.
*/ staticvoid atmel_release_port(struct uart_port *port)
{ struct platform_device *mpdev = to_platform_device(port->dev->parent); int size = resource_size(mpdev->resource);
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.