/* * 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); 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 volatile struct NS16550 __iomem *udbg_comport; static void udbg_550_putc(char c) { if (udbg_comport) { while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; out_8(&udbg_comport->thr, c); if (c == '\n') udbg_550_putc('\r'); } } 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_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; volatile 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 < 0 || speed > (clock / 16)) speed = 9600; return speed; } #ifdef CONFIG_PPC_MAPLE void udbg_maple_real_putc(char c) { if (udbg_comport) { while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) /* wait for idle */; real_writeb(c, &udbg_comport->thr); eieio(); if (c == '\n') udbg_maple_real_putc('\r'); } } void __init udbg_init_maple_realmode(void) { udbg_comport = (volatile struct NS16550 __iomem *)0xf40003f8; udbg_putc = udbg_maple_real_putc; udbg_getc = NULL; udbg_getc_poll = NULL; } #endif /* CONFIG_PPC_MAPLE */