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 49f276b5baSBenjamin Herrenschmidt static struct NS16550 __iomem *udbg_comport; 5035cd8785SDavid Gibson 51af9c7249SAndrew Klossner static void udbg_550_flush(void) 5235cd8785SDavid Gibson { 5335cd8785SDavid Gibson if (udbg_comport) { 5435cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) 5535cd8785SDavid Gibson /* wait for idle */; 56af9c7249SAndrew Klossner } 57af9c7249SAndrew Klossner } 58af9c7249SAndrew Klossner 59af9c7249SAndrew Klossner static void udbg_550_putc(char c) 60af9c7249SAndrew Klossner { 61af9c7249SAndrew Klossner if (udbg_comport) { 6235cd8785SDavid Gibson if (c == '\n') 6335cd8785SDavid Gibson udbg_550_putc('\r'); 64af9c7249SAndrew Klossner udbg_550_flush(); 65af9c7249SAndrew Klossner out_8(&udbg_comport->thr, c); 6635cd8785SDavid Gibson } 6735cd8785SDavid Gibson } 6835cd8785SDavid Gibson 6935cd8785SDavid Gibson static int udbg_550_getc_poll(void) 7035cd8785SDavid Gibson { 7135cd8785SDavid Gibson if (udbg_comport) { 7235cd8785SDavid Gibson if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0) 7335cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 7435cd8785SDavid Gibson else 7535cd8785SDavid Gibson return -1; 7635cd8785SDavid Gibson } 7735cd8785SDavid Gibson return -1; 7835cd8785SDavid Gibson } 7935cd8785SDavid Gibson 80bb6b9b28SBenjamin Herrenschmidt static int udbg_550_getc(void) 8135cd8785SDavid Gibson { 8235cd8785SDavid Gibson if (udbg_comport) { 8335cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) 8435cd8785SDavid Gibson /* wait for char */; 8535cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 8635cd8785SDavid Gibson } 87bb6b9b28SBenjamin Herrenschmidt return -1; 8835cd8785SDavid Gibson } 8935cd8785SDavid Gibson 90463ce0e1SBenjamin Herrenschmidt void udbg_init_uart(void __iomem *comport, unsigned int speed, 91463ce0e1SBenjamin Herrenschmidt unsigned int clock) 9235cd8785SDavid Gibson { 93171505daSBenjamin Herrenschmidt unsigned int dll, base_bauds; 94463ce0e1SBenjamin Herrenschmidt 95171505daSBenjamin Herrenschmidt if (clock == 0) 96171505daSBenjamin Herrenschmidt clock = 1843200; 97463ce0e1SBenjamin Herrenschmidt if (speed == 0) 98463ce0e1SBenjamin Herrenschmidt speed = 9600; 99171505daSBenjamin Herrenschmidt 100171505daSBenjamin Herrenschmidt base_bauds = clock / 16; 101463ce0e1SBenjamin Herrenschmidt dll = base_bauds / speed; 10235cd8785SDavid Gibson 10335cd8785SDavid Gibson if (comport) { 10435cd8785SDavid Gibson udbg_comport = (struct NS16550 __iomem *)comport; 10535cd8785SDavid Gibson out_8(&udbg_comport->lcr, 0x00); 10635cd8785SDavid Gibson out_8(&udbg_comport->ier, 0xff); 10735cd8785SDavid Gibson out_8(&udbg_comport->ier, 0x00); 108463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, LCR_DLAB); 109463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dll, dll & 0xff); 110463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dlm, dll >> 8); 111463ce0e1SBenjamin Herrenschmidt /* 8 data, 1 stop, no parity */ 112463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, 0x03); 113463ce0e1SBenjamin Herrenschmidt /* RTS/DTR */ 114463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->mcr, 0x03); 115463ce0e1SBenjamin Herrenschmidt /* Clear & enable FIFOs */ 116463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->fcr ,0x07); 11735cd8785SDavid Gibson udbg_putc = udbg_550_putc; 118af9c7249SAndrew Klossner udbg_flush = udbg_550_flush; 11935cd8785SDavid Gibson udbg_getc = udbg_550_getc; 12035cd8785SDavid Gibson udbg_getc_poll = udbg_550_getc_poll; 12135cd8785SDavid Gibson } 12235cd8785SDavid Gibson } 12335cd8785SDavid Gibson 124463ce0e1SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) 125463ce0e1SBenjamin Herrenschmidt { 126463ce0e1SBenjamin Herrenschmidt unsigned int dll, dlm, divisor, prescaler, speed; 127463ce0e1SBenjamin Herrenschmidt u8 old_lcr; 128f276b5baSBenjamin Herrenschmidt struct NS16550 __iomem *port = comport; 129463ce0e1SBenjamin Herrenschmidt 130463ce0e1SBenjamin Herrenschmidt old_lcr = in_8(&port->lcr); 131463ce0e1SBenjamin Herrenschmidt 132463ce0e1SBenjamin Herrenschmidt /* select divisor latch registers. */ 133463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, LCR_DLAB); 134463ce0e1SBenjamin Herrenschmidt 135463ce0e1SBenjamin Herrenschmidt /* now, read the divisor */ 136463ce0e1SBenjamin Herrenschmidt dll = in_8(&port->dll); 137463ce0e1SBenjamin Herrenschmidt dlm = in_8(&port->dlm); 138463ce0e1SBenjamin Herrenschmidt divisor = dlm << 8 | dll; 139463ce0e1SBenjamin Herrenschmidt 140463ce0e1SBenjamin Herrenschmidt /* check prescaling */ 141463ce0e1SBenjamin Herrenschmidt if (in_8(&port->mcr) & 0x80) 142463ce0e1SBenjamin Herrenschmidt prescaler = 4; 143463ce0e1SBenjamin Herrenschmidt else 144463ce0e1SBenjamin Herrenschmidt prescaler = 1; 145463ce0e1SBenjamin Herrenschmidt 146463ce0e1SBenjamin Herrenschmidt /* restore the LCR */ 147463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, old_lcr); 148463ce0e1SBenjamin Herrenschmidt 149463ce0e1SBenjamin Herrenschmidt /* calculate speed */ 150463ce0e1SBenjamin Herrenschmidt speed = (clock / prescaler) / (divisor * 16); 151463ce0e1SBenjamin Herrenschmidt 152463ce0e1SBenjamin Herrenschmidt /* sanity check */ 153bb5e6491Sroel kluin if (speed > (clock / 16)) 154463ce0e1SBenjamin Herrenschmidt speed = 9600; 155463ce0e1SBenjamin Herrenschmidt 156463ce0e1SBenjamin Herrenschmidt return speed; 157463ce0e1SBenjamin Herrenschmidt } 158463ce0e1SBenjamin Herrenschmidt 15935cd8785SDavid Gibson #ifdef CONFIG_PPC_MAPLE 160af9c7249SAndrew Klossner void udbg_maple_real_flush(void) 16135cd8785SDavid Gibson { 16235cd8785SDavid Gibson if (udbg_comport) { 16335cd8785SDavid Gibson while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 16435cd8785SDavid Gibson /* wait for idle */; 165af9c7249SAndrew Klossner } 166af9c7249SAndrew Klossner } 167af9c7249SAndrew Klossner 168af9c7249SAndrew Klossner void udbg_maple_real_putc(char c) 169af9c7249SAndrew Klossner { 170af9c7249SAndrew Klossner if (udbg_comport) { 17135cd8785SDavid Gibson if (c == '\n') 17235cd8785SDavid Gibson udbg_maple_real_putc('\r'); 173af9c7249SAndrew Klossner udbg_maple_real_flush(); 174af9c7249SAndrew Klossner real_writeb(c, &udbg_comport->thr); eieio(); 17535cd8785SDavid Gibson } 17635cd8785SDavid Gibson } 17735cd8785SDavid Gibson 178296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void) 17935cd8785SDavid Gibson { 180f276b5baSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *)0xf40003f8; 18135cd8785SDavid Gibson 18235cd8785SDavid Gibson udbg_putc = udbg_maple_real_putc; 183af9c7249SAndrew Klossner udbg_flush = udbg_maple_real_flush; 18435cd8785SDavid Gibson udbg_getc = NULL; 18535cd8785SDavid Gibson udbg_getc_poll = NULL; 18635cd8785SDavid Gibson } 18735cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */ 18839c870d5SOlof Johansson 18939c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI 190af9c7249SAndrew Klossner void udbg_pas_real_flush(void) 19139c870d5SOlof Johansson { 19239c870d5SOlof Johansson if (udbg_comport) { 19339c870d5SOlof Johansson while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 19439c870d5SOlof Johansson /* wait for idle */; 195af9c7249SAndrew Klossner } 196af9c7249SAndrew Klossner } 197af9c7249SAndrew Klossner 198af9c7249SAndrew Klossner void udbg_pas_real_putc(char c) 199af9c7249SAndrew Klossner { 200af9c7249SAndrew Klossner if (udbg_comport) { 20139c870d5SOlof Johansson if (c == '\n') 20239c870d5SOlof Johansson udbg_pas_real_putc('\r'); 203af9c7249SAndrew Klossner udbg_pas_real_flush(); 204af9c7249SAndrew Klossner real_205_writeb(c, &udbg_comport->thr); eieio(); 20539c870d5SOlof Johansson } 20639c870d5SOlof Johansson } 20739c870d5SOlof Johansson 20839c870d5SOlof Johansson void udbg_init_pas_realmode(void) 20939c870d5SOlof Johansson { 210f276b5baSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL; 21139c870d5SOlof Johansson 21239c870d5SOlof Johansson udbg_putc = udbg_pas_real_putc; 213af9c7249SAndrew Klossner udbg_flush = udbg_pas_real_flush; 21439c870d5SOlof Johansson udbg_getc = NULL; 21539c870d5SOlof Johansson udbg_getc_poll = NULL; 21639c870d5SOlof Johansson } 21739c870d5SOlof Johansson #endif /* CONFIG_PPC_MAPLE */ 218d9b55a03SDavid Gibson 219d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x 220d9b55a03SDavid Gibson #include <platforms/44x/44x.h> 221d9b55a03SDavid Gibson 222f694cda8SBenjamin Herrenschmidt static void udbg_44x_as1_flush(void) 223d9b55a03SDavid Gibson { 224d9b55a03SDavid Gibson if (udbg_comport) { 225d9b55a03SDavid Gibson while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 226d9b55a03SDavid Gibson /* wait for idle */; 227af9c7249SAndrew Klossner } 228af9c7249SAndrew Klossner } 229af9c7249SAndrew Klossner 230af9c7249SAndrew Klossner static void udbg_44x_as1_putc(char c) 231af9c7249SAndrew Klossner { 232af9c7249SAndrew Klossner if (udbg_comport) { 233d9b55a03SDavid Gibson if (c == '\n') 234d9b55a03SDavid Gibson udbg_44x_as1_putc('\r'); 235af9c7249SAndrew Klossner udbg_44x_as1_flush(); 236af9c7249SAndrew Klossner as1_writeb(c, &udbg_comport->thr); eieio(); 237d9b55a03SDavid Gibson } 238d9b55a03SDavid Gibson } 239d9b55a03SDavid Gibson 24070dea47dSHollis Blanchard static int udbg_44x_as1_getc(void) 24170dea47dSHollis Blanchard { 24270dea47dSHollis Blanchard if (udbg_comport) { 24370dea47dSHollis Blanchard while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0) 24470dea47dSHollis Blanchard ; /* wait for char */ 24570dea47dSHollis Blanchard return as1_readb(&udbg_comport->rbr); 24670dea47dSHollis Blanchard } 24770dea47dSHollis Blanchard return -1; 24870dea47dSHollis Blanchard } 24970dea47dSHollis Blanchard 250d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void) 251d9b55a03SDavid Gibson { 252d9b55a03SDavid Gibson udbg_comport = 253f276b5baSBenjamin Herrenschmidt (struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR; 254d9b55a03SDavid Gibson 255d9b55a03SDavid Gibson udbg_putc = udbg_44x_as1_putc; 256af9c7249SAndrew Klossner udbg_flush = udbg_44x_as1_flush; 25770dea47dSHollis Blanchard udbg_getc = udbg_44x_as1_getc; 258d9b55a03SDavid Gibson } 259d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ 2609dae8afdSBenjamin Herrenschmidt 2619dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x 262af9c7249SAndrew Klossner static void udbg_40x_real_flush(void) 2639dae8afdSBenjamin Herrenschmidt { 2649dae8afdSBenjamin Herrenschmidt if (udbg_comport) { 2659dae8afdSBenjamin Herrenschmidt while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 2669dae8afdSBenjamin Herrenschmidt /* wait for idle */; 267af9c7249SAndrew Klossner } 268af9c7249SAndrew Klossner } 269af9c7249SAndrew Klossner 270af9c7249SAndrew Klossner static void udbg_40x_real_putc(char c) 271af9c7249SAndrew Klossner { 272af9c7249SAndrew Klossner if (udbg_comport) { 2739dae8afdSBenjamin Herrenschmidt if (c == '\n') 2749dae8afdSBenjamin Herrenschmidt udbg_40x_real_putc('\r'); 275af9c7249SAndrew Klossner udbg_40x_real_flush(); 276af9c7249SAndrew Klossner real_writeb(c, &udbg_comport->thr); eieio(); 2779dae8afdSBenjamin Herrenschmidt } 2789dae8afdSBenjamin Herrenschmidt } 2799dae8afdSBenjamin Herrenschmidt 2809dae8afdSBenjamin Herrenschmidt static int udbg_40x_real_getc(void) 2819dae8afdSBenjamin Herrenschmidt { 2829dae8afdSBenjamin Herrenschmidt if (udbg_comport) { 2839dae8afdSBenjamin Herrenschmidt while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0) 2849dae8afdSBenjamin Herrenschmidt ; /* wait for char */ 2859dae8afdSBenjamin Herrenschmidt return real_readb(&udbg_comport->rbr); 2869dae8afdSBenjamin Herrenschmidt } 2879dae8afdSBenjamin Herrenschmidt return -1; 2889dae8afdSBenjamin Herrenschmidt } 2899dae8afdSBenjamin Herrenschmidt 2909dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void) 2919dae8afdSBenjamin Herrenschmidt { 2929dae8afdSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *) 2939dae8afdSBenjamin Herrenschmidt CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR; 2949dae8afdSBenjamin Herrenschmidt 2959dae8afdSBenjamin Herrenschmidt udbg_putc = udbg_40x_real_putc; 296af9c7249SAndrew Klossner udbg_flush = udbg_40x_real_flush; 2979dae8afdSBenjamin Herrenschmidt udbg_getc = udbg_40x_real_getc; 2989dae8afdSBenjamin Herrenschmidt udbg_getc_poll = NULL; 2999dae8afdSBenjamin Herrenschmidt } 3009dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */ 301