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