8250_dwlib.c (6219B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* Synopsys DesignWare 8250 library. */ 3 4#include <linux/bitops.h> 5#include <linux/bitfield.h> 6#include <linux/device.h> 7#include <linux/kernel.h> 8#include <linux/property.h> 9#include <linux/serial_8250.h> 10#include <linux/serial_core.h> 11 12#include "8250_dwlib.h" 13 14/* Offsets for the DesignWare specific registers */ 15#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */ 16#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */ 17#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */ 18#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ 19#define DW_UART_CPR 0xf4 /* Component Parameter Register */ 20#define DW_UART_UCV 0xf8 /* UART Component Version */ 21 22/* Transceiver Control Register bits */ 23#define DW_UART_TCR_RS485_EN BIT(0) 24#define DW_UART_TCR_RE_POL BIT(1) 25#define DW_UART_TCR_DE_POL BIT(2) 26#define DW_UART_TCR_XFER_MODE GENMASK(4, 3) 27#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0) 28#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1) 29#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2) 30 31/* Component Parameter Register bits */ 32#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) 33#define DW_UART_CPR_AFCE_MODE (1 << 4) 34#define DW_UART_CPR_THRE_MODE (1 << 5) 35#define DW_UART_CPR_SIR_MODE (1 << 6) 36#define DW_UART_CPR_SIR_LP_MODE (1 << 7) 37#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) 38#define DW_UART_CPR_FIFO_ACCESS (1 << 9) 39#define DW_UART_CPR_FIFO_STAT (1 << 10) 40#define DW_UART_CPR_SHADOW (1 << 11) 41#define DW_UART_CPR_ENCODED_PARMS (1 << 12) 42#define DW_UART_CPR_DMA_EXTRA (1 << 13) 43#define DW_UART_CPR_FIFO_MODE (0xff << 16) 44 45/* Helper for FIFO size calculation */ 46#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) 47 48/* 49 * divisor = div(I) + div(F) 50 * "I" means integer, "F" means fractional 51 * quot = div(I) = clk / (16 * baud) 52 * frac = div(F) * 2^dlf_size 53 * 54 * let rem = clk % (16 * baud) 55 * we have: div(F) * (16 * baud) = rem 56 * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) 57 */ 58static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud, 59 unsigned int *frac) 60{ 61 unsigned int quot, rem, base_baud = baud * 16; 62 struct dw8250_port_data *d = p->private_data; 63 64 quot = p->uartclk / base_baud; 65 rem = p->uartclk % base_baud; 66 *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); 67 68 return quot; 69} 70 71static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, 72 unsigned int quot, unsigned int quot_frac) 73{ 74 dw8250_writel_ext(p, DW_UART_DLF, quot_frac); 75 serial8250_do_set_divisor(p, baud, quot, quot_frac); 76} 77 78void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old) 79{ 80 p->status &= ~UPSTAT_AUTOCTS; 81 if (termios->c_cflag & CRTSCTS) 82 p->status |= UPSTAT_AUTOCTS; 83 84 serial8250_do_set_termios(p, termios, old); 85} 86EXPORT_SYMBOL_GPL(dw8250_do_set_termios); 87 88static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485) 89{ 90 u32 tcr; 91 92 tcr = dw8250_readl_ext(p, DW_UART_TCR); 93 tcr &= ~DW_UART_TCR_XFER_MODE; 94 95 if (rs485->flags & SER_RS485_ENABLED) { 96 /* Clear unsupported flags. */ 97 rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | 98 SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND; 99 tcr |= DW_UART_TCR_RS485_EN; 100 101 if (rs485->flags & SER_RS485_RX_DURING_TX) { 102 tcr |= DW_UART_TCR_XFER_MODE_DE_DURING_RE; 103 } else { 104 /* HW does not support same DE level for tx and rx */ 105 if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == 106 !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) 107 return -EINVAL; 108 109 tcr |= DW_UART_TCR_XFER_MODE_DE_OR_RE; 110 } 111 dw8250_writel_ext(p, DW_UART_DE_EN, 1); 112 dw8250_writel_ext(p, DW_UART_RE_EN, 1); 113 } else { 114 rs485->flags = 0; 115 116 tcr &= ~DW_UART_TCR_RS485_EN; 117 } 118 119 /* Reset to default polarity */ 120 tcr |= DW_UART_TCR_DE_POL; 121 tcr &= ~DW_UART_TCR_RE_POL; 122 123 if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) 124 tcr &= ~DW_UART_TCR_DE_POL; 125 if (device_property_read_bool(p->dev, "rs485-rx-active-high")) 126 tcr |= DW_UART_TCR_RE_POL; 127 128 dw8250_writel_ext(p, DW_UART_TCR, tcr); 129 130 rs485->delay_rts_before_send = 0; 131 rs485->delay_rts_after_send = 0; 132 133 p->rs485 = *rs485; 134 135 return 0; 136} 137 138/* 139 * Tests if RE_EN register can have non-zero value to see if RS-485 HW support 140 * is present. 141 */ 142static bool dw8250_detect_rs485_hw(struct uart_port *p) 143{ 144 u32 reg; 145 146 dw8250_writel_ext(p, DW_UART_RE_EN, 1); 147 reg = dw8250_readl_ext(p, DW_UART_RE_EN); 148 dw8250_writel_ext(p, DW_UART_RE_EN, 0); 149 return reg; 150} 151 152void dw8250_setup_port(struct uart_port *p) 153{ 154 struct dw8250_port_data *pd = p->private_data; 155 struct dw8250_data *data = to_dw8250_data(pd); 156 struct uart_8250_port *up = up_to_u8250p(p); 157 u32 reg; 158 159 pd->hw_rs485_support = dw8250_detect_rs485_hw(p); 160 if (pd->hw_rs485_support) { 161 p->rs485_config = dw8250_rs485_config; 162 } else { 163 p->rs485_config = serial8250_em485_config; 164 up->rs485_start_tx = serial8250_em485_start_tx; 165 up->rs485_stop_tx = serial8250_em485_stop_tx; 166 } 167 up->capabilities |= UART_CAP_NOTEMT; 168 169 /* 170 * If the Component Version Register returns zero, we know that 171 * ADDITIONAL_FEATURES are not enabled. No need to go any further. 172 */ 173 reg = dw8250_readl_ext(p, DW_UART_UCV); 174 if (!reg) 175 return; 176 177 dev_dbg(p->dev, "Designware UART version %c.%c%c\n", 178 (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); 179 180 dw8250_writel_ext(p, DW_UART_DLF, ~0U); 181 reg = dw8250_readl_ext(p, DW_UART_DLF); 182 dw8250_writel_ext(p, DW_UART_DLF, 0); 183 184 if (reg) { 185 pd->dlf_size = fls(reg); 186 p->get_divisor = dw8250_get_divisor; 187 p->set_divisor = dw8250_set_divisor; 188 } 189 190 reg = dw8250_readl_ext(p, DW_UART_CPR); 191 if (!reg) { 192 reg = data->pdata->cpr_val; 193 dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); 194 } 195 if (!reg) 196 return; 197 198 /* Select the type based on FIFO */ 199 if (reg & DW_UART_CPR_FIFO_MODE) { 200 p->type = PORT_16550A; 201 p->flags |= UPF_FIXED_TYPE; 202 p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); 203 up->capabilities = UART_CAP_FIFO | UART_CAP_NOTEMT; 204 } 205 206 if (reg & DW_UART_CPR_AFCE_MODE) 207 up->capabilities |= UART_CAP_AFE; 208 209 if (reg & DW_UART_CPR_SIR_MODE) 210 up->capabilities |= UART_CAP_IRDA; 211} 212EXPORT_SYMBOL_GPL(dw8250_setup_port);