135cd8785SDavid Gibson /* 235cd8785SDavid Gibson * udbg for for NS16550 compatable serial ports 335cd8785SDavid Gibson * 435cd8785SDavid Gibson * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp 535cd8785SDavid Gibson * 635cd8785SDavid Gibson * This program is free software; you can redistribute it and/or 735cd8785SDavid Gibson * modify it under the terms of the GNU General Public License 835cd8785SDavid Gibson * as published by the Free Software Foundation; either version 935cd8785SDavid Gibson * 2 of the License, or (at your option) any later version. 1035cd8785SDavid Gibson */ 1135cd8785SDavid Gibson #include <linux/types.h> 1235cd8785SDavid Gibson #include <asm/udbg.h> 1335cd8785SDavid Gibson #include <asm/io.h> 1435cd8785SDavid Gibson 1535cd8785SDavid Gibson extern u8 real_readb(volatile u8 __iomem *addr); 1635cd8785SDavid Gibson extern void real_writeb(u8 data, volatile u8 __iomem *addr); 1739c870d5SOlof Johansson extern u8 real_205_readb(volatile u8 __iomem *addr); 1839c870d5SOlof Johansson extern void real_205_writeb(u8 data, volatile u8 __iomem *addr); 1935cd8785SDavid Gibson 2035cd8785SDavid Gibson struct NS16550 { 2135cd8785SDavid Gibson /* this struct must be packed */ 2235cd8785SDavid Gibson unsigned char rbr; /* 0 */ 2335cd8785SDavid Gibson unsigned char ier; /* 1 */ 2435cd8785SDavid Gibson unsigned char fcr; /* 2 */ 2535cd8785SDavid Gibson unsigned char lcr; /* 3 */ 2635cd8785SDavid Gibson unsigned char mcr; /* 4 */ 2735cd8785SDavid Gibson unsigned char lsr; /* 5 */ 2835cd8785SDavid Gibson unsigned char msr; /* 6 */ 2935cd8785SDavid Gibson unsigned char scr; /* 7 */ 3035cd8785SDavid Gibson }; 3135cd8785SDavid Gibson 3235cd8785SDavid Gibson #define thr rbr 3335cd8785SDavid Gibson #define iir fcr 3435cd8785SDavid Gibson #define dll rbr 3535cd8785SDavid Gibson #define dlm ier 3635cd8785SDavid Gibson #define dlab lcr 3735cd8785SDavid Gibson 3835cd8785SDavid Gibson #define LSR_DR 0x01 /* Data ready */ 3935cd8785SDavid Gibson #define LSR_OE 0x02 /* Overrun */ 4035cd8785SDavid Gibson #define LSR_PE 0x04 /* Parity error */ 4135cd8785SDavid Gibson #define LSR_FE 0x08 /* Framing error */ 4235cd8785SDavid Gibson #define LSR_BI 0x10 /* Break */ 4335cd8785SDavid Gibson #define LSR_THRE 0x20 /* Xmit holding register empty */ 4435cd8785SDavid Gibson #define LSR_TEMT 0x40 /* Xmitter empty */ 4535cd8785SDavid Gibson #define LSR_ERR 0x80 /* Error */ 4635cd8785SDavid Gibson 47463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80 48463ce0e1SBenjamin Herrenschmidt 4935cd8785SDavid Gibson static volatile struct NS16550 __iomem *udbg_comport; 5035cd8785SDavid Gibson 5151d3082fSBenjamin Herrenschmidt static void udbg_550_putc(char c) 5235cd8785SDavid Gibson { 5335cd8785SDavid Gibson if (udbg_comport) { 5435cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) 5535cd8785SDavid Gibson /* wait for idle */; 5635cd8785SDavid Gibson out_8(&udbg_comport->thr, c); 5735cd8785SDavid Gibson if (c == '\n') 5835cd8785SDavid Gibson udbg_550_putc('\r'); 5935cd8785SDavid Gibson } 6035cd8785SDavid Gibson } 6135cd8785SDavid Gibson 6235cd8785SDavid Gibson static int udbg_550_getc_poll(void) 6335cd8785SDavid Gibson { 6435cd8785SDavid Gibson if (udbg_comport) { 6535cd8785SDavid Gibson if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0) 6635cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 6735cd8785SDavid Gibson else 6835cd8785SDavid Gibson return -1; 6935cd8785SDavid Gibson } 7035cd8785SDavid Gibson return -1; 7135cd8785SDavid Gibson } 7235cd8785SDavid Gibson 73bb6b9b28SBenjamin Herrenschmidt static int udbg_550_getc(void) 7435cd8785SDavid Gibson { 7535cd8785SDavid Gibson if (udbg_comport) { 7635cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) 7735cd8785SDavid Gibson /* wait for char */; 7835cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 7935cd8785SDavid Gibson } 80bb6b9b28SBenjamin Herrenschmidt return -1; 8135cd8785SDavid Gibson } 8235cd8785SDavid Gibson 83463ce0e1SBenjamin Herrenschmidt void udbg_init_uart(void __iomem *comport, unsigned int speed, 84463ce0e1SBenjamin Herrenschmidt unsigned int clock) 8535cd8785SDavid Gibson { 86171505daSBenjamin Herrenschmidt unsigned int dll, base_bauds; 87463ce0e1SBenjamin Herrenschmidt 88171505daSBenjamin Herrenschmidt if (clock == 0) 89171505daSBenjamin Herrenschmidt clock = 1843200; 90463ce0e1SBenjamin Herrenschmidt if (speed == 0) 91463ce0e1SBenjamin Herrenschmidt speed = 9600; 92171505daSBenjamin Herrenschmidt 93171505daSBenjamin Herrenschmidt base_bauds = clock / 16; 94463ce0e1SBenjamin Herrenschmidt dll = base_bauds / speed; 9535cd8785SDavid Gibson 9635cd8785SDavid Gibson if (comport) { 9735cd8785SDavid Gibson udbg_comport = (struct NS16550 __iomem *)comport; 9835cd8785SDavid Gibson out_8(&udbg_comport->lcr, 0x00); 9935cd8785SDavid Gibson out_8(&udbg_comport->ier, 0xff); 10035cd8785SDavid Gibson out_8(&udbg_comport->ier, 0x00); 101463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, LCR_DLAB); 102463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dll, dll & 0xff); 103463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dlm, dll >> 8); 104463ce0e1SBenjamin Herrenschmidt /* 8 data, 1 stop, no parity */ 105463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, 0x03); 106463ce0e1SBenjamin Herrenschmidt /* RTS/DTR */ 107463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->mcr, 0x03); 108463ce0e1SBenjamin Herrenschmidt /* Clear & enable FIFOs */ 109463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->fcr ,0x07); 11035cd8785SDavid Gibson udbg_putc = udbg_550_putc; 11135cd8785SDavid Gibson udbg_getc = udbg_550_getc; 11235cd8785SDavid Gibson udbg_getc_poll = udbg_550_getc_poll; 11335cd8785SDavid Gibson } 11435cd8785SDavid Gibson } 11535cd8785SDavid Gibson 116463ce0e1SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) 117463ce0e1SBenjamin Herrenschmidt { 118463ce0e1SBenjamin Herrenschmidt unsigned int dll, dlm, divisor, prescaler, speed; 119463ce0e1SBenjamin Herrenschmidt u8 old_lcr; 120463ce0e1SBenjamin Herrenschmidt volatile struct NS16550 __iomem *port = comport; 121463ce0e1SBenjamin Herrenschmidt 122463ce0e1SBenjamin Herrenschmidt old_lcr = in_8(&port->lcr); 123463ce0e1SBenjamin Herrenschmidt 124463ce0e1SBenjamin Herrenschmidt /* select divisor latch registers. */ 125463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, LCR_DLAB); 126463ce0e1SBenjamin Herrenschmidt 127463ce0e1SBenjamin Herrenschmidt /* now, read the divisor */ 128463ce0e1SBenjamin Herrenschmidt dll = in_8(&port->dll); 129463ce0e1SBenjamin Herrenschmidt dlm = in_8(&port->dlm); 130463ce0e1SBenjamin Herrenschmidt divisor = dlm << 8 | dll; 131463ce0e1SBenjamin Herrenschmidt 132463ce0e1SBenjamin Herrenschmidt /* check prescaling */ 133463ce0e1SBenjamin Herrenschmidt if (in_8(&port->mcr) & 0x80) 134463ce0e1SBenjamin Herrenschmidt prescaler = 4; 135463ce0e1SBenjamin Herrenschmidt else 136463ce0e1SBenjamin Herrenschmidt prescaler = 1; 137463ce0e1SBenjamin Herrenschmidt 138463ce0e1SBenjamin Herrenschmidt /* restore the LCR */ 139463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, old_lcr); 140463ce0e1SBenjamin Herrenschmidt 141463ce0e1SBenjamin Herrenschmidt /* calculate speed */ 142463ce0e1SBenjamin Herrenschmidt speed = (clock / prescaler) / (divisor * 16); 143463ce0e1SBenjamin Herrenschmidt 144463ce0e1SBenjamin Herrenschmidt /* sanity check */ 145d0e132b5SKumar Gala if (speed < 0 || speed > (clock / 16)) 146463ce0e1SBenjamin Herrenschmidt speed = 9600; 147463ce0e1SBenjamin Herrenschmidt 148463ce0e1SBenjamin Herrenschmidt return speed; 149463ce0e1SBenjamin Herrenschmidt } 150463ce0e1SBenjamin Herrenschmidt 15135cd8785SDavid Gibson #ifdef CONFIG_PPC_MAPLE 1524009d980SOlaf Hering void udbg_maple_real_putc(char c) 15335cd8785SDavid Gibson { 15435cd8785SDavid Gibson if (udbg_comport) { 15535cd8785SDavid Gibson while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 15635cd8785SDavid Gibson /* wait for idle */; 15735cd8785SDavid Gibson real_writeb(c, &udbg_comport->thr); eieio(); 15835cd8785SDavid Gibson if (c == '\n') 15935cd8785SDavid Gibson udbg_maple_real_putc('\r'); 16035cd8785SDavid Gibson } 16135cd8785SDavid Gibson } 16235cd8785SDavid Gibson 163296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void) 16435cd8785SDavid Gibson { 16535cd8785SDavid Gibson udbg_comport = (volatile struct NS16550 __iomem *)0xf40003f8; 16635cd8785SDavid Gibson 16735cd8785SDavid Gibson udbg_putc = udbg_maple_real_putc; 16835cd8785SDavid Gibson udbg_getc = NULL; 16935cd8785SDavid Gibson udbg_getc_poll = NULL; 17035cd8785SDavid Gibson } 17135cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */ 17239c870d5SOlof Johansson 17339c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI 17439c870d5SOlof Johansson void udbg_pas_real_putc(char c) 17539c870d5SOlof Johansson { 17639c870d5SOlof Johansson if (udbg_comport) { 17739c870d5SOlof Johansson while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 17839c870d5SOlof Johansson /* wait for idle */; 17939c870d5SOlof Johansson real_205_writeb(c, &udbg_comport->thr); eieio(); 18039c870d5SOlof Johansson if (c == '\n') 18139c870d5SOlof Johansson udbg_pas_real_putc('\r'); 18239c870d5SOlof Johansson } 18339c870d5SOlof Johansson } 18439c870d5SOlof Johansson 18539c870d5SOlof Johansson void udbg_init_pas_realmode(void) 18639c870d5SOlof Johansson { 18739c870d5SOlof Johansson udbg_comport = (volatile struct NS16550 __iomem *)0xfcff03f8; 18839c870d5SOlof Johansson 18939c870d5SOlof Johansson udbg_putc = udbg_pas_real_putc; 19039c870d5SOlof Johansson udbg_getc = NULL; 19139c870d5SOlof Johansson udbg_getc_poll = NULL; 19239c870d5SOlof Johansson } 19339c870d5SOlof Johansson #endif /* CONFIG_PPC_MAPLE */ 194