1/*
2 * udbg for NS16550 compatible serial ports
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 */
11#include <linux/types.h>
12#include <asm/udbg.h>
13#include <asm/io.h>
14#include <asm/reg_a2.h>
15
16extern u8 real_readb(volatile u8 __iomem  *addr);
17extern void real_writeb(u8 data, volatile u8 __iomem *addr);
18extern u8 real_205_readb(volatile u8 __iomem  *addr);
19extern void real_205_writeb(u8 data, volatile u8 __iomem *addr);
20
21#define UART_RBR	0
22#define UART_IER	1
23#define UART_FCR	2
24#define UART_LCR	3
25#define UART_MCR	4
26#define UART_LSR	5
27#define UART_MSR	6
28#define UART_SCR	7
29#define UART_THR	UART_RBR
30#define UART_IIR	UART_FCR
31#define UART_DLL	UART_RBR
32#define UART_DLM	UART_IER
33#define UART_DLAB	UART_LCR
34
35#define LSR_DR   0x01  /* Data ready */
36#define LSR_OE   0x02  /* Overrun */
37#define LSR_PE   0x04  /* Parity error */
38#define LSR_FE   0x08  /* Framing error */
39#define LSR_BI   0x10  /* Break */
40#define LSR_THRE 0x20  /* Xmit holding register empty */
41#define LSR_TEMT 0x40  /* Xmitter empty */
42#define LSR_ERR  0x80  /* Error */
43
44#define LCR_DLAB 0x80
45
46static u8 (*udbg_uart_in)(unsigned int reg);
47static void (*udbg_uart_out)(unsigned int reg, u8 data);
48
49static void udbg_uart_flush(void)
50{
51	if (!udbg_uart_in)
52		return;
53
54	/* wait for idle */
55	while ((udbg_uart_in(UART_LSR) & LSR_THRE) == 0)
56		cpu_relax();
57}
58
59static void udbg_uart_putc(char c)
60{
61	if (!udbg_uart_out)
62		return;
63
64	if (c == '\n')
65		udbg_uart_putc('\r');
66	udbg_uart_flush();
67	udbg_uart_out(UART_THR, c);
68}
69
70static int udbg_uart_getc_poll(void)
71{
72	if (!udbg_uart_in)
73		return -1;
74
75	if (!(udbg_uart_in(UART_LSR) & LSR_DR))
76		return udbg_uart_in(UART_RBR);
77
78	return -1;
79}
80
81static int udbg_uart_getc(void)
82{
83	if (!udbg_uart_in)
84		return -1;
85	/* wait for char */
86	while (!(udbg_uart_in(UART_LSR) & LSR_DR))
87		cpu_relax();
88	return udbg_uart_in(UART_RBR);
89}
90
91static void udbg_use_uart(void)
92{
93	udbg_putc = udbg_uart_putc;
94	udbg_flush = udbg_uart_flush;
95	udbg_getc = udbg_uart_getc;
96	udbg_getc_poll = udbg_uart_getc_poll;
97}
98
99void udbg_uart_setup(unsigned int speed, unsigned int clock)
100{
101	unsigned int dll, base_bauds;
102
103	if (!udbg_uart_out)
104		return;
105
106	if (clock == 0)
107		clock = 1843200;
108	if (speed == 0)
109		speed = 9600;
110
111	base_bauds = clock / 16;
112	dll = base_bauds / speed;
113
114	udbg_uart_out(UART_LCR, 0x00);
115	udbg_uart_out(UART_IER, 0xff);
116	udbg_uart_out(UART_IER, 0x00);
117	udbg_uart_out(UART_LCR, LCR_DLAB);
118	udbg_uart_out(UART_DLL, dll & 0xff);
119	udbg_uart_out(UART_DLM, dll >> 8);
120	/* 8 data, 1 stop, no parity */
121	udbg_uart_out(UART_LCR, 0x3);
122	/* RTS/DTR */
123	udbg_uart_out(UART_MCR, 0x3);
124	/* Clear & enable FIFOs */
125	udbg_uart_out(UART_FCR, 0x7);
126}
127
128unsigned int udbg_probe_uart_speed(unsigned int clock)
129{
130	unsigned int dll, dlm, divisor, prescaler, speed;
131	u8 old_lcr;
132
133	old_lcr = udbg_uart_in(UART_LCR);
134
135	/* select divisor latch registers.  */
136	udbg_uart_out(UART_LCR, old_lcr | LCR_DLAB);
137
138	/* now, read the divisor */
139	dll = udbg_uart_in(UART_DLL);
140	dlm = udbg_uart_in(UART_DLM);
141	divisor = dlm << 8 | dll;
142
143	/* check prescaling */
144	if (udbg_uart_in(UART_MCR) & 0x80)
145		prescaler = 4;
146	else
147		prescaler = 1;
148
149	/* restore the LCR */
150	udbg_uart_out(UART_LCR, old_lcr);
151
152	/* calculate speed */
153	speed = (clock / prescaler) / (divisor * 16);
154
155	/* sanity check */
156	if (speed > (clock / 16))
157		speed = 9600;
158
159	return speed;
160}
161
162static union {
163	unsigned char __iomem *mmio_base;
164	unsigned long pio_base;
165} udbg_uart;
166
167static unsigned int udbg_uart_stride = 1;
168
169static u8 udbg_uart_in_pio(unsigned int reg)
170{
171	return inb(udbg_uart.pio_base + (reg * udbg_uart_stride));
172}
173
174static void udbg_uart_out_pio(unsigned int reg, u8 data)
175{
176	outb(data, udbg_uart.pio_base + (reg * udbg_uart_stride));
177}
178
179void udbg_uart_init_pio(unsigned long port, unsigned int stride)
180{
181	if (!port)
182		return;
183	udbg_uart.pio_base = port;
184	udbg_uart_stride = stride;
185	udbg_uart_in = udbg_uart_in_pio;
186	udbg_uart_out = udbg_uart_out_pio;
187	udbg_use_uart();
188}
189
190static u8 udbg_uart_in_mmio(unsigned int reg)
191{
192	return in_8(udbg_uart.mmio_base + (reg * udbg_uart_stride));
193}
194
195static void udbg_uart_out_mmio(unsigned int reg, u8 data)
196{
197	out_8(udbg_uart.mmio_base + (reg * udbg_uart_stride), data);
198}
199
200
201void udbg_uart_init_mmio(void __iomem *addr, unsigned int stride)
202{
203	if (!addr)
204		return;
205	udbg_uart.mmio_base = addr;
206	udbg_uart_stride = stride;
207	udbg_uart_in = udbg_uart_in_mmio;
208	udbg_uart_out = udbg_uart_out_mmio;
209	udbg_use_uart();
210}
211
212#ifdef CONFIG_PPC_MAPLE
213
214#define UDBG_UART_MAPLE_ADDR	((void __iomem *)0xf40003f8)
215
216static u8 udbg_uart_in_maple(unsigned int reg)
217{
218	return real_readb(UDBG_UART_MAPLE_ADDR + reg);
219}
220
221static void udbg_uart_out_maple(unsigned int reg, u8 val)
222{
223	real_writeb(val, UDBG_UART_MAPLE_ADDR + reg);
224}
225
226void __init udbg_init_maple_realmode(void)
227{
228	udbg_uart_in = udbg_uart_in_maple;
229	udbg_uart_out = udbg_uart_out_maple;
230	udbg_use_uart();
231}
232
233#endif /* CONFIG_PPC_MAPLE */
234
235#ifdef CONFIG_PPC_PASEMI
236
237#define UDBG_UART_PAS_ADDR	((void __iomem *)0xfcff03f8UL)
238
239static u8 udbg_uart_in_pas(unsigned int reg)
240{
241	return real_205_readb(UDBG_UART_PAS_ADDR + reg);
242}
243
244static void udbg_uart_out_pas(unsigned int reg, u8 val)
245{
246	real_205_writeb(val, UDBG_UART_PAS_ADDR + reg);
247}
248
249void __init udbg_init_pas_realmode(void)
250{
251	udbg_uart_in = udbg_uart_in_pas;
252	udbg_uart_out = udbg_uart_out_pas;
253	udbg_use_uart();
254}
255
256#endif /* CONFIG_PPC_PASEMI */
257
258#ifdef CONFIG_PPC_EARLY_DEBUG_44x
259
260#include <platforms/44x/44x.h>
261
262static u8 udbg_uart_in_44x_as1(unsigned int reg)
263{
264	return as1_readb((void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg);
265}
266
267static void udbg_uart_out_44x_as1(unsigned int reg, u8 val)
268{
269	as1_writeb(val, (void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg);
270}
271
272void __init udbg_init_44x_as1(void)
273{
274	udbg_uart_in = udbg_uart_in_44x_as1;
275	udbg_uart_out = udbg_uart_out_44x_as1;
276	udbg_use_uart();
277}
278
279#endif /* CONFIG_PPC_EARLY_DEBUG_44x */
280
281#ifdef CONFIG_PPC_EARLY_DEBUG_40x
282
283static u8 udbg_uart_in_40x(unsigned int reg)
284{
285	return real_readb((void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR
286			  + reg);
287}
288
289static void udbg_uart_out_40x(unsigned int reg, u8 val)
290{
291	real_writeb(val, (void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR
292		    + reg);
293}
294
295void __init udbg_init_40x_realmode(void)
296{
297	udbg_uart_in = udbg_uart_in_40x;
298	udbg_uart_out = udbg_uart_out_40x;
299	udbg_use_uart();
300}
301
302#endif /* CONFIG_PPC_EARLY_DEBUG_40x */
303