|  | /* | 
|  | * udbg for NS16550 compatible 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> | 
|  | #include <asm/reg_a2.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 */ | 
|  |  | 
|  | #ifdef CONFIG_PPC_EARLY_DEBUG_WSP | 
|  | static void udbg_wsp_flush(void) | 
|  | { | 
|  | if (udbg_comport) { | 
|  | while ((readb(&udbg_comport->lsr) & LSR_THRE) == 0) | 
|  | /* wait for idle */; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void udbg_wsp_putc(char c) | 
|  | { | 
|  | if (udbg_comport) { | 
|  | if (c == '\n') | 
|  | udbg_wsp_putc('\r'); | 
|  | udbg_wsp_flush(); | 
|  | writeb(c, &udbg_comport->thr); eieio(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int udbg_wsp_getc(void) | 
|  | { | 
|  | if (udbg_comport) { | 
|  | while ((readb(&udbg_comport->lsr) & LSR_DR) == 0) | 
|  | ; /* wait for char */ | 
|  | return readb(&udbg_comport->rbr); | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int udbg_wsp_getc_poll(void) | 
|  | { | 
|  | if (udbg_comport) | 
|  | if (readb(&udbg_comport->lsr) & LSR_DR) | 
|  | return readb(&udbg_comport->rbr); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | void __init udbg_init_wsp(void) | 
|  | { | 
|  | udbg_comport = (struct NS16550 __iomem *)WSP_UART_VIRT; | 
|  |  | 
|  | udbg_init_uart(udbg_comport, 57600, 50000000); | 
|  |  | 
|  | udbg_putc = udbg_wsp_putc; | 
|  | udbg_flush = udbg_wsp_flush; | 
|  | udbg_getc = udbg_wsp_getc; | 
|  | udbg_getc_poll = udbg_wsp_getc_poll; | 
|  | } | 
|  | #endif /* CONFIG_PPC_EARLY_DEBUG_WSP */ |