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 35static 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 42static 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 */ 60static 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 73static 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 80void dw8250_setup_port(struct uart_port *p) 81{ 82 struct uart_8250_port *up = up_to_u8250p(p); 83 u32 reg, old_dlf; 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 reg = dw8250_readl_ext(p, DW_UART_UCV); 90 if (!reg) 91 return; 92 93 dev_dbg(p->dev, "Designware UART version %c.%c%c\n", 94 (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); 95 96 /* Preserve value written by firmware or bootloader */ 97 old_dlf = dw8250_readl_ext(p, DW_UART_DLF); 98 dw8250_writel_ext(p, DW_UART_DLF, ~0U); 99 reg = dw8250_readl_ext(p, DW_UART_DLF); 100 dw8250_writel_ext(p, DW_UART_DLF, old_dlf); 101 102 if (reg) { 103 struct dw8250_port_data *d = p->private_data; 104 105 d->dlf_size = fls(reg); 106 p->get_divisor = dw8250_get_divisor; 107 p->set_divisor = dw8250_set_divisor; 108 } 109 110 reg = dw8250_readl_ext(p, DW_UART_CPR); 111 if (!reg) 112 return; 113 114 /* Select the type based on FIFO */ 115 if (reg & DW_UART_CPR_FIFO_MODE) { 116 p->type = PORT_16550A; 117 p->flags |= UPF_FIXED_TYPE; 118 p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); 119 up->capabilities = UART_CAP_FIFO; 120 } 121 122 if (reg & DW_UART_CPR_AFCE_MODE) 123 up->capabilities |= UART_CAP_AFE; 124 125 if (reg & DW_UART_CPR_SIR_MODE) 126 up->capabilities |= UART_CAP_IRDA; 127} 128EXPORT_SYMBOL_GPL(dw8250_setup_port); 129