Branch data Line data Source code
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 : 0 : static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) 36 : : { 37 : 0 : if (p->iotype == UPIO_MEM32BE) 38 : 0 : return ioread32be(p->membase + offset); 39 : 0 : return readl(p->membase + offset); 40 : : } 41 : : 42 : 0 : static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) 43 : : { 44 : 0 : if (p->iotype == UPIO_MEM32BE) 45 : 0 : iowrite32be(reg, p->membase + offset); 46 : : else 47 : 0 : 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 : 0 : static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud, 61 : : unsigned int *frac) 62 : : { 63 : 0 : unsigned int quot, rem, base_baud = baud * 16; 64 : 0 : struct dw8250_port_data *d = p->private_data; 65 : : 66 : 0 : quot = p->uartclk / base_baud; 67 : 0 : rem = p->uartclk % base_baud; 68 : 0 : *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); 69 : : 70 : 0 : return quot; 71 : : } 72 : : 73 : 0 : static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, 74 : : unsigned int quot, unsigned int quot_frac) 75 : : { 76 [ # # ]: 0 : dw8250_writel_ext(p, DW_UART_DLF, quot_frac); 77 : 0 : serial8250_do_set_divisor(p, baud, quot, quot_frac); 78 : 0 : } 79 : : 80 : 0 : void dw8250_setup_port(struct uart_port *p) 81 : : { 82 [ # # ]: 0 : struct uart_8250_port *up = up_to_u8250p(p); 83 : 0 : u32 reg; 84 : : 85 : : /* 86 : : * If the Component Version Register returns zero, we know that 87 : : * ADDITIONAL_FEATURES are not enabled. No need to go any further. 88 : : */ 89 [ # # ]: 0 : reg = dw8250_readl_ext(p, DW_UART_UCV); 90 [ # # ]: 0 : if (!reg) 91 : : return; 92 : : 93 : 0 : dev_dbg(p->dev, "Designware UART version %c.%c%c\n", 94 : : (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); 95 : : 96 [ # # ]: 0 : dw8250_writel_ext(p, DW_UART_DLF, ~0U); 97 [ # # ]: 0 : reg = dw8250_readl_ext(p, DW_UART_DLF); 98 [ # # ]: 0 : dw8250_writel_ext(p, DW_UART_DLF, 0); 99 : : 100 [ # # ]: 0 : if (reg) { 101 : 0 : struct dw8250_port_data *d = p->private_data; 102 : : 103 : 0 : d->dlf_size = fls(reg); 104 : 0 : p->get_divisor = dw8250_get_divisor; 105 : 0 : p->set_divisor = dw8250_set_divisor; 106 : : } 107 : : 108 [ # # ]: 0 : reg = dw8250_readl_ext(p, DW_UART_CPR); 109 [ # # ]: 0 : if (!reg) 110 : : return; 111 : : 112 : : /* Select the type based on FIFO */ 113 [ # # ]: 0 : if (reg & DW_UART_CPR_FIFO_MODE) { 114 : 0 : p->type = PORT_16550A; 115 : 0 : p->flags |= UPF_FIXED_TYPE; 116 : 0 : p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); 117 : 0 : up->capabilities = UART_CAP_FIFO; 118 : : } 119 : : 120 [ # # ]: 0 : if (reg & DW_UART_CPR_AFCE_MODE) 121 : 0 : up->capabilities |= UART_CAP_AFE; 122 : : 123 [ # # ]: 0 : if (reg & DW_UART_CPR_SIR_MODE) 124 : 0 : up->capabilities |= UART_CAP_IRDA; 125 : : } 126 : : EXPORT_SYMBOL_GPL(dw8250_setup_port);