1136e0ab9SAndy Shevchenko // SPDX-License-Identifier: GPL-2.0+ 2136e0ab9SAndy Shevchenko /* Synopsys DesignWare 8250 library. */ 3136e0ab9SAndy Shevchenko 4136e0ab9SAndy Shevchenko #include <linux/bitops.h> 5136e0ab9SAndy Shevchenko #include <linux/device.h> 6136e0ab9SAndy Shevchenko #include <linux/io.h> 7136e0ab9SAndy Shevchenko #include <linux/kernel.h> 8136e0ab9SAndy Shevchenko #include <linux/serial_8250.h> 9136e0ab9SAndy Shevchenko #include <linux/serial_core.h> 10136e0ab9SAndy Shevchenko 11136e0ab9SAndy Shevchenko #include "8250_dwlib.h" 12136e0ab9SAndy Shevchenko 13136e0ab9SAndy Shevchenko /* Offsets for the DesignWare specific registers */ 14136e0ab9SAndy Shevchenko #define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ 15136e0ab9SAndy Shevchenko #define DW_UART_CPR 0xf4 /* Component Parameter Register */ 16136e0ab9SAndy Shevchenko #define DW_UART_UCV 0xf8 /* UART Component Version */ 17136e0ab9SAndy Shevchenko 18136e0ab9SAndy Shevchenko /* Component Parameter Register bits */ 19136e0ab9SAndy Shevchenko #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) 20136e0ab9SAndy Shevchenko #define DW_UART_CPR_AFCE_MODE (1 << 4) 21136e0ab9SAndy Shevchenko #define DW_UART_CPR_THRE_MODE (1 << 5) 22136e0ab9SAndy Shevchenko #define DW_UART_CPR_SIR_MODE (1 << 6) 23136e0ab9SAndy Shevchenko #define DW_UART_CPR_SIR_LP_MODE (1 << 7) 24136e0ab9SAndy Shevchenko #define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) 25136e0ab9SAndy Shevchenko #define DW_UART_CPR_FIFO_ACCESS (1 << 9) 26136e0ab9SAndy Shevchenko #define DW_UART_CPR_FIFO_STAT (1 << 10) 27136e0ab9SAndy Shevchenko #define DW_UART_CPR_SHADOW (1 << 11) 28136e0ab9SAndy Shevchenko #define DW_UART_CPR_ENCODED_PARMS (1 << 12) 29136e0ab9SAndy Shevchenko #define DW_UART_CPR_DMA_EXTRA (1 << 13) 30136e0ab9SAndy Shevchenko #define DW_UART_CPR_FIFO_MODE (0xff << 16) 31136e0ab9SAndy Shevchenko 32136e0ab9SAndy Shevchenko /* Helper for FIFO size calculation */ 33136e0ab9SAndy Shevchenko #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) 34136e0ab9SAndy Shevchenko 35136e0ab9SAndy Shevchenko static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) 36136e0ab9SAndy Shevchenko { 37136e0ab9SAndy Shevchenko if (p->iotype == UPIO_MEM32BE) 38136e0ab9SAndy Shevchenko return ioread32be(p->membase + offset); 39136e0ab9SAndy Shevchenko return readl(p->membase + offset); 40136e0ab9SAndy Shevchenko } 41136e0ab9SAndy Shevchenko 42136e0ab9SAndy Shevchenko static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) 43136e0ab9SAndy Shevchenko { 44136e0ab9SAndy Shevchenko if (p->iotype == UPIO_MEM32BE) 45136e0ab9SAndy Shevchenko iowrite32be(reg, p->membase + offset); 46136e0ab9SAndy Shevchenko else 47136e0ab9SAndy Shevchenko writel(reg, p->membase + offset); 48136e0ab9SAndy Shevchenko } 49136e0ab9SAndy Shevchenko 50136e0ab9SAndy Shevchenko /* 51136e0ab9SAndy Shevchenko * divisor = div(I) + div(F) 52136e0ab9SAndy Shevchenko * "I" means integer, "F" means fractional 53136e0ab9SAndy Shevchenko * quot = div(I) = clk / (16 * baud) 54136e0ab9SAndy Shevchenko * frac = div(F) * 2^dlf_size 55136e0ab9SAndy Shevchenko * 56136e0ab9SAndy Shevchenko * let rem = clk % (16 * baud) 57136e0ab9SAndy Shevchenko * we have: div(F) * (16 * baud) = rem 58136e0ab9SAndy Shevchenko * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) 59136e0ab9SAndy Shevchenko */ 60136e0ab9SAndy Shevchenko static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud, 61136e0ab9SAndy Shevchenko unsigned int *frac) 62136e0ab9SAndy Shevchenko { 63136e0ab9SAndy Shevchenko unsigned int quot, rem, base_baud = baud * 16; 64136e0ab9SAndy Shevchenko struct dw8250_port_data *d = p->private_data; 65136e0ab9SAndy Shevchenko 66136e0ab9SAndy Shevchenko quot = p->uartclk / base_baud; 67136e0ab9SAndy Shevchenko rem = p->uartclk % base_baud; 68136e0ab9SAndy Shevchenko *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); 69136e0ab9SAndy Shevchenko 70136e0ab9SAndy Shevchenko return quot; 71136e0ab9SAndy Shevchenko } 72136e0ab9SAndy Shevchenko 73136e0ab9SAndy Shevchenko static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, 74136e0ab9SAndy Shevchenko unsigned int quot, unsigned int quot_frac) 75136e0ab9SAndy Shevchenko { 76136e0ab9SAndy Shevchenko dw8250_writel_ext(p, DW_UART_DLF, quot_frac); 77136e0ab9SAndy Shevchenko serial8250_do_set_divisor(p, baud, quot, quot_frac); 78136e0ab9SAndy Shevchenko } 79136e0ab9SAndy Shevchenko 807c4fc082SAndy Shevchenko void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old) 817c4fc082SAndy Shevchenko { 827c4fc082SAndy Shevchenko p->status &= ~UPSTAT_AUTOCTS; 837c4fc082SAndy Shevchenko if (termios->c_cflag & CRTSCTS) 847c4fc082SAndy Shevchenko p->status |= UPSTAT_AUTOCTS; 857c4fc082SAndy Shevchenko 867c4fc082SAndy Shevchenko serial8250_do_set_termios(p, termios, old); 877c4fc082SAndy Shevchenko } 887c4fc082SAndy Shevchenko EXPORT_SYMBOL_GPL(dw8250_do_set_termios); 897c4fc082SAndy Shevchenko 90136e0ab9SAndy Shevchenko void dw8250_setup_port(struct uart_port *p) 91136e0ab9SAndy Shevchenko { 92*593dea00SMiquel Raynal struct dw8250_port_data *pd = p->private_data; 93*593dea00SMiquel Raynal struct dw8250_data *data = to_dw8250_data(pd); 94136e0ab9SAndy Shevchenko struct uart_8250_port *up = up_to_u8250p(p); 95136e0ab9SAndy Shevchenko u32 reg; 96136e0ab9SAndy Shevchenko 97136e0ab9SAndy Shevchenko /* 98136e0ab9SAndy Shevchenko * If the Component Version Register returns zero, we know that 99136e0ab9SAndy Shevchenko * ADDITIONAL_FEATURES are not enabled. No need to go any further. 100136e0ab9SAndy Shevchenko */ 101136e0ab9SAndy Shevchenko reg = dw8250_readl_ext(p, DW_UART_UCV); 102136e0ab9SAndy Shevchenko if (!reg) 103136e0ab9SAndy Shevchenko return; 104136e0ab9SAndy Shevchenko 105136e0ab9SAndy Shevchenko dev_dbg(p->dev, "Designware UART version %c.%c%c\n", 106136e0ab9SAndy Shevchenko (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); 107136e0ab9SAndy Shevchenko 108136e0ab9SAndy Shevchenko dw8250_writel_ext(p, DW_UART_DLF, ~0U); 109136e0ab9SAndy Shevchenko reg = dw8250_readl_ext(p, DW_UART_DLF); 110136e0ab9SAndy Shevchenko dw8250_writel_ext(p, DW_UART_DLF, 0); 111136e0ab9SAndy Shevchenko 112136e0ab9SAndy Shevchenko if (reg) { 113*593dea00SMiquel Raynal pd->dlf_size = fls(reg); 114136e0ab9SAndy Shevchenko p->get_divisor = dw8250_get_divisor; 115136e0ab9SAndy Shevchenko p->set_divisor = dw8250_set_divisor; 116136e0ab9SAndy Shevchenko } 117136e0ab9SAndy Shevchenko 118136e0ab9SAndy Shevchenko reg = dw8250_readl_ext(p, DW_UART_CPR); 119*593dea00SMiquel Raynal if (!reg) { 120*593dea00SMiquel Raynal reg = data->pdata->cpr_val; 121*593dea00SMiquel Raynal dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); 122*593dea00SMiquel Raynal } 123136e0ab9SAndy Shevchenko if (!reg) 124136e0ab9SAndy Shevchenko return; 125136e0ab9SAndy Shevchenko 126136e0ab9SAndy Shevchenko /* Select the type based on FIFO */ 127136e0ab9SAndy Shevchenko if (reg & DW_UART_CPR_FIFO_MODE) { 128136e0ab9SAndy Shevchenko p->type = PORT_16550A; 129136e0ab9SAndy Shevchenko p->flags |= UPF_FIXED_TYPE; 130136e0ab9SAndy Shevchenko p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); 131136e0ab9SAndy Shevchenko up->capabilities = UART_CAP_FIFO; 132136e0ab9SAndy Shevchenko } 133136e0ab9SAndy Shevchenko 134136e0ab9SAndy Shevchenko if (reg & DW_UART_CPR_AFCE_MODE) 135136e0ab9SAndy Shevchenko up->capabilities |= UART_CAP_AFE; 136136e0ab9SAndy Shevchenko 137136e0ab9SAndy Shevchenko if (reg & DW_UART_CPR_SIR_MODE) 138136e0ab9SAndy Shevchenko up->capabilities |= UART_CAP_IRDA; 139136e0ab9SAndy Shevchenko } 140136e0ab9SAndy Shevchenko EXPORT_SYMBOL_GPL(dw8250_setup_port); 141