blob: 52dc3fdad7b458be076f0d8dc2dda7455854ce33 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stefan Roese6985d492016-05-17 16:36:00 +02002/*
3 * Copyright (C) 2016 Stefan Roese <sr@denx.de>
Stefan Roese6985d492016-05-17 16:36:00 +02004 */
5
6#include <common.h>
Pali Rohár5f41bab2021-05-25 19:42:40 +02007#include <clk.h>
Stefan Roese6985d492016-05-17 16:36:00 +02008#include <dm.h>
9#include <serial.h>
10#include <asm/io.h>
Pali Rohár139d0812021-05-25 19:42:38 +020011#include <asm/arch/cpu.h>
Stefan Roese6985d492016-05-17 16:36:00 +020012
Simon Glass8a8d24b2020-12-03 16:55:23 -070013struct mvebu_plat {
Stefan Roese6985d492016-05-17 16:36:00 +020014 void __iomem *base;
Pali Rohár5f41bab2021-05-25 19:42:40 +020015 ulong tbg_rate;
16 u8 tbg_idx;
Stefan Roese6985d492016-05-17 16:36:00 +020017};
18
19/*
20 * Register offset
21 */
22#define UART_RX_REG 0x00
23#define UART_TX_REG 0x04
24#define UART_CTRL_REG 0x08
25#define UART_STATUS_REG 0x0c
26#define UART_BAUD_REG 0x10
27#define UART_POSSR_REG 0x14
28
29#define UART_STATUS_RX_RDY 0x10
Pali Roháraea2f722021-01-14 15:46:35 +010030#define UART_STATUS_TX_EMPTY 0x40
Stefan Roese6985d492016-05-17 16:36:00 +020031#define UART_STATUS_TXFIFO_FULL 0x800
32
33#define UART_CTRL_RXFIFO_RESET 0x4000
34#define UART_CTRL_TXFIFO_RESET 0x8000
35
Stefan Roese6985d492016-05-17 16:36:00 +020036static int mvebu_serial_putc(struct udevice *dev, const char ch)
37{
Simon Glass8a8d24b2020-12-03 16:55:23 -070038 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +020039 void __iomem *base = plat->base;
40
41 while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
42 ;
43
44 writel(ch, base + UART_TX_REG);
45
46 return 0;
47}
48
49static int mvebu_serial_getc(struct udevice *dev)
50{
Simon Glass8a8d24b2020-12-03 16:55:23 -070051 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +020052 void __iomem *base = plat->base;
53
54 while (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY))
55 ;
56
57 return readl(base + UART_RX_REG) & 0xff;
58}
59
60static int mvebu_serial_pending(struct udevice *dev, bool input)
61{
Simon Glass8a8d24b2020-12-03 16:55:23 -070062 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +020063 void __iomem *base = plat->base;
64
Pali Roháraea2f722021-01-14 15:46:35 +010065 if (input) {
66 if (readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY)
67 return 1;
68 } else {
69 if (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
70 return 1;
71 }
Stefan Roese6985d492016-05-17 16:36:00 +020072
73 return 0;
74}
75
76static int mvebu_serial_setbrg(struct udevice *dev, int baudrate)
77{
Simon Glass8a8d24b2020-12-03 16:55:23 -070078 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +020079 void __iomem *base = plat->base;
Pali Rohár5f41bab2021-05-25 19:42:40 +020080 u32 divider, d1, d2;
81 u32 oversampling;
Stefan Roese6985d492016-05-17 16:36:00 +020082
83 /*
84 * Calculate divider
85 * baudrate = clock / 16 / divider
86 */
Pali Rohár5f41bab2021-05-25 19:42:40 +020087 d1 = d2 = 1;
88 divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * 16 * d1 * d2);
Stefan Roese6985d492016-05-17 16:36:00 +020089
90 /*
91 * Set Programmable Oversampling Stack to 0,
92 * UART defaults to 16x scheme
93 */
Pali Rohár5f41bab2021-05-25 19:42:40 +020094 oversampling = 0;
95
96 if (divider < 1)
97 divider = 1;
98 else if (divider > 1023) {
99 /*
100 * If divider is too high for selected baudrate then set
101 * divider d1 to the maximal value 6.
102 */
103 d1 = 6;
104 divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
105 baudrate * 16 * d1 * d2);
106 if (divider < 1)
107 divider = 1;
108 else if (divider > 1023) {
109 /*
110 * If divider is still too high then set also divider
111 * d2 to the maximal value 6.
112 */
113 d2 = 6;
114 divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
115 baudrate * 16 * d1 * d2);
116 if (divider < 1)
117 divider = 1;
118 else if (divider > 1023) {
119 /*
120 * And if divider is still to high then
121 * use oversampling with maximal factor 63.
122 */
123 oversampling = (63 << 0) | (63 << 8) |
124 (63 << 16) | (63 << 24);
125 divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
126 baudrate * 63 * d1 * d2);
127 if (divider < 1)
128 divider = 1;
129 else if (divider > 1023)
130 divider = 1023;
131 }
132 }
133 }
134
135 divider |= BIT(19); /* Do not use XTAL as a base clock */
136 divider |= d1 << 15; /* Set d1 divider */
137 divider |= d2 << 12; /* Set d2 divider */
138 divider |= plat->tbg_idx << 10; /* Use selected TBG as a base clock */
139
140 while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
141 ;
142 writel(divider, base + UART_BAUD_REG);
143 writel(oversampling, base + UART_POSSR_REG);
Stefan Roese6985d492016-05-17 16:36:00 +0200144
145 return 0;
146}
147
148static int mvebu_serial_probe(struct udevice *dev)
149{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700150 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +0200151 void __iomem *base = plat->base;
Pali Rohár5f41bab2021-05-25 19:42:40 +0200152 struct udevice *nb_clk;
153 ofnode nb_clk_node;
154 int i, res;
155
156 nb_clk_node = ofnode_by_compatible(ofnode_null(),
157 "marvell,armada-3700-periph-clock-nb");
158 if (!ofnode_valid(nb_clk_node)) {
159 printf("%s: NB periph clock node not available\n", __func__);
160 return -ENODEV;
161 }
162
163 res = device_get_global_by_ofnode(nb_clk_node, &nb_clk);
164 if (res) {
165 printf("%s: Cannot get NB periph clock\n", __func__);
166 return res;
167 }
168
169 /*
170 * Choose the TBG clock with lowest frequency which allows to configure
171 * UART also at lower baudrates.
172 */
173 for (i = 0; i < 4; i++) {
174 struct clk clk;
175 ulong rate;
176
177 res = clk_get_by_index_nodev(nb_clk_node, i, &clk);
178 if (res) {
179 printf("%s: Cannot get TBG clock %i: %i\n", __func__,
180 i, res);
181 return -ENODEV;
182 }
183
184 rate = clk_get_rate(&clk);
185 if (!rate || IS_ERR_VALUE(rate)) {
186 printf("%s: Cannot get rate for TBG clock %i\n",
187 __func__, i);
188 return -EINVAL;
189 }
190
191 if (!i || plat->tbg_rate > rate) {
192 plat->tbg_rate = rate;
193 plat->tbg_idx = i;
194 }
195 }
Stefan Roese6985d492016-05-17 16:36:00 +0200196
197 /* reset FIFOs */
198 writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
199 base + UART_CTRL_REG);
200
201 /* No Parity, 1 Stop */
202 writel(0, base + UART_CTRL_REG);
203
204 return 0;
205}
206
Pali Rohár82147282021-05-25 19:42:41 +0200207static int mvebu_serial_remove(struct udevice *dev)
208{
209 struct mvebu_plat *plat = dev_get_plat(dev);
210 void __iomem *base = plat->base;
211 ulong new_parent_rate, parent_rate;
212 u32 new_divider, divider;
213 u32 new_oversampling;
214 u32 oversampling;
215 u32 d1, d2;
216
217 /*
218 * Switch UART base clock back to XTAL because older Linux kernel
219 * expects it. Otherwise it does not calculate UART divisor correctly
220 * and therefore UART does not work in kernel.
221 */
222 divider = readl(base + UART_BAUD_REG);
223 if (!(divider & BIT(19))) /* UART already uses XTAL */
224 return 0;
225
226 /* Read current divisors settings */
227 d1 = (divider >> 15) & 7;
228 d2 = (divider >> 12) & 7;
229 parent_rate = plat->tbg_rate;
230 divider &= 1023;
231 oversampling = readl(base + UART_POSSR_REG) & 63;
232 if (!oversampling)
233 oversampling = 16;
234
235 /* Calculate new divisor against XTAL clock without changing baudrate */
236 new_oversampling = 0;
237 new_parent_rate = get_ref_clk() * 1000000;
238 new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 *
239 oversampling, parent_rate * 16);
240
241 /*
242 * UART does not work reliably when XTAL divisor is smaller than 4.
243 * In this case we do not switch UART parent to XTAL. User either
244 * configured unsupported settings or has newer kernel with patches
245 * which allow usage of non-XTAL clock as a parent clock.
246 */
247 if (new_divider < 4)
248 return 0;
249
250 /*
251 * If new divisor is larger than maximal supported, try to switch
252 * from default x16 scheme to oversampling with maximal factor 63.
253 */
254 if (new_divider > 1023) {
255 new_oversampling = 63;
256 new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 *
257 d2 * oversampling,
258 parent_rate * new_oversampling);
259 if (new_divider < 4 || new_divider > 1023)
260 return 0;
261 }
262
263 while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
264 ;
265
266 writel(new_divider, base + UART_BAUD_REG);
267 writel(new_oversampling, base + UART_POSSR_REG);
268
269 return 0;
270}
271
Simon Glassd1998a92020-12-03 16:55:21 -0700272static int mvebu_serial_of_to_plat(struct udevice *dev)
Stefan Roese6985d492016-05-17 16:36:00 +0200273{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700274 struct mvebu_plat *plat = dev_get_plat(dev);
Stefan Roese6985d492016-05-17 16:36:00 +0200275
Masahiro Yamada702e57e2020-08-04 14:14:43 +0900276 plat->base = dev_read_addr_ptr(dev);
Stefan Roese6985d492016-05-17 16:36:00 +0200277
278 return 0;
279}
280
281static const struct dm_serial_ops mvebu_serial_ops = {
282 .putc = mvebu_serial_putc,
283 .pending = mvebu_serial_pending,
284 .getc = mvebu_serial_getc,
285 .setbrg = mvebu_serial_setbrg,
286};
287
288static const struct udevice_id mvebu_serial_ids[] = {
289 { .compatible = "marvell,armada-3700-uart" },
290 { }
291};
292
293U_BOOT_DRIVER(serial_mvebu) = {
294 .name = "serial_mvebu",
295 .id = UCLASS_SERIAL,
296 .of_match = mvebu_serial_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700297 .of_to_plat = mvebu_serial_of_to_plat,
Simon Glass8a8d24b2020-12-03 16:55:23 -0700298 .plat_auto = sizeof(struct mvebu_plat),
Stefan Roese6985d492016-05-17 16:36:00 +0200299 .probe = mvebu_serial_probe,
Pali Rohár82147282021-05-25 19:42:41 +0200300 .remove = mvebu_serial_remove,
301 .flags = DM_FLAG_OS_PREPARE,
Stefan Roese6985d492016-05-17 16:36:00 +0200302 .ops = &mvebu_serial_ops,
Stefan Roese6985d492016-05-17 16:36:00 +0200303};
304
305#ifdef CONFIG_DEBUG_MVEBU_A3700_UART
306
307#include <debug_uart.h>
308
309static inline void _debug_uart_init(void)
310{
311 void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
Pali Rohár5cd424d2021-07-26 14:58:58 +0200312 u32 parent_rate, divider;
Stefan Roese6985d492016-05-17 16:36:00 +0200313
314 /* reset FIFOs */
315 writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
316 base + UART_CTRL_REG);
317
318 /* No Parity, 1 Stop */
319 writel(0, base + UART_CTRL_REG);
320
321 /*
322 * Calculate divider
323 * baudrate = clock / 16 / divider
324 */
Pali Rohár139d0812021-05-25 19:42:38 +0200325 parent_rate = get_ref_clk() * 1000000;
Pali Rohár5cd424d2021-07-26 14:58:58 +0200326 divider = DIV_ROUND_CLOSEST(parent_rate, CONFIG_BAUDRATE * 16);
Pali Rohár139d0812021-05-25 19:42:38 +0200327 writel(divider, base + UART_BAUD_REG);
Stefan Roese6985d492016-05-17 16:36:00 +0200328
329 /*
330 * Set Programmable Oversampling Stack to 0,
331 * UART defaults to 16x scheme
332 */
333 writel(0, base + UART_POSSR_REG);
334}
335
336static inline void _debug_uart_putc(int ch)
337{
338 void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
339
340 while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
341 ;
342
343 writel(ch, base + UART_TX_REG);
344}
345
346DEBUG_UART_FUNCS
347#endif