Commit | Line | Data |
---|---|---|
7f853352 | 1 | /* |
25985edc | 2 | * udbg for NS16550 compatible serial ports |
7f853352 MM |
3 | * |
4 | * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
7f853352 | 11 | #include <linux/types.h> |
188d2ce7 | 12 | #include <asm/udbg.h> |
7f853352 | 13 | #include <asm/io.h> |
a0496d45 | 14 | #include <asm/reg_a2.h> |
7f853352 MM |
15 | |
16 | extern u8 real_readb(volatile u8 __iomem *addr); | |
17 | extern void real_writeb(u8 data, volatile u8 __iomem *addr); | |
39c870d5 OJ |
18 | extern u8 real_205_readb(volatile u8 __iomem *addr); |
19 | extern void real_205_writeb(u8 data, volatile u8 __iomem *addr); | |
7f853352 MM |
20 | |
21 | struct NS16550 { | |
22 | /* this struct must be packed */ | |
23 | unsigned char rbr; /* 0 */ | |
24 | unsigned char ier; /* 1 */ | |
25 | unsigned char fcr; /* 2 */ | |
26 | unsigned char lcr; /* 3 */ | |
27 | unsigned char mcr; /* 4 */ | |
28 | unsigned char lsr; /* 5 */ | |
29 | unsigned char msr; /* 6 */ | |
30 | unsigned char scr; /* 7 */ | |
31 | }; | |
32 | ||
33 | #define thr rbr | |
34 | #define iir fcr | |
35 | #define dll rbr | |
36 | #define dlm ier | |
37 | #define dlab lcr | |
38 | ||
39 | #define LSR_DR 0x01 /* Data ready */ | |
40 | #define LSR_OE 0x02 /* Overrun */ | |
41 | #define LSR_PE 0x04 /* Parity error */ | |
42 | #define LSR_FE 0x08 /* Framing error */ | |
43 | #define LSR_BI 0x10 /* Break */ | |
44 | #define LSR_THRE 0x20 /* Xmit holding register empty */ | |
45 | #define LSR_TEMT 0x40 /* Xmitter empty */ | |
46 | #define LSR_ERR 0x80 /* Error */ | |
47 | ||
463ce0e1 BH |
48 | #define LCR_DLAB 0x80 |
49 | ||
f276b5ba | 50 | static struct NS16550 __iomem *udbg_comport; |
7f853352 | 51 | |
af9c7249 | 52 | static void udbg_550_flush(void) |
7f853352 MM |
53 | { |
54 | if (udbg_comport) { | |
55 | while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) | |
56 | /* wait for idle */; | |
af9c7249 AK |
57 | } |
58 | } | |
59 | ||
60 | static void udbg_550_putc(char c) | |
61 | { | |
62 | if (udbg_comport) { | |
7f853352 MM |
63 | if (c == '\n') |
64 | udbg_550_putc('\r'); | |
af9c7249 AK |
65 | udbg_550_flush(); |
66 | out_8(&udbg_comport->thr, c); | |
7f853352 MM |
67 | } |
68 | } | |
69 | ||
70 | static int udbg_550_getc_poll(void) | |
71 | { | |
72 | if (udbg_comport) { | |
73 | if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0) | |
74 | return in_8(&udbg_comport->rbr); | |
75 | else | |
76 | return -1; | |
77 | } | |
78 | return -1; | |
79 | } | |
80 | ||
bb6b9b28 | 81 | static int udbg_550_getc(void) |
7f853352 MM |
82 | { |
83 | if (udbg_comport) { | |
84 | while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) | |
85 | /* wait for char */; | |
86 | return in_8(&udbg_comport->rbr); | |
87 | } | |
bb6b9b28 | 88 | return -1; |
7f853352 MM |
89 | } |
90 | ||
463ce0e1 BH |
91 | void udbg_init_uart(void __iomem *comport, unsigned int speed, |
92 | unsigned int clock) | |
7f853352 | 93 | { |
171505da | 94 | unsigned int dll, base_bauds; |
463ce0e1 | 95 | |
171505da BH |
96 | if (clock == 0) |
97 | clock = 1843200; | |
463ce0e1 BH |
98 | if (speed == 0) |
99 | speed = 9600; | |
171505da BH |
100 | |
101 | base_bauds = clock / 16; | |
463ce0e1 | 102 | dll = base_bauds / speed; |
7f853352 MM |
103 | |
104 | if (comport) { | |
105 | udbg_comport = (struct NS16550 __iomem *)comport; | |
106 | out_8(&udbg_comport->lcr, 0x00); | |
107 | out_8(&udbg_comport->ier, 0xff); | |
108 | out_8(&udbg_comport->ier, 0x00); | |
463ce0e1 BH |
109 | out_8(&udbg_comport->lcr, LCR_DLAB); |
110 | out_8(&udbg_comport->dll, dll & 0xff); | |
111 | out_8(&udbg_comport->dlm, dll >> 8); | |
112 | /* 8 data, 1 stop, no parity */ | |
113 | out_8(&udbg_comport->lcr, 0x03); | |
114 | /* RTS/DTR */ | |
115 | out_8(&udbg_comport->mcr, 0x03); | |
116 | /* Clear & enable FIFOs */ | |
117 | out_8(&udbg_comport->fcr ,0x07); | |
c8f1c8be | 118 | udbg_putc = udbg_550_putc; |
af9c7249 | 119 | udbg_flush = udbg_550_flush; |
c8f1c8be MM |
120 | udbg_getc = udbg_550_getc; |
121 | udbg_getc_poll = udbg_550_getc_poll; | |
7f853352 MM |
122 | } |
123 | } | |
124 | ||
463ce0e1 BH |
125 | unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) |
126 | { | |
127 | unsigned int dll, dlm, divisor, prescaler, speed; | |
128 | u8 old_lcr; | |
f276b5ba | 129 | struct NS16550 __iomem *port = comport; |
463ce0e1 BH |
130 | |
131 | old_lcr = in_8(&port->lcr); | |
132 | ||
133 | /* select divisor latch registers. */ | |
134 | out_8(&port->lcr, LCR_DLAB); | |
135 | ||
136 | /* now, read the divisor */ | |
137 | dll = in_8(&port->dll); | |
138 | dlm = in_8(&port->dlm); | |
139 | divisor = dlm << 8 | dll; | |
140 | ||
141 | /* check prescaling */ | |
142 | if (in_8(&port->mcr) & 0x80) | |
143 | prescaler = 4; | |
144 | else | |
145 | prescaler = 1; | |
146 | ||
147 | /* restore the LCR */ | |
148 | out_8(&port->lcr, old_lcr); | |
149 | ||
150 | /* calculate speed */ | |
151 | speed = (clock / prescaler) / (divisor * 16); | |
152 | ||
153 | /* sanity check */ | |
bb5e6491 | 154 | if (speed > (clock / 16)) |
463ce0e1 BH |
155 | speed = 9600; |
156 | ||
157 | return speed; | |
158 | } | |
159 | ||
7f853352 | 160 | #ifdef CONFIG_PPC_MAPLE |
af9c7249 | 161 | void udbg_maple_real_flush(void) |
7f853352 MM |
162 | { |
163 | if (udbg_comport) { | |
164 | while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) | |
165 | /* wait for idle */; | |
af9c7249 AK |
166 | } |
167 | } | |
168 | ||
169 | void udbg_maple_real_putc(char c) | |
170 | { | |
171 | if (udbg_comport) { | |
7f853352 MM |
172 | if (c == '\n') |
173 | udbg_maple_real_putc('\r'); | |
af9c7249 AK |
174 | udbg_maple_real_flush(); |
175 | real_writeb(c, &udbg_comport->thr); eieio(); | |
7f853352 MM |
176 | } |
177 | } | |
178 | ||
296167ae | 179 | void __init udbg_init_maple_realmode(void) |
7f853352 | 180 | { |
f276b5ba | 181 | udbg_comport = (struct NS16550 __iomem *)0xf40003f8; |
7f853352 | 182 | |
c8f1c8be | 183 | udbg_putc = udbg_maple_real_putc; |
af9c7249 | 184 | udbg_flush = udbg_maple_real_flush; |
c8f1c8be MM |
185 | udbg_getc = NULL; |
186 | udbg_getc_poll = NULL; | |
7f853352 MM |
187 | } |
188 | #endif /* CONFIG_PPC_MAPLE */ | |
39c870d5 OJ |
189 | |
190 | #ifdef CONFIG_PPC_PASEMI | |
af9c7249 | 191 | void udbg_pas_real_flush(void) |
39c870d5 OJ |
192 | { |
193 | if (udbg_comport) { | |
194 | while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0) | |
195 | /* wait for idle */; | |
af9c7249 AK |
196 | } |
197 | } | |
198 | ||
199 | void udbg_pas_real_putc(char c) | |
200 | { | |
201 | if (udbg_comport) { | |
39c870d5 OJ |
202 | if (c == '\n') |
203 | udbg_pas_real_putc('\r'); | |
af9c7249 AK |
204 | udbg_pas_real_flush(); |
205 | real_205_writeb(c, &udbg_comport->thr); eieio(); | |
39c870d5 OJ |
206 | } |
207 | } | |
208 | ||
209 | void udbg_init_pas_realmode(void) | |
210 | { | |
f276b5ba | 211 | udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL; |
39c870d5 OJ |
212 | |
213 | udbg_putc = udbg_pas_real_putc; | |
af9c7249 | 214 | udbg_flush = udbg_pas_real_flush; |
39c870d5 OJ |
215 | udbg_getc = NULL; |
216 | udbg_getc_poll = NULL; | |
217 | } | |
218 | #endif /* CONFIG_PPC_MAPLE */ | |
d9b55a03 DG |
219 | |
220 | #ifdef CONFIG_PPC_EARLY_DEBUG_44x | |
221 | #include <platforms/44x/44x.h> | |
222 | ||
f694cda8 | 223 | static void udbg_44x_as1_flush(void) |
d9b55a03 DG |
224 | { |
225 | if (udbg_comport) { | |
226 | while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0) | |
227 | /* wait for idle */; | |
af9c7249 AK |
228 | } |
229 | } | |
230 | ||
231 | static void udbg_44x_as1_putc(char c) | |
232 | { | |
233 | if (udbg_comport) { | |
d9b55a03 DG |
234 | if (c == '\n') |
235 | udbg_44x_as1_putc('\r'); | |
af9c7249 AK |
236 | udbg_44x_as1_flush(); |
237 | as1_writeb(c, &udbg_comport->thr); eieio(); | |
d9b55a03 DG |
238 | } |
239 | } | |
240 | ||
70dea47d HB |
241 | static int udbg_44x_as1_getc(void) |
242 | { | |
243 | if (udbg_comport) { | |
244 | while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0) | |
245 | ; /* wait for char */ | |
246 | return as1_readb(&udbg_comport->rbr); | |
247 | } | |
248 | return -1; | |
249 | } | |
250 | ||
d9b55a03 DG |
251 | void __init udbg_init_44x_as1(void) |
252 | { | |
253 | udbg_comport = | |
f276b5ba | 254 | (struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR; |
d9b55a03 DG |
255 | |
256 | udbg_putc = udbg_44x_as1_putc; | |
af9c7249 | 257 | udbg_flush = udbg_44x_as1_flush; |
70dea47d | 258 | udbg_getc = udbg_44x_as1_getc; |
d9b55a03 DG |
259 | } |
260 | #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ | |
9dae8afd BH |
261 | |
262 | #ifdef CONFIG_PPC_EARLY_DEBUG_40x | |
af9c7249 | 263 | static void udbg_40x_real_flush(void) |
9dae8afd BH |
264 | { |
265 | if (udbg_comport) { | |
266 | while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) | |
267 | /* wait for idle */; | |
af9c7249 AK |
268 | } |
269 | } | |
270 | ||
271 | static void udbg_40x_real_putc(char c) | |
272 | { | |
273 | if (udbg_comport) { | |
9dae8afd BH |
274 | if (c == '\n') |
275 | udbg_40x_real_putc('\r'); | |
af9c7249 AK |
276 | udbg_40x_real_flush(); |
277 | real_writeb(c, &udbg_comport->thr); eieio(); | |
9dae8afd BH |
278 | } |
279 | } | |
280 | ||
281 | static int udbg_40x_real_getc(void) | |
282 | { | |
283 | if (udbg_comport) { | |
284 | while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0) | |
285 | ; /* wait for char */ | |
286 | return real_readb(&udbg_comport->rbr); | |
287 | } | |
288 | return -1; | |
289 | } | |
290 | ||
291 | void __init udbg_init_40x_realmode(void) | |
292 | { | |
293 | udbg_comport = (struct NS16550 __iomem *) | |
294 | CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR; | |
295 | ||
296 | udbg_putc = udbg_40x_real_putc; | |
af9c7249 | 297 | udbg_flush = udbg_40x_real_flush; |
9dae8afd BH |
298 | udbg_getc = udbg_40x_real_getc; |
299 | udbg_getc_poll = NULL; | |
300 | } | |
301 | #endif /* CONFIG_PPC_EARLY_DEBUG_40x */ | |
a0496d45 JM |
302 | |
303 | #ifdef CONFIG_PPC_EARLY_DEBUG_WSP | |
304 | static void udbg_wsp_flush(void) | |
305 | { | |
306 | if (udbg_comport) { | |
307 | while ((readb(&udbg_comport->lsr) & LSR_THRE) == 0) | |
308 | /* wait for idle */; | |
309 | } | |
310 | } | |
311 | ||
312 | static void udbg_wsp_putc(char c) | |
313 | { | |
314 | if (udbg_comport) { | |
315 | if (c == '\n') | |
316 | udbg_wsp_putc('\r'); | |
317 | udbg_wsp_flush(); | |
318 | writeb(c, &udbg_comport->thr); eieio(); | |
319 | } | |
320 | } | |
321 | ||
322 | static int udbg_wsp_getc(void) | |
323 | { | |
324 | if (udbg_comport) { | |
325 | while ((readb(&udbg_comport->lsr) & LSR_DR) == 0) | |
326 | ; /* wait for char */ | |
327 | return readb(&udbg_comport->rbr); | |
328 | } | |
329 | return -1; | |
330 | } | |
331 | ||
332 | static int udbg_wsp_getc_poll(void) | |
333 | { | |
334 | if (udbg_comport) | |
335 | if (readb(&udbg_comport->lsr) & LSR_DR) | |
336 | return readb(&udbg_comport->rbr); | |
337 | return -1; | |
338 | } | |
339 | ||
340 | void __init udbg_init_wsp(void) | |
341 | { | |
342 | udbg_comport = (struct NS16550 __iomem *)WSP_UART_VIRT; | |
343 | ||
344 | udbg_init_uart(udbg_comport, 57600, 50000000); | |
345 | ||
346 | udbg_putc = udbg_wsp_putc; | |
347 | udbg_flush = udbg_wsp_flush; | |
348 | udbg_getc = udbg_wsp_getc; | |
349 | udbg_getc_poll = udbg_wsp_getc_poll; | |
350 | } | |
351 | #endif /* CONFIG_PPC_EARLY_DEBUG_WSP */ |