301 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * udbg for NS16550 compatable serial ports
 | |
|  *
 | |
|  * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp
 | |
|  *
 | |
|  *      This program is free software; you can redistribute it and/or
 | |
|  *      modify it under the terms of the GNU General Public License
 | |
|  *      as published by the Free Software Foundation; either version
 | |
|  *      2 of the License, or (at your option) any later version.
 | |
|  */
 | |
| #include <linux/types.h>
 | |
| #include <asm/udbg.h>
 | |
| #include <asm/io.h>
 | |
| 
 | |
| extern u8 real_readb(volatile u8 __iomem  *addr);
 | |
| extern void real_writeb(u8 data, volatile u8 __iomem *addr);
 | |
| extern u8 real_205_readb(volatile u8 __iomem  *addr);
 | |
| extern void real_205_writeb(u8 data, volatile u8 __iomem *addr);
 | |
| 
 | |
| struct NS16550 {
 | |
| 	/* this struct must be packed */
 | |
| 	unsigned char rbr;  /* 0 */
 | |
| 	unsigned char ier;  /* 1 */
 | |
| 	unsigned char fcr;  /* 2 */
 | |
| 	unsigned char lcr;  /* 3 */
 | |
| 	unsigned char mcr;  /* 4 */
 | |
| 	unsigned char lsr;  /* 5 */
 | |
| 	unsigned char msr;  /* 6 */
 | |
| 	unsigned char scr;  /* 7 */
 | |
| };
 | |
| 
 | |
| #define thr rbr
 | |
| #define iir fcr
 | |
| #define dll rbr
 | |
| #define dlm ier
 | |
| #define dlab lcr
 | |
| 
 | |
| #define LSR_DR   0x01  /* Data ready */
 | |
| #define LSR_OE   0x02  /* Overrun */
 | |
| #define LSR_PE   0x04  /* Parity error */
 | |
| #define LSR_FE   0x08  /* Framing error */
 | |
| #define LSR_BI   0x10  /* Break */
 | |
| #define LSR_THRE 0x20  /* Xmit holding register empty */
 | |
| #define LSR_TEMT 0x40  /* Xmitter empty */
 | |
| #define LSR_ERR  0x80  /* Error */
 | |
| 
 | |
| #define LCR_DLAB 0x80
 | |
| 
 | |
| static struct NS16550 __iomem *udbg_comport;
 | |
| 
 | |
