xref: /openbmc/linux/arch/powerpc/kernel/udbg_16550.c (revision 9dae8afd)
135cd8785SDavid Gibson /*
235cd8785SDavid Gibson  * udbg for for NS16550 compatable 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>
1435cd8785SDavid Gibson 
1535cd8785SDavid Gibson extern u8 real_readb(volatile u8 __iomem  *addr);
1635cd8785SDavid Gibson extern void real_writeb(u8 data, volatile u8 __iomem *addr);
1739c870d5SOlof Johansson extern u8 real_205_readb(volatile u8 __iomem  *addr);
1839c870d5SOlof Johansson extern void real_205_writeb(u8 data, volatile u8 __iomem *addr);
1935cd8785SDavid Gibson 
2035cd8785SDavid Gibson struct NS16550 {
2135cd8785SDavid Gibson 	/* this struct must be packed */
2235cd8785SDavid Gibson 	unsigned char rbr;  /* 0 */
2335cd8785SDavid Gibson 	unsigned char ier;  /* 1 */
2435cd8785SDavid Gibson 	unsigned char fcr;  /* 2 */
2535cd8785SDavid Gibson 	unsigned char lcr;  /* 3 */
2635cd8785SDavid Gibson 	unsigned char mcr;  /* 4 */
2735cd8785SDavid Gibson 	unsigned char lsr;  /* 5 */
2835cd8785SDavid Gibson 	unsigned char msr;  /* 6 */
2935cd8785SDavid Gibson 	unsigned char scr;  /* 7 */
3035cd8785SDavid Gibson };
3135cd8785SDavid Gibson 
3235cd8785SDavid Gibson #define thr rbr
3335cd8785SDavid Gibson #define iir fcr
3435cd8785SDavid Gibson #define dll rbr
3535cd8785SDavid Gibson #define dlm ier
3635cd8785SDavid Gibson #define dlab lcr
3735cd8785SDavid Gibson 
3835cd8785SDavid Gibson #define LSR_DR   0x01  /* Data ready */
3935cd8785SDavid Gibson #define LSR_OE   0x02  /* Overrun */
4035cd8785SDavid Gibson #define LSR_PE   0x04  /* Parity error */
4135cd8785SDavid Gibson #define LSR_FE   0x08  /* Framing error */
4235cd8785SDavid Gibson #define LSR_BI   0x10  /* Break */
4335cd8785SDavid Gibson #define LSR_THRE 0x20  /* Xmit holding register empty */
4435cd8785SDavid Gibson #define LSR_TEMT 0x40  /* Xmitter empty */
4535cd8785SDavid Gibson #define LSR_ERR  0x80  /* Error */
4635cd8785SDavid Gibson 
47463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80
48463ce0e1SBenjamin Herrenschmidt 
49f276b5baSBenjamin Herrenschmidt static struct NS16550 __iomem *udbg_comport;
5035cd8785SDavid Gibson 
5151d3082fSBenjamin Herrenschmidt static void udbg_550_putc(char c)
5235cd8785SDavid Gibson {
5335cd8785SDavid Gibson 	if (udbg_comport) {
5435cd8785SDavid Gibson 		while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
5535cd8785SDavid Gibson 			/* wait for idle */;
5635cd8785SDavid Gibson 		out_8(&udbg_comport->thr, c);
5735cd8785SDavid Gibson 		if (c == '\n')
5835cd8785SDavid Gibson 			udbg_550_putc('\r');
5935cd8785SDavid Gibson 	}
6035cd8785SDavid Gibson }
6135cd8785SDavid Gibson 
6235cd8785SDavid Gibson static int udbg_550_getc_poll(void)
6335cd8785SDavid Gibson {
6435cd8785SDavid Gibson 	if (udbg_comport) {
6535cd8785SDavid Gibson 		if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0)
6635cd8785SDavid Gibson 			return in_8(&udbg_comport->rbr);
6735cd8785SDavid Gibson 		else
6835cd8785SDavid Gibson 			return -1;
6935cd8785SDavid Gibson 	}
7035cd8785SDavid Gibson 	return -1;
7135cd8785SDavid Gibson }
7235cd8785SDavid Gibson 
73bb6b9b28SBenjamin Herrenschmidt static int udbg_550_getc(void)
7435cd8785SDavid Gibson {
7535cd8785SDavid Gibson 	if (udbg_comport) {
7635cd8785SDavid Gibson 		while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
7735cd8785SDavid Gibson 			/* wait for char */;
7835cd8785SDavid Gibson 		return in_8(&udbg_comport->rbr);
7935cd8785SDavid Gibson 	}
80bb6b9b28SBenjamin Herrenschmidt 	return -1;
8135cd8785SDavid Gibson }
8235cd8785SDavid Gibson 
83463ce0e1SBenjamin Herrenschmidt void udbg_init_uart(void __iomem *comport, unsigned int speed,
84463ce0e1SBenjamin Herrenschmidt 		    unsigned int clock)
8535cd8785SDavid Gibson {
86171505daSBenjamin Herrenschmidt 	unsigned int dll, base_bauds;
87463ce0e1SBenjamin Herrenschmidt 
88171505daSBenjamin Herrenschmidt 	if (clock == 0)
89171505daSBenjamin Herrenschmidt 		clock = 1843200;
90463ce0e1SBenjamin Herrenschmidt 	if (speed == 0)
91463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
92171505daSBenjamin Herrenschmidt 
93171505daSBenjamin Herrenschmidt 	base_bauds = clock / 16;
94463ce0e1SBenjamin Herrenschmidt 	dll = base_bauds / speed;
9535cd8785SDavid Gibson 
9635cd8785SDavid Gibson 	if (comport) {
9735cd8785SDavid Gibson 		udbg_comport = (struct NS16550 __iomem *)comport;
9835cd8785SDavid Gibson 		out_8(&udbg_comport->lcr, 0x00);
9935cd8785SDavid Gibson 		out_8(&udbg_comport->ier, 0xff);
10035cd8785SDavid Gibson 		out_8(&udbg_comport->ier, 0x00);
101463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->lcr, LCR_DLAB);
102463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->dll, dll & 0xff);
103463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->dlm, dll >> 8);
104463ce0e1SBenjamin Herrenschmidt 		/* 8 data, 1 stop, no parity */
105463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->lcr, 0x03);
106463ce0e1SBenjamin Herrenschmidt 		/* RTS/DTR */
107463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->mcr, 0x03);
108463ce0e1SBenjamin Herrenschmidt 		/* Clear & enable FIFOs */
109463ce0e1SBenjamin Herrenschmidt 		out_8(&udbg_comport->fcr ,0x07);
11035cd8785SDavid Gibson 		udbg_putc = udbg_550_putc;
11135cd8785SDavid Gibson 		udbg_getc = udbg_550_getc;
11235cd8785SDavid Gibson 		udbg_getc_poll = udbg_550_getc_poll;
11335cd8785SDavid Gibson 	}
11435cd8785SDavid Gibson }
11535cd8785SDavid Gibson 
116463ce0e1SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
117463ce0e1SBenjamin Herrenschmidt {
118463ce0e1SBenjamin Herrenschmidt 	unsigned int dll, dlm, divisor, prescaler, speed;
119463ce0e1SBenjamin Herrenschmidt 	u8 old_lcr;
120f276b5baSBenjamin Herrenschmidt 	struct NS16550 __iomem *port = comport;
121463ce0e1SBenjamin Herrenschmidt 
122463ce0e1SBenjamin Herrenschmidt 	old_lcr = in_8(&port->lcr);
123463ce0e1SBenjamin Herrenschmidt 
124463ce0e1SBenjamin Herrenschmidt 	/* select divisor latch registers.  */
125463ce0e1SBenjamin Herrenschmidt 	out_8(&port->lcr, LCR_DLAB);
126463ce0e1SBenjamin Herrenschmidt 
127463ce0e1SBenjamin Herrenschmidt 	/* now, read the divisor */
128463ce0e1SBenjamin Herrenschmidt 	dll = in_8(&port->dll);
129463ce0e1SBenjamin Herrenschmidt 	dlm = in_8(&port->dlm);
130463ce0e1SBenjamin Herrenschmidt 	divisor = dlm << 8 | dll;
131463ce0e1SBenjamin Herrenschmidt 
132463ce0e1SBenjamin Herrenschmidt 	/* check prescaling */
133463ce0e1SBenjamin Herrenschmidt 	if (in_8(&port->mcr) & 0x80)
134463ce0e1SBenjamin Herrenschmidt 		prescaler = 4;
135463ce0e1SBenjamin Herrenschmidt 	else
136463ce0e1SBenjamin Herrenschmidt 		prescaler = 1;
137463ce0e1SBenjamin Herrenschmidt 
138463ce0e1SBenjamin Herrenschmidt 	/* restore the LCR */
139463ce0e1SBenjamin Herrenschmidt 	out_8(&port->lcr, old_lcr);
140463ce0e1SBenjamin Herrenschmidt 
141463ce0e1SBenjamin Herrenschmidt 	/* calculate speed */
142463ce0e1SBenjamin Herrenschmidt 	speed = (clock / prescaler) / (divisor * 16);
143463ce0e1SBenjamin Herrenschmidt 
144463ce0e1SBenjamin Herrenschmidt 	/* sanity check */
145d0e132b5SKumar Gala 	if (speed < 0 || speed > (clock / 16))
146463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
147463ce0e1SBenjamin Herrenschmidt 
148463ce0e1SBenjamin Herrenschmidt 	return speed;
149463ce0e1SBenjamin Herrenschmidt }
150463ce0e1SBenjamin Herrenschmidt 
15135cd8785SDavid Gibson #ifdef CONFIG_PPC_MAPLE
1524009d980SOlaf Hering void udbg_maple_real_putc(char c)
15335cd8785SDavid Gibson {
15435cd8785SDavid Gibson 	if (udbg_comport) {
15535cd8785SDavid Gibson 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
15635cd8785SDavid Gibson 			/* wait for idle */;
15735cd8785SDavid Gibson 		real_writeb(c, &udbg_comport->thr); eieio();
15835cd8785SDavid Gibson 		if (c == '\n')
15935cd8785SDavid Gibson 			udbg_maple_real_putc('\r');
16035cd8785SDavid Gibson 	}
16135cd8785SDavid Gibson }
16235cd8785SDavid Gibson 
163296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void)
16435cd8785SDavid Gibson {
165f276b5baSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)0xf40003f8;
16635cd8785SDavid Gibson 
16735cd8785SDavid Gibson 	udbg_putc = udbg_maple_real_putc;
16835cd8785SDavid Gibson 	udbg_getc = NULL;
16935cd8785SDavid Gibson 	udbg_getc_poll = NULL;
17035cd8785SDavid Gibson }
17135cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */
17239c870d5SOlof Johansson 
17339c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI
17439c870d5SOlof Johansson void udbg_pas_real_putc(char c)
17539c870d5SOlof Johansson {
17639c870d5SOlof Johansson 	if (udbg_comport) {
17739c870d5SOlof Johansson 		while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
17839c870d5SOlof Johansson 			/* wait for idle */;
17939c870d5SOlof Johansson 		real_205_writeb(c, &udbg_comport->thr); eieio();
18039c870d5SOlof Johansson 		if (c == '\n')
18139c870d5SOlof Johansson 			udbg_pas_real_putc('\r');
18239c870d5SOlof Johansson 	}
18339c870d5SOlof Johansson }
18439c870d5SOlof Johansson 
18539c870d5SOlof Johansson void udbg_init_pas_realmode(void)
18639c870d5SOlof Johansson {
187f276b5baSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL;
18839c870d5SOlof Johansson 
18939c870d5SOlof Johansson 	udbg_putc = udbg_pas_real_putc;
19039c870d5SOlof Johansson 	udbg_getc = NULL;
19139c870d5SOlof Johansson 	udbg_getc_poll = NULL;
19239c870d5SOlof Johansson }
19339c870d5SOlof Johansson #endif /* CONFIG_PPC_MAPLE */
194d9b55a03SDavid Gibson 
195d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x
196d9b55a03SDavid Gibson #include <platforms/44x/44x.h>
197d9b55a03SDavid Gibson 
198d9b55a03SDavid Gibson static void udbg_44x_as1_putc(char c)
199d9b55a03SDavid Gibson {
200d9b55a03SDavid Gibson 	if (udbg_comport) {
201d9b55a03SDavid Gibson 		while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
202d9b55a03SDavid Gibson 			/* wait for idle */;
203d9b55a03SDavid Gibson 		as1_writeb(c, &udbg_comport->thr); eieio();
204d9b55a03SDavid Gibson 		if (c == '\n')
205d9b55a03SDavid Gibson 			udbg_44x_as1_putc('\r');
206d9b55a03SDavid Gibson 	}
207d9b55a03SDavid Gibson }
208d9b55a03SDavid Gibson 
20970dea47dSHollis Blanchard static int udbg_44x_as1_getc(void)
21070dea47dSHollis Blanchard {
21170dea47dSHollis Blanchard 	if (udbg_comport) {
21270dea47dSHollis Blanchard 		while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0)
21370dea47dSHollis Blanchard 			; /* wait for char */
21470dea47dSHollis Blanchard 		return as1_readb(&udbg_comport->rbr);
21570dea47dSHollis Blanchard 	}
21670dea47dSHollis Blanchard 	return -1;
21770dea47dSHollis Blanchard }
21870dea47dSHollis Blanchard 
219d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void)
220d9b55a03SDavid Gibson {
221d9b55a03SDavid Gibson 	udbg_comport =
222f276b5baSBenjamin Herrenschmidt 		(struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR;
223d9b55a03SDavid Gibson 
224d9b55a03SDavid Gibson 	udbg_putc = udbg_44x_as1_putc;
22570dea47dSHollis Blanchard 	udbg_getc = udbg_44x_as1_getc;
226d9b55a03SDavid Gibson }
227d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */
2289dae8afdSBenjamin Herrenschmidt 
2299dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x
2309dae8afdSBenjamin Herrenschmidt static void udbg_40x_real_putc(char c)
2319dae8afdSBenjamin Herrenschmidt {
2329dae8afdSBenjamin Herrenschmidt 	if (udbg_comport) {
2339dae8afdSBenjamin Herrenschmidt 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
2349dae8afdSBenjamin Herrenschmidt 			/* wait for idle */;
2359dae8afdSBenjamin Herrenschmidt 		real_writeb(c, &udbg_comport->thr); eieio();
2369dae8afdSBenjamin Herrenschmidt 		if (c == '\n')
2379dae8afdSBenjamin Herrenschmidt 			udbg_40x_real_putc('\r');
2389dae8afdSBenjamin Herrenschmidt 	}
2399dae8afdSBenjamin Herrenschmidt }
2409dae8afdSBenjamin Herrenschmidt 
2419dae8afdSBenjamin Herrenschmidt static int udbg_40x_real_getc(void)
2429dae8afdSBenjamin Herrenschmidt {
2439dae8afdSBenjamin Herrenschmidt 	if (udbg_comport) {
2449dae8afdSBenjamin Herrenschmidt 		while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0)
2459dae8afdSBenjamin Herrenschmidt 			; /* wait for char */
2469dae8afdSBenjamin Herrenschmidt 		return real_readb(&udbg_comport->rbr);
2479dae8afdSBenjamin Herrenschmidt 	}
2489dae8afdSBenjamin Herrenschmidt 	return -1;
2499dae8afdSBenjamin Herrenschmidt }
2509dae8afdSBenjamin Herrenschmidt 
2519dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void)
2529dae8afdSBenjamin Herrenschmidt {
2539dae8afdSBenjamin Herrenschmidt 	udbg_comport = (struct NS16550 __iomem *)
2549dae8afdSBenjamin Herrenschmidt 		CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR;
2559dae8afdSBenjamin Herrenschmidt 
2569dae8afdSBenjamin Herrenschmidt 	udbg_putc = udbg_40x_real_putc;
2579dae8afdSBenjamin Herrenschmidt 	udbg_getc = udbg_40x_real_getc;
2589dae8afdSBenjamin Herrenschmidt 	udbg_getc_poll = NULL;
2599dae8afdSBenjamin Herrenschmidt }
2609dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
261