1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Synopsys DesignWare 8250 library. */ 3 4 #include <linux/bitops.h> 5 #include <linux/device.h> 6 #include <linux/io.h> 7 #include <linux/kernel.h> 8 #include <linux/serial_8250.h> 9 #include <linux/serial_core.h> 10 11 #include "8250_dwlib.h" 12 13 /* Offsets for the DesignWare specific registers */ 14 #define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ 15 #define DW_UART_CPR 0xf4 /* Component Parameter Register */ 16 #define DW_UART_UCV 0xf8 /* UART Component Version */ 17 18 /* Component Parameter Register bits */ 19 #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) 20 #define DW_UART_CPR_AFCE_MODE (1 << 4) 21 #define DW_UART_CPR_THRE_MODE (1 << 5) 22 #define DW_UART_CPR_SIR_MODE (1 << 6) 23 #define DW_UART_CPR_SIR_LP_MODE (1 << 7) 24 #define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) 25 #define DW_UART_CPR_FIFO_ACCESS (1 << 9) 26 #define DW_UART_CPR_FIFO_STAT (1 << 10) 27 #define DW_UART_CPR_SHADOW (1 << 11) 28 #define DW_UART_CPR_ENCODED_PARMS (1 << 12) 29 #define DW_UART_CPR_DMA_EXTRA (1 << 13) 30 #define DW_UART_CPR_FIFO_MODE (0xff << 16) 31 32 /* Helper for FIFO size calculation */ 33 #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) 34 35 static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) 36 { 37 if (p->iotype == UPIO_MEM32BE) 38 return ioread32be(p->membase + offset); 39 return readl(p->membase + offset); 40 } 41 42 static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) 43 { 44 if (p->iotype == UPIO_MEM32BE) 45 iowrite32be(reg, p->membase + offset); 46 else 47 writel(reg, p->membase + offset); 48 } 49 50 /* 51 * divisor = div(I) + div(F) 52 * "I" means integer, "F" means fractional 53 * quot = div(I) = clk / (16 * baud) 54 * frac = div(F) * 2^dlf_size 55 * 56 * let rem = clk % (16 * baud) 57 * we have: div(F) * (16 * baud) = rem 58 * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) 59 */ 60 static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud, 61 unsigned int *frac) 62 { 63 unsigned int quot, rem, base_baud = baud * 16; 64 struct dw8250_port_data *d = p->private_data; 65 66 quot = p->uartclk / base_baud; 67 rem = p->uartclk % base_baud; 68 *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); 69 70 return quot; 71 } 72 73 static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, 74 unsigned int quot, unsigned int quot_frac) 75 { 76 dw8250_writel_ext(p, DW_UART_DLF, quot_frac); 77 serial8250_do_set_divisor(p, baud, quot, quot_frac); 78 } 79 80 void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old) 81 { 82 p->status &= ~UPSTAT_AUTOCTS; 83 if (termios->c_cflag & CRTSCTS) 84 p->status |= UPSTAT_AUTOCTS; 85 86 serial8250_do_set_termios(p, termios, old); 87 } 88 EXPORT_SYMBOL_GPL(dw8250_do_set_termios); 89 90 void dw8250_setup_port(struct uart_port *p) 91 { 92 struct uart_8250_port *up = up_to_u8250p(p); 93 u32 reg; 94 95 /* 96 * If the Component Version Register returns zero, we know that 97 * ADDITIONAL_FEATURES are not enabled. No need to go any further. 98 */ 99 reg = dw8250_readl_ext(p, DW_UART_UCV); 100 if (!reg) 101 return; 102 103 dev_dbg(p->dev, "Designware UART version %c.%c%c\n", 104 (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); 105 106 dw8250_writel_ext(p, DW_UART_DLF, ~0U); 107 reg = dw8250_readl_ext(p, DW_UART_DLF); 108 dw8250_writel_ext(p, DW_UART_DLF, 0); 109 110 if (reg) { 111 struct dw8250_port_data *d = p->private_data; 112 113 d->dlf_size = fls(reg); 114 p->get_divisor = dw8250_get_divisor; 115 p->set_divisor = dw8250_set_divisor; 116 } 117 118 reg = dw8250_readl_ext(p, DW_UART_CPR); 119 if (!reg) 120 return; 121 122 /* Select the type based on FIFO */ 123 if (reg & DW_UART_CPR_FIFO_MODE) { 124 p->type = PORT_16550A; 125 p->flags |= UPF_FIXED_TYPE; 126 p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); 127 up->capabilities = UART_CAP_FIFO; 128 } 129 130 if (reg & DW_UART_CPR_AFCE_MODE) 131 up->capabilities |= UART_CAP_AFE; 132 133 if (reg & DW_UART_CPR_SIR_MODE) 134 up->capabilities |= UART_CAP_IRDA; 135 } 136 EXPORT_SYMBOL_GPL(dw8250_setup_port); 137