xref: /openbmc/linux/arch/powerpc/kernel/udbg_16550.c (revision cd32e2dc)
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 
2130925748SBenjamin Herrenschmidt #define UART_RBR	0
2230925748SBenjamin Herrenschmidt #define UART_IER	1
2330925748SBenjamin Herrenschmidt #define UART_FCR	2
2430925748SBenjamin Herrenschmidt #define UART_LCR	3
2530925748SBenjamin Herrenschmidt #define UART_MCR	4
2630925748SBenjamin Herrenschmidt #define UART_LSR	5
2730925748SBenjamin Herrenschmidt #define UART_MSR	6
2830925748SBenjamin Herrenschmidt #define UART_SCR	7
2930925748SBenjamin Herrenschmidt #define UART_THR	UART_RBR
3030925748SBenjamin Herrenschmidt #define UART_IIR	UART_FCR
3130925748SBenjamin Herrenschmidt #define UART_DLL	UART_RBR
3230925748SBenjamin Herrenschmidt #define UART_DLM	UART_IER
3330925748SBenjamin Herrenschmidt #define UART_DLAB	UART_LCR
3435cd8785SDavid Gibson 
3535cd8785SDavid Gibson #define LSR_DR   0x01  /* Data ready */
3635cd8785SDavid Gibson #define LSR_OE   0x02  /* Overrun */
3735cd8785SDavid Gibson #define LSR_PE   0x04  /* Parity error */
3835cd8785SDavid Gibson #define LSR_FE   0x08  /* Framing error */
3935cd8785SDavid Gibson #define LSR_BI   0x10  /* Break */
4035cd8785SDavid Gibson #define LSR_THRE 0x20  /* Xmit holding register empty */
4135cd8785SDavid Gibson #define LSR_TEMT 0x40  /* Xmitter empty */
4235cd8785SDavid Gibson #define LSR_ERR  0x80  /* Error */
4335cd8785SDavid Gibson 
44463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80
45463ce0e1SBenjamin Herrenschmidt 
4630925748SBenjamin Herrenschmidt static u8 (*udbg_uart_in)(unsigned int reg);
4730925748SBenjamin Herrenschmidt static void (*udbg_uart_out)(unsigned int reg, u8 data);
4835cd8785SDavid Gibson 
4930925748SBenjamin Herrenschmidt static void udbg_uart_flush(void)
5035cd8785SDavid Gibson {
5130925748SBenjamin Herrenschmidt 	if (!udbg_uart_in)
5230925748SBenjamin Herrenschmidt 		return;
5330925748SBenjamin Herrenschmidt 
5430925748SBenjamin Herrenschmidt 	/* wait for idle */
5530925748SBenjamin Herrenschmidt 	while ((udbg_uart_in(UART_LSR) & LSR_THRE) == 0)
5630925748SBenjamin Herrenschmidt 		cpu_relax();
57af9c7249SAndrew Klossner }
58af9c7249SAndrew Klossner 
5930925748SBenjamin Herrenschmidt static void udbg_uart_putc(char c)
60af9c7249SAndrew Klossner {
6130925748SBenjamin Herrenschmidt 	if (!udbg_uart_out)
6230925748SBenjamin Herrenschmidt 		return;
6330925748SBenjamin Herrenschmidt 
6435cd8785SDavid Gibson 	if (c == '\n')
6530925748SBenjamin Herrenschmidt 		udbg_uart_putc('\r');
6630925748SBenjamin Herrenschmidt 	udbg_uart_flush();
6730925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_THR, c);
6835cd8785SDavid Gibson }
6935cd8785SDavid Gibson 
7030925748SBenjamin Herrenschmidt static int udbg_uart_getc_poll(void)
7135cd8785SDavid Gibson {
72cd32e2dcSAnton Blanchard 	if (!udbg_uart_in)
73cd32e2dcSAnton Blanchard 		return -1;
74cd32e2dcSAnton Blanchard 
75cd32e2dcSAnton Blanchard 	if (!(udbg_uart_in(UART_LSR) & LSR_DR))
7630925748SBenjamin Herrenschmidt 		return udbg_uart_in(UART_RBR);
77cd32e2dcSAnton Blanchard 
7835cd8785SDavid Gibson 	return -1;
7935cd8785SDavid Gibson }
8035cd8785SDavid Gibson 
8130925748SBenjamin Herrenschmidt static int udbg_uart_getc(void)
8235cd8785SDavid Gibson {
8330925748SBenjamin Herrenschmidt 	if (!udbg_uart_in)
84bb6b9b28SBenjamin Herrenschmidt 		return -1;
8530925748SBenjamin Herrenschmidt 	/* wait for char */
8630925748SBenjamin Herrenschmidt 	while (!(udbg_uart_in(UART_LSR) & LSR_DR))
8730925748SBenjamin Herrenschmidt 		cpu_relax();
8830925748SBenjamin Herrenschmidt 	return udbg_uart_in(UART_RBR);
8935cd8785SDavid Gibson }
9035cd8785SDavid Gibson 
9130925748SBenjamin Herrenschmidt static void udbg_use_uart(void)
9230925748SBenjamin Herrenschmidt {
9330925748SBenjamin Herrenschmidt 	udbg_putc = udbg_uart_putc;
9430925748SBenjamin Herrenschmidt 	udbg_flush = udbg_uart_flush;
9530925748SBenjamin Herrenschmidt 	udbg_getc = udbg_uart_getc;
9630925748SBenjamin Herrenschmidt 	udbg_getc_poll = udbg_uart_getc_poll;
9730925748SBenjamin Herrenschmidt }
9830925748SBenjamin Herrenschmidt 
9930925748SBenjamin Herrenschmidt void udbg_uart_setup(unsigned int speed, unsigned int clock)
10035cd8785SDavid Gibson {
101171505daSBenjamin Herrenschmidt 	unsigned int dll, base_bauds;
102463ce0e1SBenjamin Herrenschmidt 
10330925748SBenjamin Herrenschmidt 	if (!udbg_uart_out)
10430925748SBenjamin Herrenschmidt 		return;
10530925748SBenjamin Herrenschmidt 
106171505daSBenjamin Herrenschmidt 	if (clock == 0)
107171505daSBenjamin Herrenschmidt 		clock = 1843200;
108463ce0e1SBenjamin Herrenschmidt 	if (speed == 0)
109463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
110171505daSBenjamin Herrenschmidt 
111171505daSBenjamin Herrenschmidt 	base_bauds = clock / 16;
112463ce0e1SBenjamin Herrenschmidt 	dll = base_bauds / speed;
11335cd8785SDavid Gibson 
11430925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_LCR, 0x00);
11530925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_IER, 0xff);
11630925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_IER, 0x00);
11730925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_LCR, LCR_DLAB);
11830925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_DLL, dll & 0xff);
11930925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_DLM, dll >> 8);
120463ce0e1SBenjamin Herrenschmidt 	/* 8 data, 1 stop, no parity */
12130925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_LCR, 0x3);
122463ce0e1SBenjamin Herrenschmidt 	/* RTS/DTR */
12330925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_MCR, 0x3);
124463ce0e1SBenjamin Herrenschmidt 	/* Clear & enable FIFOs */
12530925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_FCR, 0x7);
12635cd8785SDavid Gibson }
12735cd8785SDavid Gibson 
12830925748SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(unsigned int clock)
129463ce0e1SBenjamin Herrenschmidt {
130463ce0e1SBenjamin Herrenschmidt 	unsigned int dll, dlm, divisor, prescaler, speed;
131463ce0e1SBenjamin Herrenschmidt 	u8 old_lcr;
132463ce0e1SBenjamin Herrenschmidt 
13330925748SBenjamin Herrenschmidt 	old_lcr = udbg_uart_in(UART_LCR);
134463ce0e1SBenjamin Herrenschmidt 
135463ce0e1SBenjamin Herrenschmidt 	/* select divisor latch registers.  */
13630925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_LCR, old_lcr | LCR_DLAB);
137463ce0e1SBenjamin Herrenschmidt 
138463ce0e1SBenjamin Herrenschmidt 	/* now, read the divisor */
13930925748SBenjamin Herrenschmidt 	dll = udbg_uart_in(UART_DLL);
14030925748SBenjamin Herrenschmidt 	dlm = udbg_uart_in(UART_DLM);
141463ce0e1SBenjamin Herrenschmidt 	divisor = dlm << 8 | dll;
142463ce0e1SBenjamin Herrenschmidt 
143463ce0e1SBenjamin Herrenschmidt 	/* check prescaling */
14430925748SBenjamin Herrenschmidt 	if (udbg_uart_in(UART_MCR) & 0x80)
145463ce0e1SBenjamin Herrenschmidt 		prescaler = 4;
146463ce0e1SBenjamin Herrenschmidt 	else
147463ce0e1SBenjamin Herrenschmidt 		prescaler = 1;
148463ce0e1SBenjamin Herrenschmidt 
149463ce0e1SBenjamin Herrenschmidt 	/* restore the LCR */
15030925748SBenjamin Herrenschmidt 	udbg_uart_out(UART_LCR, old_lcr);
151463ce0e1SBenjamin Herrenschmidt 
152463ce0e1SBenjamin Herrenschmidt 	/* calculate speed */
153463ce0e1SBenjamin Herrenschmidt 	speed = (clock / prescaler) / (divisor * 16);
154463ce0e1SBenjamin Herrenschmidt 
155463ce0e1SBenjamin Herrenschmidt 	/* sanity check */
156bb5e6491Sroel kluin 	if (speed > (clock / 16))
157463ce0e1SBenjamin Herrenschmidt 		speed = 9600;
158463ce0e1SBenjamin Herrenschmidt 
159463ce0e1SBenjamin Herrenschmidt 	return speed;
160463ce0e1SBenjamin Herrenschmidt }
161463ce0e1SBenjamin Herrenschmidt 
16230925748SBenjamin Herrenschmidt static union {
16330925748SBenjamin Herrenschmidt 	unsigned char __iomem *mmio_base;
16430925748SBenjamin Herrenschmidt 	unsigned long pio_base;
16530925748SBenjamin Herrenschmidt } udbg_uart;
16630925748SBenjamin Herrenschmidt 
16730925748SBenjamin Herrenschmidt static unsigned int udbg_uart_stride = 1;
16830925748SBenjamin Herrenschmidt 
16930925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pio(unsigned int reg)
17035cd8785SDavid Gibson {
17130925748SBenjamin Herrenschmidt 	return inb(udbg_uart.pio_base + (reg * udbg_uart_stride));
172af9c7249SAndrew Klossner }
173af9c7249SAndrew Klossner 
17430925748SBenjamin Herrenschmidt static void udbg_uart_out_pio(unsigned int reg, u8 data)
175af9c7249SAndrew Klossner {
17630925748SBenjamin Herrenschmidt 	outb(data, udbg_uart.pio_base + (reg * udbg_uart_stride));
17735cd8785SDavid Gibson }
17830925748SBenjamin Herrenschmidt 
17930925748SBenjamin Herrenschmidt void udbg_uart_init_pio(unsigned long port, unsigned int stride)
18030925748SBenjamin Herrenschmidt {
18130925748SBenjamin Herrenschmidt 	if (!port)
18230925748SBenjamin Herrenschmidt 		return;
18330925748SBenjamin Herrenschmidt 	udbg_uart.pio_base = port;
18430925748SBenjamin Herrenschmidt 	udbg_uart_stride = stride;
18530925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_pio;
18630925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_pio;
18730925748SBenjamin Herrenschmidt 	udbg_use_uart();
18830925748SBenjamin Herrenschmidt }
18930925748SBenjamin Herrenschmidt 
19030925748SBenjamin Herrenschmidt static u8 udbg_uart_in_mmio(unsigned int reg)
19130925748SBenjamin Herrenschmidt {
19230925748SBenjamin Herrenschmidt 	return in_8(udbg_uart.mmio_base + (reg * udbg_uart_stride));
19330925748SBenjamin Herrenschmidt }
19430925748SBenjamin Herrenschmidt 
19530925748SBenjamin Herrenschmidt static void udbg_uart_out_mmio(unsigned int reg, u8 data)
19630925748SBenjamin Herrenschmidt {
19730925748SBenjamin Herrenschmidt 	out_8(udbg_uart.mmio_base + (reg * udbg_uart_stride), data);
19830925748SBenjamin Herrenschmidt }
19930925748SBenjamin Herrenschmidt 
20030925748SBenjamin Herrenschmidt 
20130925748SBenjamin Herrenschmidt void udbg_uart_init_mmio(void __iomem *addr, unsigned int stride)
20230925748SBenjamin Herrenschmidt {
20330925748SBenjamin Herrenschmidt 	if (!addr)
20430925748SBenjamin Herrenschmidt 		return;
20530925748SBenjamin Herrenschmidt 	udbg_uart.mmio_base = addr;
20630925748SBenjamin Herrenschmidt 	udbg_uart_stride = stride;
20730925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_mmio;
20830925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_mmio;
20930925748SBenjamin Herrenschmidt 	udbg_use_uart();
21030925748SBenjamin Herrenschmidt }
21130925748SBenjamin Herrenschmidt 
21230925748SBenjamin Herrenschmidt #ifdef CONFIG_PPC_MAPLE
21330925748SBenjamin Herrenschmidt 
21430925748SBenjamin Herrenschmidt #define UDBG_UART_MAPLE_ADDR	((void __iomem *)0xf40003f8)
21530925748SBenjamin Herrenschmidt 
21630925748SBenjamin Herrenschmidt static u8 udbg_uart_in_maple(unsigned int reg)
21730925748SBenjamin Herrenschmidt {
21830925748SBenjamin Herrenschmidt 	return real_readb(UDBG_UART_MAPLE_ADDR + reg);
21930925748SBenjamin Herrenschmidt }
22030925748SBenjamin Herrenschmidt 
22130925748SBenjamin Herrenschmidt static void udbg_uart_out_maple(unsigned int reg, u8 val)
22230925748SBenjamin Herrenschmidt {
22330925748SBenjamin Herrenschmidt 	real_writeb(val, UDBG_UART_MAPLE_ADDR + reg);
22435cd8785SDavid Gibson }
22535cd8785SDavid Gibson 
226296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void)
22735cd8785SDavid Gibson {
22830925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_maple;
22930925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_maple;
23030925748SBenjamin Herrenschmidt 	udbg_use_uart();
23135cd8785SDavid Gibson }
23230925748SBenjamin Herrenschmidt 
23335cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */
23439c870d5SOlof Johansson 
23539c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI
23630925748SBenjamin Herrenschmidt 
23730925748SBenjamin Herrenschmidt #define UDBG_UART_PAS_ADDR	((void __iomem *)0xfcff03f8UL)
23830925748SBenjamin Herrenschmidt 
23930925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pas(unsigned int reg)
24039c870d5SOlof Johansson {
24130925748SBenjamin Herrenschmidt 	return real_205_readb(UDBG_UART_PAS_ADDR + reg);
242af9c7249SAndrew Klossner }
243af9c7249SAndrew Klossner 
24430925748SBenjamin Herrenschmidt static void udbg_uart_out_pas(unsigned int reg, u8 val)
245af9c7249SAndrew Klossner {
24630925748SBenjamin Herrenschmidt 	real_205_writeb(val, UDBG_UART_PAS_ADDR + reg);
24739c870d5SOlof Johansson }
24839c870d5SOlof Johansson 
24930925748SBenjamin Herrenschmidt void __init udbg_init_pas_realmode(void)
25039c870d5SOlof Johansson {
25130925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_pas;
25230925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_pas;
25330925748SBenjamin Herrenschmidt 	udbg_use_uart();
25439c870d5SOlof Johansson }
25530925748SBenjamin Herrenschmidt 
25630925748SBenjamin Herrenschmidt #endif /* CONFIG_PPC_PASEMI */
257d9b55a03SDavid Gibson 
258d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x
25930925748SBenjamin Herrenschmidt 
260d9b55a03SDavid Gibson #include <platforms/44x/44x.h>
261d9b55a03SDavid Gibson 
26230925748SBenjamin Herrenschmidt static u8 udbg_uart_in_44x_as1(unsigned int reg)
263d9b55a03SDavid Gibson {
26430925748SBenjamin Herrenschmidt 	return as1_readb((void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg);
265af9c7249SAndrew Klossner }
266af9c7249SAndrew Klossner 
26730925748SBenjamin Herrenschmidt static void udbg_uart_out_44x_as1(unsigned int reg, u8 val)
268af9c7249SAndrew Klossner {
26930925748SBenjamin Herrenschmidt 	as1_writeb(val, (void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg);
27070dea47dSHollis Blanchard }
27170dea47dSHollis Blanchard 
272d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void)
273d9b55a03SDavid Gibson {
27430925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_44x_as1;
27530925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_44x_as1;
27630925748SBenjamin Herrenschmidt 	udbg_use_uart();
277d9b55a03SDavid Gibson }
27830925748SBenjamin Herrenschmidt 
279d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */
2809dae8afdSBenjamin Herrenschmidt 
2819dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x
28230925748SBenjamin Herrenschmidt 
28330925748SBenjamin Herrenschmidt static u8 udbg_uart_in_40x(unsigned int reg)
2849dae8afdSBenjamin Herrenschmidt {
28530925748SBenjamin Herrenschmidt 	return real_readb((void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR
28630925748SBenjamin Herrenschmidt 			  + reg);
287af9c7249SAndrew Klossner }
288af9c7249SAndrew Klossner 
28930925748SBenjamin Herrenschmidt static void udbg_uart_out_40x(unsigned int reg, u8 val)
290af9c7249SAndrew Klossner {
29130925748SBenjamin Herrenschmidt 	real_writeb(val, (void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR
29230925748SBenjamin Herrenschmidt 		    + reg);
2939dae8afdSBenjamin Herrenschmidt }
2949dae8afdSBenjamin Herrenschmidt 
2959dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void)
2969dae8afdSBenjamin Herrenschmidt {
29730925748SBenjamin Herrenschmidt 	udbg_uart_in = udbg_uart_in_40x;
29830925748SBenjamin Herrenschmidt 	udbg_uart_out = udbg_uart_out_40x;
29930925748SBenjamin Herrenschmidt 	udbg_use_uart();
3009dae8afdSBenjamin Herrenschmidt }
30130925748SBenjamin Herrenschmidt 
3029dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
303