1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Texas Instruments' OMAP serial driver
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
6  *	Lokesh Vutla <lokeshvutla@ti.com>
7  */
8 
9 #include <config.h>
10 #include <dm.h>
11 #include <dt-structs.h>
12 #include <log.h>
13 #include <ns16550.h>
14 #include <serial.h>
15 #include <clk.h>
16 #include <linux/err.h>
17 
18 /*
19  * These are the definitions for the MDR1 register
20  */
21 #define UART_OMAP_MDR1_16X_MODE		0x00	/* UART 16x mode */
22 #define UART_OMAP_MDR1_13X_MODE		0x03	/* UART 13x mode */
23 
24 #ifndef CFG_SYS_NS16550_CLK
25 #define CFG_SYS_NS16550_CLK  0
26 #endif
27 
28 #ifdef CONFIG_DEBUG_UART_OMAP
29 
30 #ifndef CFG_SYS_NS16550_IER
31 #define CFG_SYS_NS16550_IER  0x00
32 #endif
33 
34 #define UART_MCRVAL 0x00
35 #define UART_LCRVAL UART_LCR_8N1
36 
serial_out_shift(void * addr,int shift,int value)37 static inline void serial_out_shift(void *addr, int shift, int value)
38 {
39 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
40 	outb(value, (ulong)addr);
41 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_LITTLE_ENDIAN)
42 	out_le32(addr, value);
43 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
44 	out_be32(addr, value);
45 #elif defined(CONFIG_SYS_NS16550_MEM32)
46 	writel(value, addr);
47 #elif defined(CONFIG_SYS_BIG_ENDIAN)
48 	writeb(value, addr + (1 << shift) - 1);
49 #else
50 	writeb(value, addr);
51 #endif
52 }
53 
serial_in_shift(void * addr,int shift)54 static inline int serial_in_shift(void *addr, int shift)
55 {
56 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
57 	return inb((ulong)addr);
58 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_LITTLE_ENDIAN)
59 	return in_le32(addr);
60 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
61 	return in_be32(addr);
62 #elif defined(CONFIG_SYS_NS16550_MEM32)
63 	return readl(addr);
64 #elif defined(CONFIG_SYS_BIG_ENDIAN)
65 	return readb(addr + (1 << shift) - 1);
66 #else
67 	return readb(addr);
68 #endif
69 }
70 
71 #include <debug_uart.h>
72 
_debug_uart_init(void)73 static inline void _debug_uart_init(void)
74 {
75 	struct ns16550 *com_port = (struct ns16550 *)CONFIG_VAL(DEBUG_UART_BASE);
76 	int baud_divisor;
77 
78 	baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK,
79 					    CONFIG_BAUDRATE);
80 	serial_dout(&com_port->ier, CFG_SYS_NS16550_IER);
81 	serial_dout(&com_port->mdr1, 0x7);
82 	serial_dout(&com_port->mcr, UART_MCRVAL);
83 	serial_dout(&com_port->fcr, UART_FCR_DEFVAL);
84 
85 	serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL);
86 	serial_dout(&com_port->dll, baud_divisor & 0xff);
87 	serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff);
88 	serial_dout(&com_port->lcr, UART_LCRVAL);
89 	serial_dout(&com_port->mdr1, 0x0);
90 }
91 
_debug_uart_putc(int ch)92 static inline void _debug_uart_putc(int ch)
93 {
94 	struct ns16550 *com_port = (struct ns16550 *)CONFIG_VAL(DEBUG_UART_BASE);
95 
96 	while (!(serial_din(&com_port->lsr) & UART_LSR_THRE))
97 		;
98 	serial_dout(&com_port->thr, ch);
99 }
100 
101 DEBUG_UART_FUNCS
102 
103 #endif
104 
105 #if CONFIG_IS_ENABLED(DM_SERIAL)
106 
107 #if CONFIG_IS_ENABLED(OF_REAL)
omap_serial_of_to_plat(struct udevice * dev)108 static int omap_serial_of_to_plat(struct udevice *dev)
109 {
110 	struct ns16550_plat *plat = dev_get_plat(dev);
111 	fdt_addr_t addr;
112 	struct clk clk;
113 	int err;
114 
115 	/* try Processor Local Bus device first */
116 	addr = dev_read_addr(dev);
117 	if (addr == FDT_ADDR_T_NONE)
118 		return -EINVAL;
119 
120 	plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE);
121 
122 	plat->reg_offset = dev_read_u32_default(dev, "reg-offset", 0);
123 	plat->reg_shift = 2;
124 
125 	err = clk_get_by_index(dev, 0, &clk);
126 	if (!err) {
127 		err = clk_get_rate(&clk);
128 		if (!IS_ERR_VALUE(err))
129 			plat->clock = err;
130 	} else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
131 		debug("omap serial failed to get clock\n");
132 		return err;
133 	}
134 
135 	if (!plat->clock)
136 		plat->clock = dev_read_u32_default(dev, "clock-frequency",
137 						   CFG_SYS_NS16550_CLK);
138 	if (!plat->clock) {
139 		debug("omap serial clock not defined\n");
140 		return -EINVAL;
141 	}
142 
143 	plat->fcr = UART_FCR_DEFVAL;
144 
145 	return 0;
146 }
147 
148 static const struct udevice_id omap_serial_ids[] = {
149 	{ .compatible = "ti,omap2-uart", },
150 	{ .compatible = "ti,omap3-uart", },
151 	{ .compatible = "ti,omap4-uart", },
152 	{ .compatible = "ti,am3352-uart", },
153 	{ .compatible = "ti,am4372-uart", },
154 	{ .compatible = "ti,dra742-uart", },
155 	{ .compatible = "ti,am654-uart", },
156 	{}
157 };
158 #endif /* OF_REAL */
159 
omap_serial_calc_divisor(struct ns16550 * com_port,int clock,int baudrate)160 static int omap_serial_calc_divisor(struct ns16550 *com_port, int clock, int baudrate)
161 {
162 	unsigned int div_13, div_16;
163 	unsigned int abs_d13, abs_d16;
164 	/*
165 	 * The below logic sets the MDR1 register based on clock and baudrate.
166 	 */
167 	div_13 = DIV_ROUND_CLOSEST(clock, 13 * baudrate);
168 	div_16 = DIV_ROUND_CLOSEST(clock, 16 * baudrate);
169 
170 	if (!div_13)
171 		div_13 = 1;
172 	if (!div_16)
173 		div_16 = 1;
174 
175 	abs_d13 = abs(baudrate - clock / 13 / div_13);
176 	abs_d16 = abs(baudrate - clock / 16 / div_16);
177 
178 	if (abs_d13 >= abs_d16)
179 		serial_out(UART_OMAP_MDR1_16X_MODE, &com_port->mdr1);
180 	else
181 		serial_out(UART_OMAP_MDR1_13X_MODE, &com_port->mdr1);
182 
183 	return abs_d13 >= abs_d16 ? div_16 : div_13;
184 }
185 
omap_serial_setbrg(struct udevice * dev,int baudrate)186 static int omap_serial_setbrg(struct udevice *dev, int baudrate)
187 {
188 	struct ns16550 *const com_port = dev_get_priv(dev);
189 	struct ns16550_plat *plat = com_port->plat;
190 	int clock_divisor;
191 
192 	clock_divisor = omap_serial_calc_divisor(com_port, plat->clock, baudrate);
193 
194 	ns16550_setbrg(com_port, clock_divisor);
195 
196 	return 0;
197 }
198 
199 const struct dm_serial_ops omap_serial_ops = {
200 	.putc = ns16550_serial_putc,
201 	.pending = ns16550_serial_pending,
202 	.getc = ns16550_serial_getc,
203 	.setbrg = omap_serial_setbrg,
204 	.setconfig = ns16550_serial_setconfig,
205 	.getinfo = ns16550_serial_getinfo,
206 };
207 
208 #if CONFIG_IS_ENABLED(SERIAL_PRESENT)
209 U_BOOT_DRIVER(omap_serial) = {
210 	.name	= "omap_serial",
211 	.id	= UCLASS_SERIAL,
212 #if CONFIG_IS_ENABLED(OF_REAL)
213 	.of_match = omap_serial_ids,
214 	.of_to_plat = omap_serial_of_to_plat,
215 	.plat_auto	= sizeof(struct ns16550_plat),
216 #endif
217 	.priv_auto	= sizeof(struct ns16550),
218 	.probe = ns16550_serial_probe,
219 	.ops	= &omap_serial_ops,
220 #if !CONFIG_IS_ENABLED(OF_CONTROL)
221 	.flags	= DM_FLAG_PRE_RELOC,
222 #endif
223 };
224 #endif
225 #endif /* DM_SERIAL */
226