Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / serial / serial_mvebu_a3700.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Stefan Roese <sr@denx.de>
4  * Copyright (C) 2021 Pali Rohár <pali@kernel.org>
5  */
6
7 #include <common.h>
8 #include <clk.h>
9 #include <dm.h>
10 #include <serial.h>
11 #include <asm/io.h>
12 #include <asm/arch/cpu.h>
13 #include <mach/soc.h>
14
15 struct mvebu_plat {
16         void __iomem *base;
17         ulong tbg_rate;
18         u8 tbg_idx;
19 };
20
21 /*
22  * Register offset
23  */
24 #define UART_RX_REG             0x00
25 #define UART_TX_REG             0x04
26 #define UART_CTRL_REG           0x08
27 #define UART_STATUS_REG         0x0c
28 #define UART_BAUD_REG           0x10
29 #define UART_POSSR_REG          0x14
30
31 #define UART_STATUS_RX_RDY      0x10
32 #define UART_STATUS_TX_EMPTY    0x40
33 #define UART_STATUS_TXFIFO_FULL 0x800
34
35 #define UART_CTRL_RXFIFO_RESET  0x4000
36 #define UART_CTRL_TXFIFO_RESET  0x8000
37
38 static int mvebu_serial_putc(struct udevice *dev, const char ch)
39 {
40         struct mvebu_plat *plat = dev_get_plat(dev);
41         void __iomem *base = plat->base;
42
43         if (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
44                 return -EAGAIN;
45
46         writel(ch, base + UART_TX_REG);
47
48         return 0;
49 }
50
51 static int mvebu_serial_getc(struct udevice *dev)
52 {
53         struct mvebu_plat *plat = dev_get_plat(dev);
54         void __iomem *base = plat->base;
55
56         if (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY))
57                 return -EAGAIN;
58
59         return readl(base + UART_RX_REG) & 0xff;
60 }
61
62 static int mvebu_serial_pending(struct udevice *dev, bool input)
63 {
64         struct mvebu_plat *plat = dev_get_plat(dev);
65         void __iomem *base = plat->base;
66
67         if (input) {
68                 if (readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY)
69                         return 1;
70         } else {
71                 if (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
72                         return 1;
73         }
74
75         return 0;
76 }
77
78 static int mvebu_serial_setbrg(struct udevice *dev, int baudrate)
79 {
80         struct mvebu_plat *plat = dev_get_plat(dev);
81         void __iomem *base = plat->base;
82         u32 divider, d1, d2;
83         u32 oversampling;
84
85         /*
86          * Calculate divider
87          * baudrate = clock / 16 / divider
88          */
89         d1 = d2 = 1;
90         divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * 16 * d1 * d2);
91
92         /*
93          * Set Programmable Oversampling Stack to 0,
94          * UART defaults to 16x scheme
95          */
96         oversampling = 0;
97
98         if (divider < 1)
99                 divider = 1;
100         else if (divider > 1023) {
101                 /*
102                  * If divider is too high for selected baudrate then set
103                  * divider d1 to the maximal value 6.
104                  */
105                 d1 = 6;
106                 divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
107                                             baudrate * 16 * d1 * d2);
108                 if (divider < 1)
109                         divider = 1;
110                 else if (divider > 1023) {
111                         /*
112                          * If divider is still too high then set also divider
113                          * d2 to the maximal value 6.
114                          */
115                         d2 = 6;
116                         divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
117                                                     baudrate * 16 * d1 * d2);
118                         if (divider < 1)
119                                 divider = 1;
120                         else if (divider > 1023) {
121                                 /*
122                                  * And if divider is still to high then
123                                  * use oversampling with maximal factor 63.
124                                  */
125                                 oversampling = (63 << 0) | (63 << 8) |
126                                               (63 << 16) | (63 << 24);
127                                 divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
128                                                 baudrate * 63 * d1 * d2);
129                                 if (divider < 1)
130                                         divider = 1;
131                                 else if (divider > 1023)
132                                         divider = 1023;
133                         }
134                 }
135         }
136
137         divider |= BIT(19); /* Do not use XTAL as a base clock */
138         divider |= d1 << 15; /* Set d1 divider */
139         divider |= d2 << 12; /* Set d2 divider */
140         divider |= plat->tbg_idx << 10; /* Use selected TBG as a base clock */
141
142         while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
143                 ;
144         writel(divider, base + UART_BAUD_REG);
145         writel(oversampling, base + UART_POSSR_REG);
146
147         return 0;
148 }
149
150 static int mvebu_serial_probe(struct udevice *dev)
151 {
152         struct mvebu_plat *plat = dev_get_plat(dev);
153         void __iomem *base = plat->base;
154         struct udevice *nb_clk;
155         ofnode nb_clk_node;
156         int i, res;
157
158         nb_clk_node = ofnode_by_compatible(ofnode_null(),
159                                            "marvell,armada-3700-periph-clock-nb");
160         if (!ofnode_valid(nb_clk_node)) {
161                 printf("%s: NB periph clock node not available\n", __func__);
162                 return -ENODEV;
163         }
164
165         res = device_get_global_by_ofnode(nb_clk_node, &nb_clk);
166         if (res) {
167                 printf("%s: Cannot get NB periph clock\n", __func__);
168                 return res;
169         }
170
171         /*
172          * Choose the TBG clock with lowest frequency which allows to configure
173          * UART also at lower baudrates.
174          */
175         for (i = 0; i < 4; i++) {
176                 struct clk clk;
177                 ulong rate;
178
179                 res = clk_get_by_index_nodev(nb_clk_node, i, &clk);
180                 if (res) {
181                         printf("%s: Cannot get TBG clock %i: %i\n", __func__,
182                                i, res);
183                         return -ENODEV;
184                 }
185
186                 rate = clk_get_rate(&clk);
187                 if (!rate || IS_ERR_VALUE(rate)) {
188                         printf("%s: Cannot get rate for TBG clock %i\n",
189                                __func__, i);
190                         return -EINVAL;
191                 }
192
193                 if (!i || plat->tbg_rate > rate) {
194                         plat->tbg_rate = rate;
195                         plat->tbg_idx = i;
196                 }
197         }
198
199         /* reset FIFOs */
200         writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
201                base + UART_CTRL_REG);
202
203         /* No Parity, 1 Stop */
204         writel(0, base + UART_CTRL_REG);
205
206         return 0;
207 }
208
209 static int mvebu_serial_remove(struct udevice *dev)
210 {
211         struct mvebu_plat *plat = dev_get_plat(dev);
212         void __iomem *base = plat->base;
213         ulong new_parent_rate, parent_rate;
214         u32 new_divider, divider;
215         u32 new_oversampling;
216         u32 oversampling;
217         u32 d1, d2;
218         u32 nb_rst;
219
220         /*
221          * Switch UART base clock back to XTAL because older Linux kernel
222          * expects it. Otherwise it does not calculate UART divisor correctly
223          * and therefore UART does not work in kernel.
224          */
225         divider = readl(base + UART_BAUD_REG);
226         if (!(divider & BIT(19))) /* UART already uses XTAL */
227                 return 0;
228
229         /* Read current divisors settings */
230         d1 = (divider >> 15) & 7;
231         d2 = (divider >> 12) & 7;
232         parent_rate = plat->tbg_rate;
233         divider &= 1023;
234         oversampling = readl(base + UART_POSSR_REG) & 63;
235         if (!oversampling)
236                 oversampling = 16;
237
238         /* Calculate new divisor against XTAL clock without changing baudrate */
239         new_oversampling = 0;
240         new_parent_rate = get_ref_clk() * 1000000;
241         new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 *
242                                         oversampling, parent_rate * 16);
243
244         /*
245          * UART does not work reliably when XTAL divisor is smaller than 4.
246          * In this case we do not switch UART parent to XTAL. User either
247          * configured unsupported settings or has newer kernel with patches
248          * which allow usage of non-XTAL clock as a parent clock.
249          */
250         if (new_divider < 4)
251                 return 0;
252
253         /*
254          * If new divisor is larger than maximal supported, try to switch
255          * from default x16 scheme to oversampling with maximal factor 63.
256          */
257         if (new_divider > 1023) {
258                 new_oversampling = 63;
259                 new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 *
260                                                 d2 * oversampling,
261                                                 parent_rate * new_oversampling);
262                 if (new_divider < 4 || new_divider > 1023)
263                         return 0;
264         }
265
266         /* wait until TX empty */
267         while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
268                 ;
269
270         /* external reset of UART via North Bridge Peripheral */
271         nb_rst = readl(MVEBU_REGISTER(0x12400));
272         writel(nb_rst & ~BIT(3), MVEBU_REGISTER(0x12400));
273         writel(nb_rst | BIT(3), MVEBU_REGISTER(0x12400));
274
275         /* set baudrate and oversampling */
276         writel(new_divider, base + UART_BAUD_REG);
277         writel(new_oversampling, base + UART_POSSR_REG);
278
279         /* No Parity, 1 Stop */
280         writel(0, base + UART_CTRL_REG);
281
282         return 0;
283 }
284
285 static int mvebu_serial_of_to_plat(struct udevice *dev)
286 {
287         struct mvebu_plat *plat = dev_get_plat(dev);
288
289         plat->base = dev_read_addr_ptr(dev);
290
291         return 0;
292 }
293
294 static const struct dm_serial_ops mvebu_serial_ops = {
295         .putc = mvebu_serial_putc,
296         .pending = mvebu_serial_pending,
297         .getc = mvebu_serial_getc,
298         .setbrg = mvebu_serial_setbrg,
299 };
300
301 static const struct udevice_id mvebu_serial_ids[] = {
302         { .compatible = "marvell,armada-3700-uart" },
303         { }
304 };
305
306 U_BOOT_DRIVER(serial_mvebu) = {
307         .name   = "serial_mvebu",
308         .id     = UCLASS_SERIAL,
309         .of_match = mvebu_serial_ids,
310         .of_to_plat = mvebu_serial_of_to_plat,
311         .plat_auto      = sizeof(struct mvebu_plat),
312         .probe  = mvebu_serial_probe,
313         .remove = mvebu_serial_remove,
314         .flags  = DM_FLAG_OS_PREPARE,
315         .ops    = &mvebu_serial_ops,
316 };
317
318 #ifdef CONFIG_DEBUG_MVEBU_A3700_UART
319
320 #include <debug_uart.h>
321
322 static inline void _debug_uart_init(void)
323 {
324         void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
325         u32 parent_rate, divider;
326
327         /* reset FIFOs */
328         writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
329                base + UART_CTRL_REG);
330
331         /* No Parity, 1 Stop */
332         writel(0, base + UART_CTRL_REG);
333
334         /*
335          * Calculate divider
336          * baudrate = clock / 16 / divider
337          */
338         parent_rate = (readl(MVEBU_REGISTER(0x13808)) & BIT(9)) ?
339                       40000000 : 25000000;
340         divider = DIV_ROUND_CLOSEST(parent_rate, CONFIG_BAUDRATE * 16);
341         writel(divider, base + UART_BAUD_REG);
342
343         /*
344          * Set Programmable Oversampling Stack to 0,
345          * UART defaults to 16x scheme
346          */
347         writel(0, base + UART_POSSR_REG);
348 }
349
350 static inline void _debug_uart_putc(int ch)
351 {
352         void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
353
354         while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
355                 ;
356
357         writel(ch, base + UART_TX_REG);
358 }
359
360 DEBUG_UART_FUNCS
361 #endif