| static void udbg_550_flush(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
 | |
| 			/* wait for idle */;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void udbg_550_putc(char c)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if (c == '\n')
 | |
| 			udbg_550_putc('\r');
 | |
| 		udbg_550_flush();
 | |
| 		out_8(&udbg_comport->thr, c);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int udbg_550_getc_poll(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0)
 | |
| 			return in_8(&udbg_comport->rbr);
 | |
| 		else
 | |
| 			return -1;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int udbg_550_getc(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
 | |
| 			/* wait for char */;
 | |
| 		return in_8(&udbg_comport->rbr);
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void udbg_init_uart(void __iomem *comport, unsigned int speed,
 | |
| 		    unsigned int clock)
 | |
| {
 | |
| 	unsigned int dll, base_bauds;
 | |
| 
 | |
| 	if (clock == 0)
 | |
| 		clock = 1843200;
 | |
| 	if (speed == 0)
 | |
| 		speed = 9600;
 | |
| 
 | |
| 	base_bauds = clock / 16;
 | |
| 	dll = base_bauds / speed;
 | |
| 
 | |
| 	if (comport) {
 | |
| 		udbg_comport = (struct NS16550 __iomem *)comport;
 | |
| 		out_8(&udbg_comport->lcr, 0x00);
 | |
| 		out_8(&udbg_comport->ier, 0xff);
 | |
| 		out_8(&udbg_comport->ier, 0x00);
 | |
| 		out_8(&udbg_comport->lcr, LCR_DLAB);
 | |
| 		out_8(&udbg_comport->dll, dll & 0xff);
 | |
| 		out_8(&udbg_comport->dlm, dll >> 8);
 | |
| 		/* 8 data, 1 stop, no parity */
 | |
| 		out_8(&udbg_comport->lcr, 0x03);
 | |
| 		/* RTS/DTR */
 | |
| 		out_8(&udbg_comport->mcr, 0x03);
 | |
| 		/* Clear & enable FIFOs */
 | |
| 		out_8(&udbg_comport->fcr ,0x07);
 | |
| 		udbg_putc = udbg_550_putc;
 | |
| 		udbg_flush = udbg_550_flush;
 | |
| 		udbg_getc = udbg_550_getc;
 | |
| 		udbg_getc_poll = udbg_550_getc_poll;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
 | |
| {
 | |
| 	unsigned int dll, dlm, divisor, prescaler, speed;
 | |
| 	u8 old_lcr;
 | |
| 	struct NS16550 __iomem *port = comport;
 | |
| 
 | |
| 	old_lcr = in_8(&port->lcr);
 | |
| 
 | |
| 	/* select divisor latch registers.  */
 | |
| 	out_8(&port->lcr, LCR_DLAB);
 | |
| 
 | |
| 	/* now, read the divisor */
 | |
| 	dll = in_8(&port->dll);
 | |
| 	dlm = in_8(&port->dlm);
 | |
| 	divisor = dlm << 8 | dll;
 | |
| 
 | |
| 	/* check prescaling */
 | |
| 	if (in_8(&port->mcr) & 0x80)
 | |
| 		prescaler = 4;
 | |
| 	else
 | |
| 		prescaler = 1;
 | |
| 
 | |
| 	/* restore the LCR */
 | |
| 	out_8(&port->lcr, old_lcr);
 | |
| 
 | |
| 	/* calculate speed */
 | |
| 	speed = (clock / prescaler) / (divisor * 16);
 | |
| 
 | |
| 	/* sanity check */
 | |
| 	if (speed > (clock / 16))
 | |
| 		speed = 9600;
 | |
| 
 | |
| 	return speed;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_PPC_MAPLE
 | |
| void udbg_maple_real_flush(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
 | |
| 			/* wait for idle */;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void udbg_maple_real_putc(char c)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if (c == '\n')
 | |
| 			udbg_maple_real_putc('\r');
 | |
| 		udbg_maple_real_flush();
 | |
| 		real_writeb(c, &udbg_comport->thr); eieio();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void __init udbg_init_maple_realmode(void)
 | |
| {
 | |
| 	udbg_comport = (struct NS16550 __iomem *)0xf40003f8;
 | |
| 
 | |
| 	udbg_putc = udbg_maple_real_putc;
 | |
| 	udbg_flush = udbg_maple_real_flush;
 | |
| 	udbg_getc = NULL;
 | |
| 	udbg_getc_poll = NULL;
 | |
| }
 | |
| #endif /* CONFIG_PPC_MAPLE */
 | |
| 
 | |
| #ifdef CONFIG_PPC_PASEMI
 | |
| void udbg_pas_real_flush(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
 | |
| 			/* wait for idle */;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void udbg_pas_real_putc(char c)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if (c == '\n')
 | |
| 			udbg_pas_real_putc('\r');
 | |
| 		udbg_pas_real_flush();
 | |
| 		real_205_writeb(c, &udbg_comport->thr); eieio();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void udbg_init_pas_realmode(void)
 | |
| {
 | |
| 	udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL;
 | |
| 
 | |
| 	udbg_putc = udbg_pas_real_putc;
 | |
| 	udbg_flush = udbg_pas_real_flush;
 | |
| 	udbg_getc = NULL;
 | |
| 	udbg_getc_poll = NULL;
 | |
| }
 | |
| #endif /* CONFIG_PPC_MAPLE */
 | |
| 
 | |
| #ifdef CONFIG_PPC_EARLY_DEBUG_44x
 | |
| #include <platforms/44x/44x.h>
 | |
| 
 | |
| static void udbg_44x_as1_flush(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
 | |
| 			/* wait for idle */;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void udbg_44x_as1_putc(char c)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if (c == '\n')
 | |
| 			udbg_44x_as1_putc('\r');
 | |
| 		udbg_44x_as1_flush();
 | |
| 		as1_writeb(c, &udbg_comport->thr); eieio();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int udbg_44x_as1_getc(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0)
 | |
| 			; /* wait for char */
 | |
| 		return as1_readb(&udbg_comport->rbr);
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void __init udbg_init_44x_as1(void)
 | |
| {
 | |
| 	udbg_comport =
 | |
| 		(struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR;
 | |
| 
 | |
| 	udbg_putc = udbg_44x_as1_putc;
 | |
| 	udbg_flush = udbg_44x_as1_flush;
 | |
| 	udbg_getc = udbg_44x_as1_getc;
 | |
| }
 | |
| #endif /* CONFIG_PPC_EARLY_DEBUG_44x */
 | |
| 
 | |
| #ifdef CONFIG_PPC_EARLY_DEBUG_40x
 | |
| static void udbg_40x_real_flush(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
 | |
| 			/* wait for idle */;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void udbg_40x_real_putc(char c)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		if (c == '\n')
 | |
| 			udbg_40x_real_putc('\r');
 | |
| 		udbg_40x_real_flush();
 | |
| 		real_writeb(c, &udbg_comport->thr); eieio();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int udbg_40x_real_getc(void)
 | |
| {
 | |
| 	if (udbg_comport) {
 | |
| 		while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0)
 | |
| 			; /* wait for char */
 | |
| 		return real_readb(&udbg_comport->rbr);
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void __init udbg_init_40x_realmode(void)
 | |
| {
 | |
| 	udbg_comport = (struct NS16550 __iomem *)
 | |
| 		CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR;
 | |
| 
 | |
| 	udbg_putc = udbg_40x_real_putc;
 | |
| 	udbg_flush = udbg_40x_real_flush;
 | |
| 	udbg_getc = udbg_40x_real_getc;
 | |
| 	udbg_getc_poll = NULL;
 | |
| }
 | |
| #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
 |