xref: /openbmc/linux/arch/powerpc/kernel/udbg_16550.c (revision a0496d45)
135cd8785SDavid Gibson /*
225985edcSLucas De Marchi  * udbg for NS16550 compatible serial ports
335cd8785SDavid Gibson  *
435cd8785SDavid Gibson  * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp
535cd8785SDavid Gibson  *
635cd8785SDavid Gibson  *      This program is free software; you can redistribute it and/or
735cd8785SDavid Gibson  *      modify it under the terms of the GNU General Public License
835cd8785SDavid Gibson  *      as published by the Free Software Foundation; either version
935cd8785SDavid Gibson  *      2 of the License, or (at your option) any later version.
1035cd8785SDavid Gibson  */
1135cd8785SDavid Gibson #include <linux/types.h>
1235cd8785SDavid Gibson #include <asm/udbg.h>
1335cd8785SDavid Gibson #include <asm/io.h>
14a0496d45SJack Miller #include <asm/reg_a2.h>
1535cd8785SDavid Gibson 
1635cd8785SDavid Gibson extern u8 real_readb(volatile u8 __iomem  *addr);
1735cd8785SDavid Gibson extern void real_writeb(u8 data, volatile u8 __iomem *addr);
1839c870d5SOlof Johansson extern u8 real_205_readb(volatile u8 __iomem  *addr);
1939c870d5SOlof Johansson extern void real_205_writeb(u8 data, volatile u8 __iomem *addr);
2035cd8785SDavid Gibson 
2135cd8785SDavid Gibson struct NS16550 {
2235cd8785SDavid Gibson 	/* this struct must be packed */
2335cd8785SDavid Gibson 	unsigned char rbr;  /* 0 */
2435cd8785SDavid Gibson 	unsigned char ier;  /* 1 */
2535cd8785SDavid Gibson 	unsigned char fcr;  /* 2 */
2635cd8785SDavid Gibson 	unsigned char lcr;  /* 3 */
2735cd8785SDavid Gibson 	unsigned char mcr;  /* 4 */
2835cd8785SDavid Gibson 	unsigned char lsr;  /* 5 */
2935cd8785SDavid Gibson 	unsigned char msr;  /* 6 */
3035cd8785SDavid Gibson 	unsigned char scr;  /* 7 */
3135cd8785SDavid Gibson };
3235cd8785SDavid Gibson 
3335cd8785SDavid Gibson #define thr rbr
3435cd8785SDavid Gibson #define iir fcr
3535cd8785SDavid Gibson #define dll rbr
3635cd8785SDavid Gibson #define dlm ier
3735cd8785SDavid Gibson #define dlab lcr
3835cd8785SDavid Gibson 
3935cd8785SDavid Gibson #define LSR_DR   0x01  /* Data ready */
4035cd8785SDavid Gibson #define LSR_OE   0x02  /* Overrun */
4135cd8785SDavid Gibson #define LSR_PE   0x04  /* Parity error */
4235cd8785SDavid Gibson #define LSR_FE   0x08  /* Framing error */
4335cd8785SDavid Gibson #define LSR_BI   0x10  /* Break */
4435cd8785SDavid Gibson #define LSR_THRE 0x20  /* Xmit holding register empty */
4535cd8785SDavid Gibson #define LSR_TEMT 0x40  /* Xmitter empty */
4635cd8785SDavid Gibson #define LSR_ERR  0x80  /* Error */
4735cd8785SDavid Gibson 
48463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80
49463ce0e1SBenjamin Herrenschmidt 
50f276b5baSBenjamin Herrenschmidt static struct NS16550 __iomem *udbg_comport;
5135cd8785SDavid Gibson 
52af9c7249SAndrew Klossner static void udbg_550_flush(void)
5335cd8785SDavid Gibson {
5435cd8785SDavid Gibson 	if (udbg_comport) {
5535cd8785SDavid Gibson 		while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
5635cd8785SDavid Gibson 			/* wait for idle */;
57af9c7249SAndrew Klossner 	}
58af9c7249SAndrew Klossner }
59af9c7249SAndrew Klossner 
60af9c7249SAndrew Klossner static void udbg_550_putc(char c)
61af9c7249SAndrew Klossner {
62af9c7249SAndrew Klossner 	if (udbg_comport) {
6335cd8785SDavid Gibson 		if (c == '\n')
6435cd8785SDavid Gibson 			udbg_550_putc('\r');
65af9c7249SAndrew Klossner 		udbg_550_flush();
66af9c7249SAndrew Klossner 		out_8(&udbg_comport->thr, c);
6735cd8785SDavid Gibson 	}
6835cd8785SDavid Gibson }
6935cd8785SDavid Gibson 
7035cd8785SDavid Gibson static int udbg_550_getc_poll(void)
7135cd8785SDavid Gibson {
7235cd8785SDavid Gibson 	if (udbg_comport) {
7335cd8785SDavid Gibson 		if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0)
7435cd8785SDavid Gibson 			return in_8(&udbg_comport->rbr);
7535cd8785SDavid Gibson 		else
7635cd8785SDavid Gibson 			return -1;
7735cd8785SDavid Gibson 	}
7835cd8785SDavid Gibson 	return -1;
7935cd8785SDavid Gibson }
8035cd8785SDavid Gibson 
81bb6b9b28SBenjamin Herrenschmidt static int udbg_550_getc(void)
8235cd8785SDavid Gibson {
8335cd8785SDavid Gibson 	if (udbg_comport) {
8435cd8785SDavid Gibson 		while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
8535cd8785SDavid Gibson 			/* wait for char */;
8635cd8785SDavid Gibson 		return in_8(&udbg_comport->rbr);
8735cd8785SDavid Gibson 	}
88bb6b9b28SBenjamin Herrenschmidt 	return -1;
8935cd8785SDavid Gibson }
9035cd8785SDavid Gibson 
91463ce0e1SBenjamin Herrenschmidt void udbg_init_uart(void __iomem *comport, unsigned int speed,
92463ce0e1SBenjamin Herrenschmidt 		    unsigned int clock)
9335cd8785SDavid Gibson {
94171505daSBenjamin Herrenschmidt 	unsigned int dll, base_bauds;
95463ce0e1SBenjamin Herrenschmidt 
96171505daSBenjamin Herrenschmidt 	if (clock == 0)
97171505daSBenjamin Herrenschmidt 		clock = 1843200;
98463ce0e1SBenjamin Herrenschmidt 	if (speed == 0)
99463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
100171505daSBenjamin Herrenschmidt 
101171505daSBenjamin Herrenschmidt 	base_bauds = clock / 16;
102463ce0e1SBenjamin Herrenschmidt 	dll = base_bauds / speed;
10335cd8785SDavid Gibson 
10435cd8785SDavid Gibson 	if (comport) {
10535cd8785SDavid Gibson 		udbg_comport = (struct NS16550 __iomem *)comport;
10635cd8785SDavid Gibson 		out_8(&udbg_comport->lcr, 0x00);
10735cd8785SDavid Gibson 		out_8(&udbg_comport->ier, 0xff);
10835cd8785SDavid Gibson 		out_8(&udbg_comport->ier, 0x00);
109463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->lcr, LCR_DLAB);
110463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->dll, dll & 0xff);
111463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->dlm, dll >> 8);
112463ce0e1SBenjamin Herrenschmidt 		/* 8 data, 1 stop, no parity */
113463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->lcr, 0x03);
114463ce0e1SBenjamin Herrenschmidt 		/* RTS/DTR */
115463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->mcr, 0x03);
116463ce0e1SBenjamin Herrenschmidt 		/* Clear & enable FIFOs */
117463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->fcr ,0x07);
11835cd8785SDavid Gibson 		udbg_putc = udbg_550_putc;
119af9c7249SAndrew Klossner 		udbg_flush = udbg_550_flush;
12035cd8785SDavid Gibson 		udbg_getc = udbg_550_getc;
12135cd8785SDavid Gibson 		udbg_getc_poll = udbg_550_getc_poll;
12235cd8785SDavid Gibson 	}
12335cd8785SDavid Gibson }
12435cd8785SDavid Gibson 
125463ce0e1SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
126463ce0e1SBenjamin Herrenschmidt {
127463ce0e1SBenjamin Herrenschmidt 	unsigned int dll, dlm, divisor, prescaler, speed;
128463ce0e1SBenjamin Herrenschmidt 	u8 old_lcr;
129f276b5baSBenjamin Herrenschmidt 	struct NS16550 __iomem *port = comport;
130463ce0e1SBenjamin Herrenschmidt 
131463ce0e1SBenjamin Herrenschmidt 	old_lcr = in_8(&port->lcr);
132463ce0e1SBenjamin Herrenschmidt 
133463ce0e1SBenjamin Herrenschmidt 	/* select divisor latch registers.  */
134463ce0e1SBenjamin Herrenschmidt 	out_8(&port->lcr, LCR_DLAB);
135463ce0e1SBenjamin Herrenschmidt 
136463ce0e1SBenjamin Herrenschmidt 	/* now, read the divisor */
137463ce0e1SBenjamin Herrenschmidt 	dll = in_8(&port->dll);
138463ce0e1SBenjamin Herrenschmidt 	dlm = in_8(&port->dlm);
139463ce0e1SBenjamin Herrenschmidt 	divisor = dlm << 8 | dll;
140463ce0e1SBenjamin Herrenschmidt 
141463ce0e1SBenjamin Herrenschmidt 	/* check prescaling */
142463ce0e1SBenjamin Herrenschmidt 	if (in_8(&port->mcr) & 0x80)
143463ce0e1SBenjamin Herrenschmidt 		prescaler = 4;
144463ce0e1SBenjamin Herrenschmidt 	else
145463ce0e1SBenjamin Herrenschmidt 		prescaler = 1;
146463ce0e1SBenjamin Herrenschmidt 
147463ce0e1SBenjamin Herrenschmidt 	/* restore the LCR */
148463ce0e1SBenjamin Herrenschmidt 	out_8(&port->lcr, old_lcr);
149463ce0e1SBenjamin Herrenschmidt 
150463ce0e1SBenjamin Herrenschmidt 	/* calculate speed */
151463ce0e1SBenjamin Herrenschmidt 	speed = (clock / prescaler) / (divisor * 16);
152463ce0e1SBenjamin Herrenschmidt 
153463ce0e1SBenjamin Herrenschmidt 	/* sanity check */
154bb5e6491Sroel kluin 	if (speed > (clock / 16))
155463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
156463ce0e1SBenjamin Herrenschmidt 
157463ce0e1SBenjamin Herrenschmidt 	return speed;
158463ce0e1SBenjamin Herrenschmidt }
159463ce0e1SBenjamin Herrenschmidt 
16035cd8785SDavid Gibson #ifdef CONFIG_PPC_MAPLE
161af9c7249SAndrew Klossner void udbg_maple_real_flush(void)
16235cd8785SDavid Gibson {
16335cd8785SDavid Gibson 	if (udbg_comport) {
16435cd8785SDavid Gibson 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
16535cd8785SDavid Gibson 			/* wait for idle */;
166af9c7249SAndrew Klossner 	}
167af9c7249SAndrew Klossner }
168af9c7249SAndrew Klossner 
169af9c7249SAndrew Klossner void udbg_maple_real_putc(char c)
170af9c7249SAndrew Klossner {
171af9c7249SAndrew Klossner 	if (udbg_comport) {
17235cd8785SDavid Gibson 		if (c == '\n')
17335cd8785SDavid Gibson 			udbg_maple_real_putc('\r');
174af9c7249SAndrew Klossner 		udbg_maple_real_flush();
175af9c7249SAndrew Klossner 		real_writeb(c, &udbg_comport->thr); eieio();
17635cd8785SDavid Gibson 	}
17735cd8785SDavid Gibson }
17835cd8785SDavid Gibson 
179296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void)
18035cd8785SDavid Gibson {
181f276b5baSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)0xf40003f8;
18235cd8785SDavid Gibson 
18335cd8785SDavid Gibson 	udbg_putc = udbg_maple_real_putc;
184af9c7249SAndrew Klossner 	udbg_flush = udbg_maple_real_flush;
18535cd8785SDavid Gibson 	udbg_getc = NULL;
18635cd8785SDavid Gibson 	udbg_getc_poll = NULL;
18735cd8785SDavid Gibson }
18835cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */
18939c870d5SOlof Johansson 
19039c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI
191af9c7249SAndrew Klossner void udbg_pas_real_flush(void)
19239c870d5SOlof Johansson {
19339c870d5SOlof Johansson 	if (udbg_comport) {
19439c870d5SOlof Johansson 		while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
19539c870d5SOlof Johansson 			/* wait for idle */;
196af9c7249SAndrew Klossner 	}
197af9c7249SAndrew Klossner }
198af9c7249SAndrew Klossner 
199af9c7249SAndrew Klossner void udbg_pas_real_putc(char c)
200af9c7249SAndrew Klossner {
201af9c7249SAndrew Klossner 	if (udbg_comport) {
20239c870d5SOlof Johansson 		if (c == '\n')
20339c870d5SOlof Johansson 			udbg_pas_real_putc('\r');
204af9c7249SAndrew Klossner 		udbg_pas_real_flush();
205af9c7249SAndrew Klossner 		real_205_writeb(c, &udbg_comport->thr); eieio();
20639c870d5SOlof Johansson 	}
20739c870d5SOlof Johansson }
20839c870d5SOlof Johansson 
20939c870d5SOlof Johansson void udbg_init_pas_realmode(void)
21039c870d5SOlof Johansson {
211f276b5baSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL;
21239c870d5SOlof Johansson 
21339c870d5SOlof Johansson 	udbg_putc = udbg_pas_real_putc;
214af9c7249SAndrew Klossner 	udbg_flush = udbg_pas_real_flush;
21539c870d5SOlof Johansson 	udbg_getc = NULL;
21639c870d5SOlof Johansson 	udbg_getc_poll = NULL;
21739c870d5SOlof Johansson }
21839c870d5SOlof Johansson #endif /* CONFIG_PPC_MAPLE */
219d9b55a03SDavid Gibson 
220d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x
221d9b55a03SDavid Gibson #include <platforms/44x/44x.h>
222d9b55a03SDavid Gibson 
223f694cda8SBenjamin Herrenschmidt static void udbg_44x_as1_flush(void)
224d9b55a03SDavid Gibson {
225d9b55a03SDavid Gibson 	if (udbg_comport) {
226d9b55a03SDavid Gibson 		while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
227d9b55a03SDavid Gibson 			/* wait for idle */;
228af9c7249SAndrew Klossner 	}
229af9c7249SAndrew Klossner }
230af9c7249SAndrew Klossner 
231af9c7249SAndrew Klossner static void udbg_44x_as1_putc(char c)
232af9c7249SAndrew Klossner {
233af9c7249SAndrew Klossner 	if (udbg_comport) {
234d9b55a03SDavid Gibson 		if (c == '\n')
235d9b55a03SDavid Gibson 			udbg_44x_as1_putc('\r');
236af9c7249SAndrew Klossner 		udbg_44x_as1_flush();
237af9c7249SAndrew Klossner 		as1_writeb(c, &udbg_comport->thr); eieio();
238d9b55a03SDavid Gibson 	}
239d9b55a03SDavid Gibson }
240d9b55a03SDavid Gibson 
24170dea47dSHollis Blanchard static int udbg_44x_as1_getc(void)
24270dea47dSHollis Blanchard {
24370dea47dSHollis Blanchard 	if (udbg_comport) {
24470dea47dSHollis Blanchard 		while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0)
24570dea47dSHollis Blanchard 			; /* wait for char */
24670dea47dSHollis Blanchard 		return as1_readb(&udbg_comport->rbr);
24770dea47dSHollis Blanchard 	}
24870dea47dSHollis Blanchard 	return -1;
24970dea47dSHollis Blanchard }
25070dea47dSHollis Blanchard 
251d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void)
252d9b55a03SDavid Gibson {
253d9b55a03SDavid Gibson 	udbg_comport =
254f276b5baSBenjamin Herrenschmidt 		(struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR;
255d9b55a03SDavid Gibson 
256d9b55a03SDavid Gibson 	udbg_putc = udbg_44x_as1_putc;
257af9c7249SAndrew Klossner 	udbg_flush = udbg_44x_as1_flush;
25870dea47dSHollis Blanchard 	udbg_getc = udbg_44x_as1_getc;
259d9b55a03SDavid Gibson }
260d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */
2619dae8afdSBenjamin Herrenschmidt 
2629dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x
263af9c7249SAndrew Klossner static void udbg_40x_real_flush(void)
2649dae8afdSBenjamin Herrenschmidt {
2659dae8afdSBenjamin Herrenschmidt 	if (udbg_comport) {
2669dae8afdSBenjamin Herrenschmidt 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
2679dae8afdSBenjamin Herrenschmidt 			/* wait for idle */;
268af9c7249SAndrew Klossner 	}
269af9c7249SAndrew Klossner }
270af9c7249SAndrew Klossner 
271af9c7249SAndrew Klossner static void udbg_40x_real_putc(char c)
272af9c7249SAndrew Klossner {
273af9c7249SAndrew Klossner 	if (udbg_comport) {
2749dae8afdSBenjamin Herrenschmidt 		if (c == '\n')
2759dae8afdSBenjamin Herrenschmidt 			udbg_40x_real_putc('\r');
276af9c7249SAndrew Klossner 		udbg_40x_real_flush();
277af9c7249SAndrew Klossner 		real_writeb(c, &udbg_comport->thr); eieio();
2789dae8afdSBenjamin Herrenschmidt 	}
2799dae8afdSBenjamin Herrenschmidt }
2809dae8afdSBenjamin Herrenschmidt 
2819dae8afdSBenjamin Herrenschmidt static int udbg_40x_real_getc(void)
2829dae8afdSBenjamin Herrenschmidt {
2839dae8afdSBenjamin Herrenschmidt 	if (udbg_comport) {
2849dae8afdSBenjamin Herrenschmidt 		while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0)
2859dae8afdSBenjamin Herrenschmidt 			; /* wait for char */
2869dae8afdSBenjamin Herrenschmidt 		return real_readb(&udbg_comport->rbr);
2879dae8afdSBenjamin Herrenschmidt 	}
2889dae8afdSBenjamin Herrenschmidt 	return -1;
2899dae8afdSBenjamin Herrenschmidt }
2909dae8afdSBenjamin Herrenschmidt 
2919dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void)
2929dae8afdSBenjamin Herrenschmidt {
2939dae8afdSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)
2949dae8afdSBenjamin Herrenschmidt 		CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR;
2959dae8afdSBenjamin Herrenschmidt 
2969dae8afdSBenjamin Herrenschmidt 	udbg_putc = udbg_40x_real_putc;
297af9c7249SAndrew Klossner 	udbg_flush = udbg_40x_real_flush;
2989dae8afdSBenjamin Herrenschmidt 	udbg_getc = udbg_40x_real_getc;
2999dae8afdSBenjamin Herrenschmidt 	udbg_getc_poll = NULL;
3009dae8afdSBenjamin Herrenschmidt }
3019dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
302a0496d45SJack Miller 
303a0496d45SJack Miller #ifdef CONFIG_PPC_EARLY_DEBUG_WSP
304a0496d45SJack Miller static void udbg_wsp_flush(void)
305a0496d45SJack Miller {
306a0496d45SJack Miller 	if (udbg_comport) {
307a0496d45SJack Miller 		while ((readb(&udbg_comport->lsr) & LSR_THRE) == 0)
308a0496d45SJack Miller 			/* wait for idle */;
309a0496d45SJack Miller 	}
310a0496d45SJack Miller }
311a0496d45SJack Miller 
312a0496d45SJack Miller static void udbg_wsp_putc(char c)
313a0496d45SJack Miller {
314a0496d45SJack Miller 	if (udbg_comport) {
315a0496d45SJack Miller 		if (c == '\n')
316a0496d45SJack Miller 			udbg_wsp_putc('\r');
317a0496d45SJack Miller 		udbg_wsp_flush();
318a0496d45SJack Miller 		writeb(c, &udbg_comport->thr); eieio();
319a0496d45SJack Miller 	}
320a0496d45SJack Miller }
321a0496d45SJack Miller 
322a0496d45SJack Miller static int udbg_wsp_getc(void)
323a0496d45SJack Miller {
324a0496d45SJack Miller 	if (udbg_comport) {
325a0496d45SJack Miller 		while ((readb(&udbg_comport->lsr) & LSR_DR) == 0)
326a0496d45SJack Miller 			; /* wait for char */
327a0496d45SJack Miller 		return readb(&udbg_comport->rbr);
328a0496d45SJack Miller 	}
329a0496d45SJack Miller 	return -1;
330a0496d45SJack Miller }
331a0496d45SJack Miller 
332a0496d45SJack Miller static int udbg_wsp_getc_poll(void)
333a0496d45SJack Miller {
334a0496d45SJack Miller 	if (udbg_comport)
335a0496d45SJack Miller 		if (readb(&udbg_comport->lsr) & LSR_DR)
336a0496d45SJack Miller 			return readb(&udbg_comport->rbr);
337a0496d45SJack Miller 	return -1;
338a0496d45SJack Miller }
339a0496d45SJack Miller 
340a0496d45SJack Miller void __init udbg_init_wsp(void)
341a0496d45SJack Miller {
342a0496d45SJack Miller 	udbg_comport = (struct NS16550 __iomem *)WSP_UART_VIRT;
343a0496d45SJack Miller 
344a0496d45SJack Miller 	udbg_init_uart(udbg_comport, 57600, 50000000);
345a0496d45SJack Miller 
346a0496d45SJack Miller 	udbg_putc = udbg_wsp_putc;
347a0496d45SJack Miller 	udbg_flush = udbg_wsp_flush;
348a0496d45SJack Miller 	udbg_getc = udbg_wsp_getc;
349a0496d45SJack Miller 	udbg_getc_poll = udbg_wsp_getc_poll;
350a0496d45SJack Miller }
351a0496d45SJack Miller #endif /* CONFIG_PPC_EARLY_DEBUG_WSP */
352