| /* | 
 |  * udbg for 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 */ |