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