]>
Commit | Line | Data |
---|---|---|
60b49761 WW |
1 | /* |
2 | * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <div64.h> | |
10 | #include <errno.h> | |
11 | #include <serial.h> | |
12 | #include <asm/io.h> | |
13 | #include <asm/addrspace.h> | |
14 | #include <asm/types.h> | |
15 | #include <dm/pinctrl.h> | |
16 | #include <mach/ar71xx_regs.h> | |
17 | ||
18 | #define AR933X_UART_DATA_REG 0x00 | |
19 | #define AR933X_UART_CS_REG 0x04 | |
20 | #define AR933X_UART_CLK_REG 0x08 | |
21 | ||
22 | #define AR933X_UART_DATA_TX_RX_MASK 0xff | |
23 | #define AR933X_UART_DATA_RX_CSR BIT(8) | |
24 | #define AR933X_UART_DATA_TX_CSR BIT(9) | |
25 | #define AR933X_UART_CS_IF_MODE_S 2 | |
26 | #define AR933X_UART_CS_IF_MODE_M 0x3 | |
27 | #define AR933X_UART_CS_IF_MODE_DTE 1 | |
28 | #define AR933X_UART_CS_IF_MODE_DCE 2 | |
29 | #define AR933X_UART_CS_TX_RDY_ORIDE BIT(7) | |
30 | #define AR933X_UART_CS_RX_RDY_ORIDE BIT(8) | |
31 | #define AR933X_UART_CLK_STEP_M 0xffff | |
32 | #define AR933X_UART_CLK_SCALE_M 0xfff | |
33 | #define AR933X_UART_CLK_SCALE_S 16 | |
34 | #define AR933X_UART_CLK_STEP_S 0 | |
35 | ||
36 | struct ar933x_serial_priv { | |
37 | void __iomem *regs; | |
38 | }; | |
39 | ||
40 | /* | |
773f3b25 | 41 | * Baudrate algorithm come from Linux/drivers/tty/serial/ar933x_uart.c |
60b49761 WW |
42 | * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17)) |
43 | */ | |
44 | static u32 ar933x_serial_get_baud(u32 clk, u32 scale, u32 step) | |
45 | { | |
46 | u64 t; | |
47 | u32 div; | |
48 | ||
49 | div = (2 << 16) * (scale + 1); | |
50 | t = clk; | |
51 | t *= step; | |
52 | t += (div / 2); | |
53 | do_div(t, div); | |
54 | ||
55 | return t; | |
56 | } | |
57 | ||
58 | static void ar933x_serial_get_scale_step(u32 clk, u32 baud, | |
59 | u32 *scale, u32 *step) | |
60 | { | |
61 | u32 tscale, baudrate; | |
62 | long min_diff; | |
63 | ||
64 | *scale = 0; | |
65 | *step = 0; | |
66 | ||
67 | min_diff = baud; | |
68 | for (tscale = 0; tscale < AR933X_UART_CLK_SCALE_M; tscale++) { | |
69 | u64 tstep; | |
70 | int diff; | |
71 | ||
72 | tstep = baud * (tscale + 1); | |
73 | tstep *= (2 << 16); | |
74 | do_div(tstep, clk); | |
75 | ||
76 | if (tstep > AR933X_UART_CLK_STEP_M) | |
77 | break; | |
78 | ||
79 | baudrate = ar933x_serial_get_baud(clk, tscale, tstep); | |
80 | diff = abs(baudrate - baud); | |
81 | if (diff < min_diff) { | |
82 | min_diff = diff; | |
83 | *scale = tscale; | |
84 | *step = tstep; | |
85 | } | |
86 | } | |
87 | } | |
88 | ||
89 | static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) | |
90 | { | |
91 | struct ar933x_serial_priv *priv = dev_get_priv(dev); | |
92 | u32 val, scale, step; | |
93 | ||
94 | val = get_serial_clock(); | |
95 | ar933x_serial_get_scale_step(val, baudrate, &scale, &step); | |
96 | ||
97 | val = (scale & AR933X_UART_CLK_SCALE_M) | |
98 | << AR933X_UART_CLK_SCALE_S; | |
99 | val |= (step & AR933X_UART_CLK_STEP_M) | |
100 | << AR933X_UART_CLK_STEP_S; | |
101 | writel(val, priv->regs + AR933X_UART_CLK_REG); | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static int ar933x_serial_putc(struct udevice *dev, const char c) | |
107 | { | |
108 | struct ar933x_serial_priv *priv = dev_get_priv(dev); | |
109 | u32 data; | |
110 | ||
111 | data = readl(priv->regs + AR933X_UART_DATA_REG); | |
112 | if (!(data & AR933X_UART_DATA_TX_CSR)) | |
113 | return -EAGAIN; | |
114 | ||
115 | data = (u32)c | AR933X_UART_DATA_TX_CSR; | |
116 | writel(data, priv->regs + AR933X_UART_DATA_REG); | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static int ar933x_serial_getc(struct udevice *dev) | |
122 | { | |
123 | struct ar933x_serial_priv *priv = dev_get_priv(dev); | |
124 | u32 data; | |
125 | ||
126 | data = readl(priv->regs + AR933X_UART_DATA_REG); | |
127 | if (!(data & AR933X_UART_DATA_RX_CSR)) | |
128 | return -EAGAIN; | |
129 | ||
130 | writel(AR933X_UART_DATA_RX_CSR, priv->regs + AR933X_UART_DATA_REG); | |
131 | return data & AR933X_UART_DATA_TX_RX_MASK; | |
132 | } | |
133 | ||
134 | static int ar933x_serial_pending(struct udevice *dev, bool input) | |
135 | { | |
136 | struct ar933x_serial_priv *priv = dev_get_priv(dev); | |
137 | u32 data; | |
138 | ||
139 | data = readl(priv->regs + AR933X_UART_DATA_REG); | |
140 | if (input) | |
141 | return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0; | |
142 | else | |
143 | return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1; | |
144 | } | |
145 | ||
146 | static int ar933x_serial_probe(struct udevice *dev) | |
147 | { | |
148 | struct ar933x_serial_priv *priv = dev_get_priv(dev); | |
60b49761 WW |
149 | fdt_addr_t addr; |
150 | u32 val; | |
60b49761 | 151 | |
a821c4af | 152 | addr = devfdt_get_addr(dev); |
60b49761 WW |
153 | if (addr == FDT_ADDR_T_NONE) |
154 | return -EINVAL; | |
155 | ||
773f3b25 | 156 | priv->regs = map_physmem(addr, AR933X_UART_SIZE, |
60b49761 WW |
157 | MAP_NOCACHE); |
158 | ||
159 | /* | |
160 | * UART controller configuration: | |
161 | * - no DMA | |
162 | * - no interrupt | |
163 | * - DCE mode | |
164 | * - no flow control | |
165 | * - set RX ready oride | |
166 | * - set TX ready oride | |
167 | */ | |
168 | val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) | | |
169 | AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE; | |
170 | writel(val, priv->regs + AR933X_UART_CS_REG); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | static const struct dm_serial_ops ar933x_serial_ops = { | |
175 | .putc = ar933x_serial_putc, | |
176 | .pending = ar933x_serial_pending, | |
177 | .getc = ar933x_serial_getc, | |
178 | .setbrg = ar933x_serial_setbrg, | |
179 | }; | |
180 | ||
181 | static const struct udevice_id ar933x_serial_ids[] = { | |
182 | { .compatible = "qca,ar9330-uart" }, | |
183 | { } | |
184 | }; | |
185 | ||
186 | U_BOOT_DRIVER(serial_ar933x) = { | |
187 | .name = "serial_ar933x", | |
188 | .id = UCLASS_SERIAL, | |
189 | .of_match = ar933x_serial_ids, | |
190 | .priv_auto_alloc_size = sizeof(struct ar933x_serial_priv), | |
191 | .probe = ar933x_serial_probe, | |
192 | .ops = &ar933x_serial_ops, | |
193 | .flags = DM_FLAG_PRE_RELOC, | |
194 | }; | |
195 | ||
196 | #ifdef CONFIG_DEBUG_UART_AR933X | |
197 | ||
198 | #include <debug_uart.h> | |
199 | ||
200 | static inline void _debug_uart_init(void) | |
201 | { | |
202 | void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE; | |
203 | u32 val, scale, step; | |
204 | ||
205 | /* | |
206 | * UART controller configuration: | |
207 | * - no DMA | |
208 | * - no interrupt | |
209 | * - DCE mode | |
210 | * - no flow control | |
211 | * - set RX ready oride | |
212 | * - set TX ready oride | |
213 | */ | |
214 | val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) | | |
215 | AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE; | |
216 | writel(val, regs + AR933X_UART_CS_REG); | |
217 | ||
218 | ar933x_serial_get_scale_step(CONFIG_DEBUG_UART_CLOCK, | |
219 | CONFIG_BAUDRATE, &scale, &step); | |
220 | ||
221 | val = (scale & AR933X_UART_CLK_SCALE_M) | |
222 | << AR933X_UART_CLK_SCALE_S; | |
223 | val |= (step & AR933X_UART_CLK_STEP_M) | |
224 | << AR933X_UART_CLK_STEP_S; | |
225 | writel(val, regs + AR933X_UART_CLK_REG); | |
226 | } | |
227 | ||
228 | static inline void _debug_uart_putc(int c) | |
229 | { | |
230 | void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE; | |
231 | u32 data; | |
232 | ||
233 | do { | |
234 | data = readl(regs + AR933X_UART_DATA_REG); | |
235 | } while (!(data & AR933X_UART_DATA_TX_CSR)); | |
236 | ||
237 | data = (u32)c | AR933X_UART_DATA_TX_CSR; | |
238 | writel(data, regs + AR933X_UART_DATA_REG); | |
239 | } | |
240 | ||
241 | DEBUG_UART_FUNCS | |
242 | ||
243 | #endif |