]>
Commit | Line | Data |
---|---|---|
e2842496 AP |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Anup Patel <anup@brainfault.org> | |
4 | */ | |
5 | ||
e2842496 | 6 | #include <common.h> |
b24f9057 | 7 | #include <clk.h> |
e2842496 AP |
8 | #include <debug_uart.h> |
9 | #include <dm.h> | |
10 | #include <errno.h> | |
11 | #include <fdtdec.h> | |
12 | #include <watchdog.h> | |
13 | #include <asm/io.h> | |
14 | #include <linux/compiler.h> | |
15 | #include <serial.h> | |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
19 | #define UART_TXFIFO_FULL 0x80000000 | |
20 | #define UART_RXFIFO_EMPTY 0x80000000 | |
21 | #define UART_RXFIFO_DATA 0x000000ff | |
22 | #define UART_TXCTRL_TXEN 0x1 | |
23 | #define UART_RXCTRL_RXEN 0x1 | |
24 | ||
25 | struct uart_sifive { | |
26 | u32 txfifo; | |
27 | u32 rxfifo; | |
28 | u32 txctrl; | |
29 | u32 rxctrl; | |
30 | u32 ie; | |
31 | u32 ip; | |
32 | u32 div; | |
33 | }; | |
34 | ||
35 | struct sifive_uart_platdata { | |
a3682008 | 36 | unsigned long clock; |
e2842496 AP |
37 | int saved_input_char; |
38 | struct uart_sifive *regs; | |
39 | }; | |
40 | ||
a3682008 AP |
41 | /** |
42 | * Find minimum divisor divides in_freq to max_target_hz; | |
43 | * Based on uart driver n SiFive FSBL. | |
44 | * | |
45 | * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1 | |
46 | * The nearest integer solution requires rounding up as to not exceed | |
47 | * max_target_hz. | |
48 | * div = ceil(f_in / f_baud) - 1 | |
49 | * = floor((f_in - 1 + f_baud) / f_baud) - 1 | |
50 | * This should not overflow as long as (f_in - 1 + f_baud) does not exceed | |
51 | * 2^32 - 1, which is unlikely since we represent frequencies in kHz. | |
52 | */ | |
53 | static inline unsigned int uart_min_clk_divisor(unsigned long in_freq, | |
54 | unsigned long max_target_hz) | |
55 | { | |
56 | unsigned long quotient = | |
57 | (in_freq + max_target_hz - 1) / (max_target_hz); | |
58 | /* Avoid underflow */ | |
59 | if (quotient == 0) | |
60 | return 0; | |
61 | else | |
62 | return quotient - 1; | |
63 | } | |
64 | ||
e2842496 AP |
65 | /* Set up the baud rate in gd struct */ |
66 | static void _sifive_serial_setbrg(struct uart_sifive *regs, | |
67 | unsigned long clock, unsigned long baud) | |
68 | { | |
a3682008 | 69 | writel((uart_min_clk_divisor(clock, baud)), ®s->div); |
e2842496 AP |
70 | } |
71 | ||
72 | static void _sifive_serial_init(struct uart_sifive *regs) | |
73 | { | |
74 | writel(UART_TXCTRL_TXEN, ®s->txctrl); | |
75 | writel(UART_RXCTRL_RXEN, ®s->rxctrl); | |
76 | writel(0, ®s->ie); | |
77 | } | |
78 | ||
79 | static int _sifive_serial_putc(struct uart_sifive *regs, const char c) | |
80 | { | |
81 | if (readl(®s->txfifo) & UART_TXFIFO_FULL) | |
82 | return -EAGAIN; | |
83 | ||
84 | writel(c, ®s->txfifo); | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | static int _sifive_serial_getc(struct uart_sifive *regs) | |
90 | { | |
91 | int ch = readl(®s->rxfifo); | |
92 | ||
93 | if (ch & UART_RXFIFO_EMPTY) | |
94 | return -EAGAIN; | |
95 | ch &= UART_RXFIFO_DATA; | |
96 | ||
97 | return (!ch) ? -EAGAIN : ch; | |
98 | } | |
99 | ||
100 | static int sifive_serial_setbrg(struct udevice *dev, int baudrate) | |
101 | { | |
ee0633ef | 102 | int ret; |
e2842496 AP |
103 | struct clk clk; |
104 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
ee0633ef | 105 | u32 clock = 0; |
e2842496 | 106 | |
ee0633ef AP |
107 | ret = clk_get_by_index(dev, 0, &clk); |
108 | if (IS_ERR_VALUE(ret)) { | |
e2842496 | 109 | debug("SiFive UART failed to get clock\n"); |
ee0633ef AP |
110 | ret = dev_read_u32(dev, "clock-frequency", &clock); |
111 | if (IS_ERR_VALUE(ret)) { | |
112 | debug("SiFive UART clock not defined\n"); | |
113 | return 0; | |
114 | } | |
115 | } else { | |
116 | clock = clk_get_rate(&clk); | |
117 | if (IS_ERR_VALUE(clock)) { | |
118 | debug("SiFive UART clock get rate failed\n"); | |
119 | return 0; | |
120 | } | |
e2842496 | 121 | } |
ee0633ef | 122 | platdata->clock = clock; |
e2842496 AP |
123 | _sifive_serial_setbrg(platdata->regs, platdata->clock, baudrate); |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static int sifive_serial_probe(struct udevice *dev) | |
129 | { | |
130 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
131 | ||
132 | /* No need to reinitialize the UART after relocation */ | |
133 | if (gd->flags & GD_FLG_RELOC) | |
134 | return 0; | |
135 | ||
136 | platdata->saved_input_char = 0; | |
137 | _sifive_serial_init(platdata->regs); | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | static int sifive_serial_getc(struct udevice *dev) | |
143 | { | |
144 | int c; | |
145 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
146 | struct uart_sifive *regs = platdata->regs; | |
147 | ||
148 | if (platdata->saved_input_char > 0) { | |
149 | c = platdata->saved_input_char; | |
150 | platdata->saved_input_char = 0; | |
151 | return c; | |
152 | } | |
153 | ||
154 | while ((c = _sifive_serial_getc(regs)) == -EAGAIN) ; | |
155 | ||
156 | return c; | |
157 | } | |
158 | ||
159 | static int sifive_serial_putc(struct udevice *dev, const char ch) | |
160 | { | |
161 | int rc; | |
162 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
163 | ||
164 | while ((rc = _sifive_serial_putc(platdata->regs, ch)) == -EAGAIN) ; | |
165 | ||
166 | return rc; | |
167 | } | |
168 | ||
169 | static int sifive_serial_pending(struct udevice *dev, bool input) | |
170 | { | |
171 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
172 | struct uart_sifive *regs = platdata->regs; | |
173 | ||
174 | if (input) { | |
175 | if (platdata->saved_input_char > 0) | |
176 | return 1; | |
177 | platdata->saved_input_char = _sifive_serial_getc(regs); | |
178 | return (platdata->saved_input_char > 0) ? 1 : 0; | |
179 | } else { | |
180 | return !!(readl(®s->txfifo) & UART_TXFIFO_FULL); | |
181 | } | |
182 | } | |
183 | ||
184 | static int sifive_serial_ofdata_to_platdata(struct udevice *dev) | |
185 | { | |
186 | struct sifive_uart_platdata *platdata = dev_get_platdata(dev); | |
187 | ||
188 | platdata->regs = (struct uart_sifive *)dev_read_addr(dev); | |
189 | if (IS_ERR(platdata->regs)) | |
190 | return PTR_ERR(platdata->regs); | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static const struct dm_serial_ops sifive_serial_ops = { | |
196 | .putc = sifive_serial_putc, | |
197 | .getc = sifive_serial_getc, | |
198 | .pending = sifive_serial_pending, | |
199 | .setbrg = sifive_serial_setbrg, | |
200 | }; | |
201 | ||
202 | static const struct udevice_id sifive_serial_ids[] = { | |
203 | { .compatible = "sifive,uart0" }, | |
204 | { } | |
205 | }; | |
206 | ||
207 | U_BOOT_DRIVER(serial_sifive) = { | |
208 | .name = "serial_sifive", | |
209 | .id = UCLASS_SERIAL, | |
210 | .of_match = sifive_serial_ids, | |
211 | .ofdata_to_platdata = sifive_serial_ofdata_to_platdata, | |
212 | .platdata_auto_alloc_size = sizeof(struct sifive_uart_platdata), | |
213 | .probe = sifive_serial_probe, | |
214 | .ops = &sifive_serial_ops, | |
215 | }; | |
216 | ||
217 | #ifdef CONFIG_DEBUG_UART_SIFIVE | |
218 | static inline void _debug_uart_init(void) | |
219 | { | |
220 | struct uart_sifive *regs = | |
221 | (struct uart_sifive *)CONFIG_DEBUG_UART_BASE; | |
222 | ||
223 | _sifive_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK, | |
224 | CONFIG_BAUDRATE); | |
225 | _sifive_serial_init(regs); | |
226 | } | |
227 | ||
228 | static inline void _debug_uart_putc(int ch) | |
229 | { | |
230 | struct uart_sifive *regs = | |
231 | (struct uart_sifive *)CONFIG_DEBUG_UART_BASE; | |
232 | ||
233 | while (_sifive_serial_putc(regs, ch) == -EAGAIN) | |
234 | WATCHDOG_RESET(); | |
235 | } | |
236 | ||
237 | DEBUG_UART_FUNCS | |
238 | ||
239 | #endif |