Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
bfcef28a BG |
2 | /* |
3 | * (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com> | |
bfcef28a BG |
4 | */ |
5 | ||
bfcef28a BG |
6 | #include <dm.h> |
7 | #include <errno.h> | |
8 | #include <fdtdec.h> | |
66006f86 | 9 | #include <linux/kernel.h> |
cd93d625 | 10 | #include <linux/bitops.h> |
bfcef28a BG |
11 | #include <linux/compiler.h> |
12 | #include <serial.h> | |
66006f86 | 13 | #include <clk.h> |
bfcef28a | 14 | |
bfcef28a BG |
15 | struct meson_uart { |
16 | u32 wfifo; | |
17 | u32 rfifo; | |
18 | u32 control; | |
19 | u32 status; | |
20 | u32 misc; | |
66006f86 | 21 | u32 reg5; /* New baud control register */ |
bfcef28a BG |
22 | }; |
23 | ||
8a8d24bd | 24 | struct meson_serial_plat { |
bfcef28a BG |
25 | struct meson_uart *reg; |
26 | }; | |
27 | ||
28 | /* AML_UART_STATUS bits */ | |
29 | #define AML_UART_PARITY_ERR BIT(16) | |
30 | #define AML_UART_FRAME_ERR BIT(17) | |
31 | #define AML_UART_TX_FIFO_WERR BIT(18) | |
32 | #define AML_UART_RX_EMPTY BIT(20) | |
33 | #define AML_UART_TX_FULL BIT(21) | |
34 | #define AML_UART_TX_EMPTY BIT(22) | |
35 | #define AML_UART_XMIT_BUSY BIT(25) | |
36 | #define AML_UART_ERR (AML_UART_PARITY_ERR | \ | |
37 | AML_UART_FRAME_ERR | \ | |
38 | AML_UART_TX_FIFO_WERR) | |
39 | ||
40 | /* AML_UART_CONTROL bits */ | |
41 | #define AML_UART_TX_EN BIT(12) | |
42 | #define AML_UART_RX_EN BIT(13) | |
43 | #define AML_UART_TX_RST BIT(22) | |
44 | #define AML_UART_RX_RST BIT(23) | |
45 | #define AML_UART_CLR_ERR BIT(24) | |
46 | ||
66006f86 ET |
47 | /* AML_UART_REG5 bits */ |
48 | #define AML_UART_REG5_XTAL_DIV2 BIT(27) | |
49 | #define AML_UART_REG5_XTAL_CLK_SEL BIT(26) /* default 0 (div by 3), 1 for no div */ | |
50 | #define AML_UART_REG5_USE_XTAL_CLK BIT(24) /* default 1 (use crystal as clock source) */ | |
51 | #define AML_UART_REG5_USE_NEW_BAUD BIT(23) /* default 1 (use new baud rate register) */ | |
52 | #define AML_UART_REG5_BAUD_MASK 0x7fffff | |
53 | ||
54 | static u32 meson_calc_baud_divisor(ulong src_rate, u32 baud) | |
55 | { | |
56 | /* | |
57 | * Usually src_rate is 24 MHz (from crystal) as clock source for serial | |
58 | * device. Since 8 Mb/s is the maximum supported baud rate, use div by 3 | |
59 | * to derive baud rate. This choice is used also in meson_serial_setbrg. | |
60 | */ | |
61 | return DIV_ROUND_CLOSEST(src_rate / 3, baud) - 1; | |
62 | } | |
63 | ||
64 | static void meson_serial_set_baud(struct meson_uart *uart, ulong src_rate, u32 baud) | |
65 | { | |
66 | /* | |
67 | * Set crystal divided by 3 (regardless of device tree clock property) | |
68 | * as clock source and the corresponding divisor to approximate baud | |
69 | */ | |
70 | u32 divisor = meson_calc_baud_divisor(src_rate, baud); | |
71 | u32 val = AML_UART_REG5_USE_XTAL_CLK | AML_UART_REG5_USE_NEW_BAUD | | |
72 | (divisor & AML_UART_REG5_BAUD_MASK); | |
73 | writel(val, &uart->reg5); | |
74 | } | |
75 | ||
bfcef28a BG |
76 | static void meson_serial_init(struct meson_uart *uart) |
77 | { | |
78 | u32 val; | |
79 | ||
80 | val = readl(&uart->control); | |
81 | val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); | |
82 | writel(val, &uart->control); | |
83 | val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); | |
84 | writel(val, &uart->control); | |
85 | val |= (AML_UART_RX_EN | AML_UART_TX_EN); | |
86 | writel(val, &uart->control); | |
87 | } | |
88 | ||
89 | static int meson_serial_probe(struct udevice *dev) | |
90 | { | |
0fd3d911 | 91 | struct meson_serial_plat *plat = dev_get_plat(dev); |
bfcef28a | 92 | struct meson_uart *const uart = plat->reg; |
66006f86 ET |
93 | struct clk per_clk; |
94 | int ret = clk_get_by_name(dev, "baud", &per_clk); | |
95 | ||
96 | if (ret) | |
97 | return ret; | |
98 | ulong rate = clk_get_rate(&per_clk); | |
bfcef28a | 99 | |
66006f86 | 100 | meson_serial_set_baud(uart, rate, CONFIG_BAUDRATE); |
bfcef28a BG |
101 | meson_serial_init(uart); |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
812196d5 NA |
106 | static void meson_serial_rx_error(struct udevice *dev) |
107 | { | |
0fd3d911 | 108 | struct meson_serial_plat *plat = dev_get_plat(dev); |
812196d5 NA |
109 | struct meson_uart *const uart = plat->reg; |
110 | u32 val = readl(&uart->control); | |
111 | ||
112 | /* Clear error */ | |
113 | val |= AML_UART_CLR_ERR; | |
114 | writel(val, &uart->control); | |
115 | val &= ~AML_UART_CLR_ERR; | |
116 | writel(val, &uart->control); | |
117 | ||
118 | /* Remove spurious byte from fifo */ | |
119 | readl(&uart->rfifo); | |
120 | } | |
121 | ||
bfcef28a BG |
122 | static int meson_serial_getc(struct udevice *dev) |
123 | { | |
0fd3d911 | 124 | struct meson_serial_plat *plat = dev_get_plat(dev); |
bfcef28a | 125 | struct meson_uart *const uart = plat->reg; |
812196d5 | 126 | uint32_t status = readl(&uart->status); |
bfcef28a | 127 | |
812196d5 | 128 | if (status & AML_UART_RX_EMPTY) |
bfcef28a BG |
129 | return -EAGAIN; |
130 | ||
812196d5 NA |
131 | if (status & AML_UART_ERR) { |
132 | meson_serial_rx_error(dev); | |
133 | return -EIO; | |
134 | } | |
135 | ||
bfcef28a BG |
136 | return readl(&uart->rfifo) & 0xff; |
137 | } | |
138 | ||
139 | static int meson_serial_putc(struct udevice *dev, const char ch) | |
140 | { | |
0fd3d911 | 141 | struct meson_serial_plat *plat = dev_get_plat(dev); |
bfcef28a BG |
142 | struct meson_uart *const uart = plat->reg; |
143 | ||
144 | if (readl(&uart->status) & AML_UART_TX_FULL) | |
145 | return -EAGAIN; | |
146 | ||
147 | writel(ch, &uart->wfifo); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
66006f86 ET |
152 | static int meson_serial_setbrg(struct udevice *dev, const int baud) |
153 | { | |
154 | /* | |
155 | * Change device baud rate if baud is reasonable (considering a 23 bit | |
156 | * counter with an 8 MHz clock input) and the actual baud | |
157 | * rate is within 2% of the requested value (2% is arbitrary). | |
158 | */ | |
159 | if (baud < 1 || baud > 8000000) | |
160 | return -EINVAL; | |
161 | ||
162 | struct meson_serial_plat *const plat = dev_get_plat(dev); | |
163 | struct meson_uart *const uart = plat->reg; | |
164 | struct clk per_clk; | |
165 | int ret = clk_get_by_name(dev, "baud", &per_clk); | |
166 | ||
167 | if (ret) | |
168 | return ret; | |
169 | ulong rate = clk_get_rate(&per_clk); | |
170 | u32 divisor = meson_calc_baud_divisor(rate, baud); | |
171 | u32 calc_baud = (rate / 3) / (divisor + 1); | |
172 | u32 calc_err = baud > calc_baud ? baud - calc_baud : calc_baud - baud; | |
173 | ||
174 | if (((calc_err * 100) / baud) > 2) | |
175 | return -EINVAL; | |
176 | ||
177 | meson_serial_set_baud(uart, rate, baud); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
bfcef28a BG |
182 | static int meson_serial_pending(struct udevice *dev, bool input) |
183 | { | |
0fd3d911 | 184 | struct meson_serial_plat *plat = dev_get_plat(dev); |
bfcef28a BG |
185 | struct meson_uart *const uart = plat->reg; |
186 | uint32_t status = readl(&uart->status); | |
187 | ||
812196d5 NA |
188 | if (input) { |
189 | if (status & AML_UART_RX_EMPTY) | |
190 | return false; | |
191 | ||
192 | /* | |
193 | * Handle and drop any RX error here to avoid | |
194 | * returning true here when an error byte is in the FIFO | |
195 | */ | |
196 | if (status & AML_UART_ERR) { | |
197 | meson_serial_rx_error(dev); | |
198 | return false; | |
199 | } | |
200 | ||
201 | return true; | |
202 | } else { | |
afa85a22 MK |
203 | if (status & AML_UART_TX_EMPTY) |
204 | return false; | |
205 | ||
206 | return true; | |
812196d5 | 207 | } |
bfcef28a BG |
208 | } |
209 | ||
d1998a9f | 210 | static int meson_serial_of_to_plat(struct udevice *dev) |
bfcef28a | 211 | { |
0fd3d911 | 212 | struct meson_serial_plat *plat = dev_get_plat(dev); |
bfcef28a BG |
213 | fdt_addr_t addr; |
214 | ||
2548493a | 215 | addr = dev_read_addr(dev); |
bfcef28a BG |
216 | if (addr == FDT_ADDR_T_NONE) |
217 | return -EINVAL; | |
218 | ||
219 | plat->reg = (struct meson_uart *)addr; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static const struct dm_serial_ops meson_serial_ops = { | |
225 | .putc = meson_serial_putc, | |
226 | .pending = meson_serial_pending, | |
227 | .getc = meson_serial_getc, | |
66006f86 | 228 | .setbrg = meson_serial_setbrg, |
bfcef28a BG |
229 | }; |
230 | ||
231 | static const struct udevice_id meson_serial_ids[] = { | |
232 | { .compatible = "amlogic,meson-uart" }, | |
e1e1e852 | 233 | { .compatible = "amlogic,meson-gx-uart" }, |
43a0b2cb | 234 | { .compatible = "amlogic,meson-a1-uart" }, |
bfcef28a BG |
235 | { } |
236 | }; | |
237 | ||
238 | U_BOOT_DRIVER(serial_meson) = { | |
239 | .name = "serial_meson", | |
240 | .id = UCLASS_SERIAL, | |
241 | .of_match = meson_serial_ids, | |
242 | .probe = meson_serial_probe, | |
243 | .ops = &meson_serial_ops, | |
d1998a9f | 244 | .of_to_plat = meson_serial_of_to_plat, |
8a8d24bd | 245 | .plat_auto = sizeof(struct meson_serial_plat), |
bfcef28a BG |
246 | }; |
247 | ||
248 | #ifdef CONFIG_DEBUG_UART_MESON | |
249 | ||
250 | #include <debug_uart.h> | |
251 | ||
252 | static inline void _debug_uart_init(void) | |
253 | { | |
254 | } | |
255 | ||
256 | static inline void _debug_uart_putc(int ch) | |
257 | { | |
b62450cf | 258 | struct meson_uart *regs = (struct meson_uart *)CONFIG_VAL(DEBUG_UART_BASE); |
bfcef28a BG |
259 | |
260 | while (readl(®s->status) & AML_UART_TX_FULL) | |
261 | ; | |
262 | ||
263 | writel(ch, ®s->wfifo); | |
264 | } | |
265 | ||
266 | DEBUG_UART_FUNCS | |
267 | ||
268 | #endif |