135cd8785SDavid Gibson /* 225985edcSLucas De Marchi * udbg for NS16550 compatible 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> 14a0496d45SJack Miller #include <asm/reg_a2.h> 1535cd8785SDavid Gibson 1635cd8785SDavid Gibson extern u8 real_readb(volatile u8 __iomem *addr); 1735cd8785SDavid Gibson extern void real_writeb(u8 data, volatile u8 __iomem *addr); 1839c870d5SOlof Johansson extern u8 real_205_readb(volatile u8 __iomem *addr); 1939c870d5SOlof Johansson extern void real_205_writeb(u8 data, volatile u8 __iomem *addr); 2035cd8785SDavid Gibson 2135cd8785SDavid Gibson struct NS16550 { 2235cd8785SDavid Gibson /* this struct must be packed */ 2335cd8785SDavid Gibson unsigned char rbr; /* 0 */ 2435cd8785SDavid Gibson unsigned char ier; /* 1 */ 2535cd8785SDavid Gibson unsigned char fcr; /* 2 */ 2635cd8785SDavid Gibson unsigned char lcr; /* 3 */ 2735cd8785SDavid Gibson unsigned char mcr; /* 4 */ 2835cd8785SDavid Gibson unsigned char lsr; /* 5 */ 2935cd8785SDavid Gibson unsigned char msr; /* 6 */ 3035cd8785SDavid Gibson unsigned char scr; /* 7 */ 3135cd8785SDavid Gibson }; 3235cd8785SDavid Gibson 3335cd8785SDavid Gibson #define thr rbr 3435cd8785SDavid Gibson #define iir fcr 3535cd8785SDavid Gibson #define dll rbr 3635cd8785SDavid Gibson #define dlm ier 3735cd8785SDavid Gibson #define dlab lcr 3835cd8785SDavid Gibson 3935cd8785SDavid Gibson #define LSR_DR 0x01 /* Data ready */ 4035cd8785SDavid Gibson #define LSR_OE 0x02 /* Overrun */ 4135cd8785SDavid Gibson #define LSR_PE 0x04 /* Parity error */ 4235cd8785SDavid Gibson #define LSR_FE 0x08 /* Framing error */ 4335cd8785SDavid Gibson #define LSR_BI 0x10 /* Break */ 4435cd8785SDavid Gibson #define LSR_THRE 0x20 /* Xmit holding register empty */ 4535cd8785SDavid Gibson #define LSR_TEMT 0x40 /* Xmitter empty */ 4635cd8785SDavid Gibson #define LSR_ERR 0x80 /* Error */ 4735cd8785SDavid Gibson 48463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80 49463ce0e1SBenjamin Herrenschmidt 50f276b5baSBenjamin Herrenschmidt static struct NS16550 __iomem *udbg_comport; 5135cd8785SDavid Gibson 52af9c7249SAndrew Klossner static void udbg_550_flush(void) 5335cd8785SDavid Gibson { 5435cd8785SDavid Gibson if (udbg_comport) { 5535cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) 5635cd8785SDavid Gibson /* wait for idle */; 57af9c7249SAndrew Klossner } 58af9c7249SAndrew Klossner } 59af9c7249SAndrew Klossner 60af9c7249SAndrew Klossner static void udbg_550_putc(char c) 61af9c7249SAndrew Klossner { 62af9c7249SAndrew Klossner if (udbg_comport) { 6335cd8785SDavid Gibson if (c == '\n') 6435cd8785SDavid Gibson udbg_550_putc('\r'); 65af9c7249SAndrew Klossner udbg_550_flush(); 66af9c7249SAndrew Klossner out_8(&udbg_comport->thr, c); 6735cd8785SDavid Gibson } 6835cd8785SDavid Gibson } 6935cd8785SDavid Gibson 7035cd8785SDavid Gibson static int udbg_550_getc_poll(void) 7135cd8785SDavid Gibson { 7235cd8785SDavid Gibson if (udbg_comport) { 7335cd8785SDavid Gibson if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0) 7435cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 7535cd8785SDavid Gibson else 7635cd8785SDavid Gibson return -1; 7735cd8785SDavid Gibson } 7835cd8785SDavid Gibson return -1; 7935cd8785SDavid Gibson } 8035cd8785SDavid Gibson 81bb6b9b28SBenjamin Herrenschmidt static int udbg_550_getc(void) 8235cd8785SDavid Gibson { 8335cd8785SDavid Gibson if (udbg_comport) { 8435cd8785SDavid Gibson while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) 8535cd8785SDavid Gibson /* wait for char */; 8635cd8785SDavid Gibson return in_8(&udbg_comport->rbr); 8735cd8785SDavid Gibson } 88bb6b9b28SBenjamin Herrenschmidt return -1; 8935cd8785SDavid Gibson } 9035cd8785SDavid Gibson 91463ce0e1SBenjamin Herrenschmidt void udbg_init_uart(void __iomem *comport, unsigned int speed, 92463ce0e1SBenjamin Herrenschmidt unsigned int clock) 9335cd8785SDavid Gibson { 94171505daSBenjamin Herrenschmidt unsigned int dll, base_bauds; 95463ce0e1SBenjamin Herrenschmidt 96171505daSBenjamin Herrenschmidt if (clock == 0) 97171505daSBenjamin Herrenschmidt clock = 1843200; 98463ce0e1SBenjamin Herrenschmidt if (speed == 0) 99463ce0e1SBenjamin Herrenschmidt speed = 9600; 100171505daSBenjamin Herrenschmidt 101171505daSBenjamin Herrenschmidt base_bauds = clock / 16; 102463ce0e1SBenjamin Herrenschmidt dll = base_bauds / speed; 10335cd8785SDavid Gibson 10435cd8785SDavid Gibson if (comport) { 10535cd8785SDavid Gibson udbg_comport = (struct NS16550 __iomem *)comport; 10635cd8785SDavid Gibson out_8(&udbg_comport->lcr, 0x00); 10735cd8785SDavid Gibson out_8(&udbg_comport->ier, 0xff); 10835cd8785SDavid Gibson out_8(&udbg_comport->ier, 0x00); 109463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, LCR_DLAB); 110463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dll, dll & 0xff); 111463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->dlm, dll >> 8); 112463ce0e1SBenjamin Herrenschmidt /* 8 data, 1 stop, no parity */ 113463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->lcr, 0x03); 114463ce0e1SBenjamin Herrenschmidt /* RTS/DTR */ 115463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->mcr, 0x03); 116463ce0e1SBenjamin Herrenschmidt /* Clear & enable FIFOs */ 117463ce0e1SBenjamin Herrenschmidt out_8(&udbg_comport->fcr ,0x07); 11835cd8785SDavid Gibson udbg_putc = udbg_550_putc; 119af9c7249SAndrew Klossner udbg_flush = udbg_550_flush; 12035cd8785SDavid Gibson udbg_getc = udbg_550_getc; 12135cd8785SDavid Gibson udbg_getc_poll = udbg_550_getc_poll; 12235cd8785SDavid Gibson } 12335cd8785SDavid Gibson } 12435cd8785SDavid Gibson 125463ce0e1SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) 126463ce0e1SBenjamin Herrenschmidt { 127463ce0e1SBenjamin Herrenschmidt unsigned int dll, dlm, divisor, prescaler, speed; 128463ce0e1SBenjamin Herrenschmidt u8 old_lcr; 129f276b5baSBenjamin Herrenschmidt struct NS16550 __iomem *port = comport; 130463ce0e1SBenjamin Herrenschmidt 131463ce0e1SBenjamin Herrenschmidt old_lcr = in_8(&port->lcr); 132463ce0e1SBenjamin Herrenschmidt 133463ce0e1SBenjamin Herrenschmidt /* select divisor latch registers. */ 134463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, LCR_DLAB); 135463ce0e1SBenjamin Herrenschmidt 136463ce0e1SBenjamin Herrenschmidt /* now, read the divisor */ 137463ce0e1SBenjamin Herrenschmidt dll = in_8(&port->dll); 138463ce0e1SBenjamin Herrenschmidt dlm = in_8(&port->dlm); 139463ce0e1SBenjamin Herrenschmidt divisor = dlm << 8 | dll; 140463ce0e1SBenjamin Herrenschmidt 141463ce0e1SBenjamin Herrenschmidt /* check prescaling */ 142463ce0e1SBenjamin Herrenschmidt if (in_8(&port->mcr) & 0x80) 143463ce0e1SBenjamin Herrenschmidt prescaler = 4; 144463ce0e1SBenjamin Herrenschmidt else 145463ce0e1SBenjamin Herrenschmidt prescaler = 1; 146463ce0e1SBenjamin Herrenschmidt 147463ce0e1SBenjamin Herrenschmidt /* restore the LCR */ 148463ce0e1SBenjamin Herrenschmidt out_8(&port->lcr, old_lcr); 149463ce0e1SBenjamin Herrenschmidt 150463ce0e1SBenjamin Herrenschmidt /* calculate speed */ 151463ce0e1SBenjamin Herrenschmidt speed = (clock / prescaler) / (divisor * 16); 152463ce0e1SBenjamin Herrenschmidt 153463ce0e1SBenjamin Herrenschmidt /* sanity check */ 154bb5e6491Sroel kluin if (speed > (clock / 16)) 155463ce0e1SBenjamin Herrenschmidt speed = 9600; 156463ce0e1SBenjamin Herrenschmidt 157463ce0e1SBenjamin Herrenschmidt return speed; 158463ce0e1SBenjamin Herrenschmidt } 159463ce0e1SBenjamin Herrenschmidt 16035cd8785SDavid Gibson #ifdef CONFIG_PPC_MAPLE 161af9c7249SAndrew Klossner void udbg_maple_real_flush(void) 16235cd8785SDavid Gibson { 16335cd8785SDavid Gibson if (udbg_comport) { 16435cd8785SDavid Gibson while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 16535cd8785SDavid Gibson /* wait for idle */; 166af9c7249SAndrew Klossner } 167af9c7249SAndrew Klossner } 168af9c7249SAndrew Klossner 169af9c7249SAndrew Klossner void udbg_maple_real_putc(char c) 170af9c7249SAndrew Klossner { 171af9c7249SAndrew Klossner if (udbg_comport) { 17235cd8785SDavid Gibson if (c == '\n') 17335cd8785SDavid Gibson udbg_maple_real_putc('\r'); 174af9c7249SAndrew Klossner udbg_maple_real_flush(); 175af9c7249SAndrew Klossner real_writeb(c, &udbg_comport->thr); eieio(); 17635cd8785SDavid Gibson } 17735cd8785SDavid Gibson } 17835cd8785SDavid Gibson 179296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void) 18035cd8785SDavid Gibson { 181f276b5baSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *)0xf40003f8; 18235cd8785SDavid Gibson 18335cd8785SDavid Gibson udbg_putc = udbg_maple_real_putc; 184af9c7249SAndrew Klossner udbg_flush = udbg_maple_real_flush; 18535cd8785SDavid Gibson udbg_getc = NULL; 18635cd8785SDavid Gibson udbg_getc_poll = NULL; 18735cd8785SDavid Gibson } 18835cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */ 18939c870d5SOlof Johansson 19039c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI 191af9c7249SAndrew Klossner void udbg_pas_real_flush(void) 19239c870d5SOlof Johansson { 19339c870d5SOlof Johansson if (udbg_comport) { 19439c870d5SOlof Johansson while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 19539c870d5SOlof Johansson /* wait for idle */; 196af9c7249SAndrew Klossner } 197af9c7249SAndrew Klossner } 198af9c7249SAndrew Klossner 199af9c7249SAndrew Klossner void udbg_pas_real_putc(char c) 200af9c7249SAndrew Klossner { 201af9c7249SAndrew Klossner if (udbg_comport) { 20239c870d5SOlof Johansson if (c == '\n') 20339c870d5SOlof Johansson udbg_pas_real_putc('\r'); 204af9c7249SAndrew Klossner udbg_pas_real_flush(); 205af9c7249SAndrew Klossner real_205_writeb(c, &udbg_comport->thr); eieio(); 20639c870d5SOlof Johansson } 20739c870d5SOlof Johansson } 20839c870d5SOlof Johansson 20939c870d5SOlof Johansson void udbg_init_pas_realmode(void) 21039c870d5SOlof Johansson { 211f276b5baSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL; 21239c870d5SOlof Johansson 21339c870d5SOlof Johansson udbg_putc = udbg_pas_real_putc; 214af9c7249SAndrew Klossner udbg_flush = udbg_pas_real_flush; 21539c870d5SOlof Johansson udbg_getc = NULL; 21639c870d5SOlof Johansson udbg_getc_poll = NULL; 21739c870d5SOlof Johansson } 21839c870d5SOlof Johansson #endif /* CONFIG_PPC_MAPLE */ 219d9b55a03SDavid Gibson 220d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x 221d9b55a03SDavid Gibson #include <platforms/44x/44x.h> 222d9b55a03SDavid Gibson 223f694cda8SBenjamin Herrenschmidt static void udbg_44x_as1_flush(void) 224d9b55a03SDavid Gibson { 225d9b55a03SDavid Gibson if (udbg_comport) { 226d9b55a03SDavid Gibson while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 227d9b55a03SDavid Gibson /* wait for idle */; 228af9c7249SAndrew Klossner } 229af9c7249SAndrew Klossner } 230af9c7249SAndrew Klossner 231af9c7249SAndrew Klossner static void udbg_44x_as1_putc(char c) 232af9c7249SAndrew Klossner { 233af9c7249SAndrew Klossner if (udbg_comport) { 234d9b55a03SDavid Gibson if (c == '\n') 235d9b55a03SDavid Gibson udbg_44x_as1_putc('\r'); 236af9c7249SAndrew Klossner udbg_44x_as1_flush(); 237af9c7249SAndrew Klossner as1_writeb(c, &udbg_comport->thr); eieio(); 238d9b55a03SDavid Gibson } 239d9b55a03SDavid Gibson } 240d9b55a03SDavid Gibson 24170dea47dSHollis Blanchard static int udbg_44x_as1_getc(void) 24270dea47dSHollis Blanchard { 24370dea47dSHollis Blanchard if (udbg_comport) { 24470dea47dSHollis Blanchard while ((as1_readb(&udbg_comport->lsr) & LSR_DR) == 0) 24570dea47dSHollis Blanchard ; /* wait for char */ 24670dea47dSHollis Blanchard return as1_readb(&udbg_comport->rbr); 24770dea47dSHollis Blanchard } 24870dea47dSHollis Blanchard return -1; 24970dea47dSHollis Blanchard } 25070dea47dSHollis Blanchard 251d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void) 252d9b55a03SDavid Gibson { 253d9b55a03SDavid Gibson udbg_comport = 254f276b5baSBenjamin Herrenschmidt (struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR; 255d9b55a03SDavid Gibson 256d9b55a03SDavid Gibson udbg_putc = udbg_44x_as1_putc; 257af9c7249SAndrew Klossner udbg_flush = udbg_44x_as1_flush; 25870dea47dSHollis Blanchard udbg_getc = udbg_44x_as1_getc; 259d9b55a03SDavid Gibson } 260d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ 2619dae8afdSBenjamin Herrenschmidt 2629dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x 263af9c7249SAndrew Klossner static void udbg_40x_real_flush(void) 2649dae8afdSBenjamin Herrenschmidt { 2659dae8afdSBenjamin Herrenschmidt if (udbg_comport) { 2669dae8afdSBenjamin Herrenschmidt while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0) 2679dae8afdSBenjamin Herrenschmidt /* wait for idle */; 268af9c7249SAndrew Klossner } 269af9c7249SAndrew Klossner } 270af9c7249SAndrew Klossner 271af9c7249SAndrew Klossner static void udbg_40x_real_putc(char c) 272af9c7249SAndrew Klossner { 273af9c7249SAndrew Klossner if (udbg_comport) { 2749dae8afdSBenjamin Herrenschmidt if (c == '\n') 2759dae8afdSBenjamin Herrenschmidt udbg_40x_real_putc('\r'); 276af9c7249SAndrew Klossner udbg_40x_real_flush(); 277af9c7249SAndrew Klossner real_writeb(c, &udbg_comport->thr); eieio(); 2789dae8afdSBenjamin Herrenschmidt } 2799dae8afdSBenjamin Herrenschmidt } 2809dae8afdSBenjamin Herrenschmidt 2819dae8afdSBenjamin Herrenschmidt static int udbg_40x_real_getc(void) 2829dae8afdSBenjamin Herrenschmidt { 2839dae8afdSBenjamin Herrenschmidt if (udbg_comport) { 2849dae8afdSBenjamin Herrenschmidt while ((real_readb(&udbg_comport->lsr) & LSR_DR) == 0) 2859dae8afdSBenjamin Herrenschmidt ; /* wait for char */ 2869dae8afdSBenjamin Herrenschmidt return real_readb(&udbg_comport->rbr); 2879dae8afdSBenjamin Herrenschmidt } 2889dae8afdSBenjamin Herrenschmidt return -1; 2899dae8afdSBenjamin Herrenschmidt } 2909dae8afdSBenjamin Herrenschmidt 2919dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void) 2929dae8afdSBenjamin Herrenschmidt { 2939dae8afdSBenjamin Herrenschmidt udbg_comport = (struct NS16550 __iomem *) 2949dae8afdSBenjamin Herrenschmidt CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR; 2959dae8afdSBenjamin Herrenschmidt 2969dae8afdSBenjamin Herrenschmidt udbg_putc = udbg_40x_real_putc; 297af9c7249SAndrew Klossner udbg_flush = udbg_40x_real_flush; 2989dae8afdSBenjamin Herrenschmidt udbg_getc = udbg_40x_real_getc; 2999dae8afdSBenjamin Herrenschmidt udbg_getc_poll = NULL; 3009dae8afdSBenjamin Herrenschmidt } 3019dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */ 302a0496d45SJack Miller 303a0496d45SJack Miller #ifdef CONFIG_PPC_EARLY_DEBUG_WSP 304a0496d45SJack Miller static void udbg_wsp_flush(void) 305a0496d45SJack Miller { 306a0496d45SJack Miller if (udbg_comport) { 307a0496d45SJack Miller while ((readb(&udbg_comport->lsr) & LSR_THRE) == 0) 308a0496d45SJack Miller /* wait for idle */; 309a0496d45SJack Miller } 310a0496d45SJack Miller } 311a0496d45SJack Miller 312a0496d45SJack Miller static void udbg_wsp_putc(char c) 313a0496d45SJack Miller { 314a0496d45SJack Miller if (udbg_comport) { 315a0496d45SJack Miller if (c == '\n') 316a0496d45SJack Miller udbg_wsp_putc('\r'); 317a0496d45SJack Miller udbg_wsp_flush(); 318a0496d45SJack Miller writeb(c, &udbg_comport->thr); eieio(); 319a0496d45SJack Miller } 320a0496d45SJack Miller } 321a0496d45SJack Miller 322a0496d45SJack Miller static int udbg_wsp_getc(void) 323a0496d45SJack Miller { 324a0496d45SJack Miller if (udbg_comport) { 325a0496d45SJack Miller while ((readb(&udbg_comport->lsr) & LSR_DR) == 0) 326a0496d45SJack Miller ; /* wait for char */ 327a0496d45SJack Miller return readb(&udbg_comport->rbr); 328a0496d45SJack Miller } 329a0496d45SJack Miller return -1; 330a0496d45SJack Miller } 331a0496d45SJack Miller 332a0496d45SJack Miller static int udbg_wsp_getc_poll(void) 333a0496d45SJack Miller { 334a0496d45SJack Miller if (udbg_comport) 335a0496d45SJack Miller if (readb(&udbg_comport->lsr) & LSR_DR) 336a0496d45SJack Miller return readb(&udbg_comport->rbr); 337a0496d45SJack Miller return -1; 338a0496d45SJack Miller } 339a0496d45SJack Miller 340a0496d45SJack Miller void __init udbg_init_wsp(void) 341a0496d45SJack Miller { 342a0496d45SJack Miller udbg_comport = (struct NS16550 __iomem *)WSP_UART_VIRT; 343a0496d45SJack Miller 344a0496d45SJack Miller udbg_init_uart(udbg_comport, 57600, 50000000); 345a0496d45SJack Miller 346a0496d45SJack Miller udbg_putc = udbg_wsp_putc; 347a0496d45SJack Miller udbg_flush = udbg_wsp_flush; 348a0496d45SJack Miller udbg_getc = udbg_wsp_getc; 349a0496d45SJack Miller udbg_getc_poll = udbg_wsp_getc_poll; 350a0496d45SJack Miller } 351a0496d45SJack Miller #endif /* CONFIG_PPC_EARLY_DEBUG_WSP */ 352