diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e7c337de31d1..87de21f0c7a3 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -56,6 +56,15 @@ /* 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 + #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif @@ -141,6 +150,9 @@ struct atmel_uart_port { struct mctrl_gpios *gpios; int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; + u32 fifo_size; + u32 rts_high; + u32 rts_low; bool ms_irq_enabled; bool is_usart; /* usart or uart */ struct timer_list uart_timer; /* uart timer */ @@ -191,6 +203,16 @@ static inline void atmel_uart_writel(struct uart_port *port, u32 reg, u32 value) __raw_writel(value, port->membase + reg); } +static inline u8 atmel_uart_readb(struct uart_port *port, u32 reg) +{ + return __raw_readb(port->membase + reg); +} + +static inline void atmel_uart_writeb(struct uart_port *port, u32 reg, u8 value) +{ + __raw_writeb(value, port->membase + reg); +} + #ifdef CONFIG_SERIAL_ATMEL_PDC static bool atmel_use_pdc_rx(struct uart_port *port) { @@ -635,7 +657,7 @@ static void atmel_rx_chars(struct uart_port *port) status = atmel_uart_readl(port, ATMEL_US_CSR); while (status & ATMEL_US_RXRDY) { - ch = atmel_uart_readl(port, ATMEL_US_RHR); + ch = atmel_uart_readb(port, ATMEL_US_RHR); /* * note that the error handling code is @@ -686,7 +708,7 @@ static void atmel_tx_chars(struct uart_port *port) if (port->x_char && (atmel_uart_readl(port, ATMEL_US_CSR) & atmel_port->tx_done_mask)) { - atmel_uart_writel(port, ATMEL_US_THR, port->x_char); + atmel_uart_writeb(port, ATMEL_US_THR, port->x_char); port->icount.tx++; port->x_char = 0; } @@ -695,7 +717,7 @@ static void atmel_tx_chars(struct uart_port *port) while (atmel_uart_readl(port, ATMEL_US_CSR) & atmel_port->tx_done_mask) { - atmel_uart_writel(port, ATMEL_US_THR, xmit->buf[xmit->tail]); + atmel_uart_writeb(port, ATMEL_US_THR, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) @@ -1796,6 +1818,29 @@ static int atmel_startup(struct uart_port *port) atmel_set_ops(port); } + /* + * Enable FIFO when available + */ + if (atmel_port->fifo_size) { + unsigned int txrdym = ATMEL_US_ONE_DATA; + unsigned int rxrdym = ATMEL_US_ONE_DATA; + unsigned int fmr; + + atmel_uart_writel(port, ATMEL_US_CR, + ATMEL_US_FIFOEN | + ATMEL_US_RXFCLR | + ATMEL_US_TXFLCLR); + + fmr = ATMEL_US_TXRDYM(txrdym) | ATMEL_US_RXRDYM(rxrdym); + if (atmel_port->rts_high && + atmel_port->rts_low) + fmr |= ATMEL_US_FRTSC | + ATMEL_US_RXFTHRES(atmel_port->rts_high) | + ATMEL_US_RXFTHRES2(atmel_port->rts_low); + + atmel_uart_writel(port, ATMEL_US_FMR, fmr); + } + /* Save current CSR for comparison in atmel_tasklet_func() */ atmel_port->irq_status_prev = atmel_get_lines_status(port); atmel_port->irq_status = atmel_port->irq_status_prev; @@ -2213,7 +2258,7 @@ static int atmel_poll_get_char(struct uart_port *port) while (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_RXRDY)) cpu_relax(); - return atmel_uart_readl(port, ATMEL_US_RHR); + return atmel_uart_readb(port, ATMEL_US_RHR); } static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) @@ -2221,7 +2266,7 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) while (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY)) cpu_relax(); - atmel_uart_writel(port, ATMEL_US_THR, ch); + atmel_uart_writeb(port, ATMEL_US_THR, ch); } #endif @@ -2328,7 +2373,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch) { while (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY)) cpu_relax(); - atmel_uart_writel(port, ATMEL_US_THR, ch); + atmel_uart_writeb(port, ATMEL_US_THR, ch); } /* @@ -2603,6 +2648,48 @@ static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) return 0; } +static void atmel_serial_probe_fifos(struct atmel_uart_port *port, + struct platform_device *pdev) +{ + port->fifo_size = 0; + port->rts_low = 0; + port->rts_high = 0; + + if (of_property_read_u32(pdev->dev.of_node, + "atmel,fifo-size", + &port->fifo_size)) + return; + + if (!port->fifo_size) + return; + + if (port->fifo_size < ATMEL_MIN_FIFO_SIZE) { + port->fifo_size = 0; + dev_err(&pdev->dev, "Invalid FIFO size\n"); + return; + } + + /* + * 0 <= rts_low <= rts_high <= fifo_size + * Once their CTS line asserted by the remote peer, some x86 UARTs tend + * to flush their internal TX FIFO, commonly up to 16 data, before + * actually stopping to send new data. So we try to set the RTS High + * Threshold to a reasonably high value respecting this 16 data + * empirical rule when possible. + */ + port->rts_high = max_t(int, port->fifo_size >> 1, + port->fifo_size - ATMEL_RTS_HIGH_OFFSET); + port->rts_low = max_t(int, port->fifo_size >> 2, + port->fifo_size - ATMEL_RTS_LOW_OFFSET); + + dev_info(&pdev->dev, "Using FIFO (%u data)\n", + port->fifo_size); + dev_dbg(&pdev->dev, "RTS High Threshold : %2u data\n", + port->rts_high); + dev_dbg(&pdev->dev, "RTS Low Threshold : %2u data\n", + port->rts_low); +} + static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; @@ -2639,6 +2726,7 @@ static int atmel_serial_probe(struct platform_device *pdev) port = &atmel_ports[ret]; port->backup_imr = 0; port->uart.line = ret; + atmel_serial_probe_fifos(port, pdev); spin_lock_init(&port->lock_suspended); diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h index c384c21d65f0..ee696d7e8a43 100644 --- a/include/linux/atmel_serial.h +++ b/include/linux/atmel_serial.h @@ -35,6 +35,11 @@ #define ATMEL_US_DTRDIS BIT(17) /* Data Terminal Ready Disable */ #define ATMEL_US_RTSEN BIT(18) /* Request To Send Enable */ #define ATMEL_US_RTSDIS BIT(19) /* Request To Send Disable */ +#define ATMEL_US_TXFCLR BIT(24) /* Transmit FIFO Clear */ +#define ATMEL_US_RXFCLR BIT(25) /* Receive FIFO Clear */ +#define ATMEL_US_TXFLCLR BIT(26) /* Transmit FIFO Lock Clear */ +#define ATMEL_US_FIFOEN BIT(30) /* FIFO enable */ +#define ATMEL_US_FIFODIS BIT(31) /* FIFO disable */ #define ATMEL_US_MR 0x04 /* Mode Register */ #define ATMEL_US_USMODE GENMASK(3, 0) /* Mode of the USART */ @@ -124,6 +129,37 @@ #define ATMEL_US_NER 0x44 /* Number of Errors Register */ #define ATMEL_US_IF 0x4c /* IrDA Filter Register */ +#define ATMEL_US_CMPR 0x90 /* Comparaison Register */ +#define ATMEL_US_FMR 0xa0 /* FIFO Mode Register */ +#define ATMEL_US_TXRDYM(data) (((data) & 0x3) << 0) /* TX Ready Mode */ +#define ATMEL_US_RXRDYM(data) (((data) & 0x3) << 4) /* RX Ready Mode */ +#define ATMEL_US_ONE_DATA 0x0 +#define ATMEL_US_TWO_DATA 0x1 +#define ATMEL_US_FOUR_DATA 0x2 +#define ATMEL_US_FRTSC BIT(7) /* FIFO RTS pin Control */ +#define ATMEL_US_TXFTHRES(thr) (((thr) & 0x3f) << 8) /* TX FIFO Threshold */ +#define ATMEL_US_RXFTHRES(thr) (((thr) & 0x3f) << 16) /* RX FIFO Threshold */ +#define ATMEL_US_RXFTHRES2(thr) (((thr) & 0x3f) << 24) /* RX FIFO Threshold2 */ + +#define ATMEL_US_FLR 0xa4 /* FIFO Level Register */ +#define ATMEL_US_TXFL(reg) (((reg) >> 0) & 0x3f) /* TX FIFO Level */ +#define ATMEL_US_RXFL(reg) (((reg) >> 16) & 0x3f) /* RX FIFO Level */ + +#define ATMEL_US_FIER 0xa8 /* FIFO Interrupt Enable Register */ +#define ATMEL_US_FIDR 0xac /* FIFO Interrupt Disable Register */ +#define ATMEL_US_FIMR 0xb0 /* FIFO Interrupt Mask Register */ +#define ATMEL_US_FESR 0xb4 /* FIFO Event Status Register */ +#define ATMEL_US_TXFEF BIT(0) /* Transmit FIFO Empty Flag */ +#define ATMEL_US_TXFFF BIT(1) /* Transmit FIFO Full Flag */ +#define ATMEL_US_TXFTHF BIT(2) /* Transmit FIFO Threshold Flag */ +#define ATMEL_US_RXFEF BIT(3) /* Receive FIFO Empty Flag */ +#define ATMEL_US_RXFFF BIT(4) /* Receive FIFO Full Flag */ +#define ATMEL_US_RXFTHF BIT(5) /* Receive FIFO Threshold Flag */ +#define ATMEL_US_TXFPTEF BIT(6) /* Transmit FIFO Pointer Error Flag */ +#define ATMEL_US_RXFPTEF BIT(7) /* Receive FIFO Pointer Error Flag */ +#define ATMEL_US_TXFLOCK BIT(8) /* Transmit FIFO Lock (FESR only) */ +#define ATMEL_US_RXFTHF2 BIT(9) /* Receive FIFO Threshold Flag 2 */ + #define ATMEL_US_NAME 0xf0 /* Ip Name */ #define ATMEL_US_VERSION 0xfc /* Ip Version */