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 2130925748SBenjamin Herrenschmidt #define UART_RBR 0 2230925748SBenjamin Herrenschmidt #define UART_IER 1 2330925748SBenjamin Herrenschmidt #define UART_FCR 2 2430925748SBenjamin Herrenschmidt #define UART_LCR 3 2530925748SBenjamin Herrenschmidt #define UART_MCR 4 2630925748SBenjamin Herrenschmidt #define UART_LSR 5 2730925748SBenjamin Herrenschmidt #define UART_MSR 6 2830925748SBenjamin Herrenschmidt #define UART_SCR 7 2930925748SBenjamin Herrenschmidt #define UART_THR UART_RBR 3030925748SBenjamin Herrenschmidt #define UART_IIR UART_FCR 3130925748SBenjamin Herrenschmidt #define UART_DLL UART_RBR 3230925748SBenjamin Herrenschmidt #define UART_DLM UART_IER 3330925748SBenjamin Herrenschmidt #define UART_DLAB UART_LCR 3435cd8785SDavid Gibson 3535cd8785SDavid Gibson #define LSR_DR 0x01 /* Data ready */ 3635cd8785SDavid Gibson #define LSR_OE 0x02 /* Overrun */ 3735cd8785SDavid Gibson #define LSR_PE 0x04 /* Parity error */ 3835cd8785SDavid Gibson #define LSR_FE 0x08 /* Framing error */ 3935cd8785SDavid Gibson #define LSR_BI 0x10 /* Break */ 4035cd8785SDavid Gibson #define LSR_THRE 0x20 /* Xmit holding register empty */ 4135cd8785SDavid Gibson #define LSR_TEMT 0x40 /* Xmitter empty */ 4235cd8785SDavid Gibson #define LSR_ERR 0x80 /* Error */ 4335cd8785SDavid Gibson 44463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80 45463ce0e1SBenjamin Herrenschmidt 4630925748SBenjamin Herrenschmidt static u8 (*udbg_uart_in)(unsigned int reg); 4730925748SBenjamin Herrenschmidt static void (*udbg_uart_out)(unsigned int reg, u8 data); 4835cd8785SDavid Gibson 4930925748SBenjamin Herrenschmidt static void udbg_uart_flush(void) 5035cd8785SDavid Gibson { 5130925748SBenjamin Herrenschmidt if (!udbg_uart_in) 5230925748SBenjamin Herrenschmidt return; 5330925748SBenjamin Herrenschmidt 5430925748SBenjamin Herrenschmidt /* wait for idle */ 5530925748SBenjamin Herrenschmidt while ((udbg_uart_in(UART_LSR) & LSR_THRE) == 0) 5630925748SBenjamin Herrenschmidt cpu_relax(); 57af9c7249SAndrew Klossner } 58af9c7249SAndrew Klossner 5930925748SBenjamin Herrenschmidt static void udbg_uart_putc(char c) 60af9c7249SAndrew Klossner { 6130925748SBenjamin Herrenschmidt if (!udbg_uart_out) 6230925748SBenjamin Herrenschmidt return; 6330925748SBenjamin Herrenschmidt 6435cd8785SDavid Gibson if (c == '\n') 6530925748SBenjamin Herrenschmidt udbg_uart_putc('\r'); 6630925748SBenjamin Herrenschmidt udbg_uart_flush(); 6730925748SBenjamin Herrenschmidt udbg_uart_out(UART_THR, c); 6835cd8785SDavid Gibson } 6935cd8785SDavid Gibson 7030925748SBenjamin Herrenschmidt static int udbg_uart_getc_poll(void) 7135cd8785SDavid Gibson { 7230925748SBenjamin Herrenschmidt if (!udbg_uart_in || !(udbg_uart_in(UART_LSR) & LSR_DR)) 7330925748SBenjamin Herrenschmidt return udbg_uart_in(UART_RBR); 7435cd8785SDavid Gibson return -1; 7535cd8785SDavid Gibson } 7635cd8785SDavid Gibson 7730925748SBenjamin Herrenschmidt static int udbg_uart_getc(void) 7835cd8785SDavid Gibson { 7930925748SBenjamin Herrenschmidt if (!udbg_uart_in) 80bb6b9b28SBenjamin Herrenschmidt return -1; 8130925748SBenjamin Herrenschmidt /* wait for char */ 8230925748SBenjamin Herrenschmidt while (!(udbg_uart_in(UART_LSR) & LSR_DR)) 8330925748SBenjamin Herrenschmidt cpu_relax(); 8430925748SBenjamin Herrenschmidt return udbg_uart_in(UART_RBR); 8535cd8785SDavid Gibson } 8635cd8785SDavid Gibson 8730925748SBenjamin Herrenschmidt static void udbg_use_uart(void) 8830925748SBenjamin Herrenschmidt { 8930925748SBenjamin Herrenschmidt udbg_putc = udbg_uart_putc; 9030925748SBenjamin Herrenschmidt udbg_flush = udbg_uart_flush; 9130925748SBenjamin Herrenschmidt udbg_getc = udbg_uart_getc; 9230925748SBenjamin Herrenschmidt udbg_getc_poll = udbg_uart_getc_poll; 9330925748SBenjamin Herrenschmidt } 9430925748SBenjamin Herrenschmidt 9530925748SBenjamin Herrenschmidt void udbg_uart_setup(unsigned int speed, unsigned int clock) 9635cd8785SDavid Gibson { 97171505daSBenjamin Herrenschmidt unsigned int dll, base_bauds; 98463ce0e1SBenjamin Herrenschmidt 9930925748SBenjamin Herrenschmidt if (!udbg_uart_out) 10030925748SBenjamin Herrenschmidt return; 10130925748SBenjamin Herrenschmidt 102171505daSBenjamin Herrenschmidt if (clock == 0) 103171505daSBenjamin Herrenschmidt clock = 1843200; 104463ce0e1SBenjamin Herrenschmidt if (speed == 0) 105463ce0e1SBenjamin Herrenschmidt speed = 9600; 106171505daSBenjamin Herrenschmidt 107171505daSBenjamin Herrenschmidt base_bauds = clock / 16; 108463ce0e1SBenjamin Herrenschmidt dll = base_bauds / speed; 10935cd8785SDavid Gibson 11030925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, 0x00); 11130925748SBenjamin Herrenschmidt udbg_uart_out(UART_IER, 0xff); 11230925748SBenjamin Herrenschmidt udbg_uart_out(UART_IER, 0x00); 11330925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, LCR_DLAB); 11430925748SBenjamin Herrenschmidt udbg_uart_out(UART_DLL, dll & 0xff); 11530925748SBenjamin Herrenschmidt udbg_uart_out(UART_DLM, dll >> 8); 116463ce0e1SBenjamin Herrenschmidt /* 8 data, 1 stop, no parity */ 11730925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, 0x3); 118463ce0e1SBenjamin Herrenschmidt /* RTS/DTR */ 11930925748SBenjamin Herrenschmidt udbg_uart_out(UART_MCR, 0x3); 120463ce0e1SBenjamin Herrenschmidt /* Clear & enable FIFOs */ 12130925748SBenjamin Herrenschmidt udbg_uart_out(UART_FCR, 0x7); 12235cd8785SDavid Gibson } 12335cd8785SDavid Gibson 12430925748SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(unsigned int clock) 125463ce0e1SBenjamin Herrenschmidt { 126463ce0e1SBenjamin Herrenschmidt unsigned int dll, dlm, divisor, prescaler, speed; 127463ce0e1SBenjamin Herrenschmidt u8 old_lcr; 128463ce0e1SBenjamin Herrenschmidt 12930925748SBenjamin Herrenschmidt old_lcr = udbg_uart_in(UART_LCR); 130463ce0e1SBenjamin Herrenschmidt 131463ce0e1SBenjamin Herrenschmidt /* select divisor latch registers. */ 13230925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, old_lcr | LCR_DLAB); 133463ce0e1SBenjamin Herrenschmidt 134463ce0e1SBenjamin Herrenschmidt /* now, read the divisor */ 13530925748SBenjamin Herrenschmidt dll = udbg_uart_in(UART_DLL); 13630925748SBenjamin Herrenschmidt dlm = udbg_uart_in(UART_DLM); 137463ce0e1SBenjamin Herrenschmidt divisor = dlm << 8 | dll; 138463ce0e1SBenjamin Herrenschmidt 139463ce0e1SBenjamin Herrenschmidt /* check prescaling */ 14030925748SBenjamin Herrenschmidt if (udbg_uart_in(UART_MCR) & 0x80) 141463ce0e1SBenjamin Herrenschmidt prescaler = 4; 142463ce0e1SBenjamin Herrenschmidt else 143463ce0e1SBenjamin Herrenschmidt prescaler = 1; 144463ce0e1SBenjamin Herrenschmidt 145463ce0e1SBenjamin Herrenschmidt /* restore the LCR */ 14630925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, old_lcr); 147463ce0e1SBenjamin Herrenschmidt 148463ce0e1SBenjamin Herrenschmidt /* calculate speed */ 149463ce0e1SBenjamin Herrenschmidt speed = (clock / prescaler) / (divisor * 16); 150463ce0e1SBenjamin Herrenschmidt 151463ce0e1SBenjamin Herrenschmidt /* sanity check */ 152bb5e6491Sroel kluin if (speed > (clock / 16)) 153463ce0e1SBenjamin Herrenschmidt speed = 9600; 154463ce0e1SBenjamin Herrenschmidt 155463ce0e1SBenjamin Herrenschmidt return speed; 156463ce0e1SBenjamin Herrenschmidt } 157463ce0e1SBenjamin Herrenschmidt 15830925748SBenjamin Herrenschmidt static union { 15930925748SBenjamin Herrenschmidt unsigned char __iomem *mmio_base; 16030925748SBenjamin Herrenschmidt unsigned long pio_base; 16130925748SBenjamin Herrenschmidt } udbg_uart; 16230925748SBenjamin Herrenschmidt 16330925748SBenjamin Herrenschmidt static unsigned int udbg_uart_stride = 1; 16430925748SBenjamin Herrenschmidt 16530925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pio(unsigned int reg) 16635cd8785SDavid Gibson { 16730925748SBenjamin Herrenschmidt return inb(udbg_uart.pio_base + (reg * udbg_uart_stride)); 168af9c7249SAndrew Klossner } 169af9c7249SAndrew Klossner 17030925748SBenjamin Herrenschmidt static void udbg_uart_out_pio(unsigned int reg, u8 data) 171af9c7249SAndrew Klossner { 17230925748SBenjamin Herrenschmidt outb(data, udbg_uart.pio_base + (reg * udbg_uart_stride)); 17335cd8785SDavid Gibson } 17430925748SBenjamin Herrenschmidt 17530925748SBenjamin Herrenschmidt void udbg_uart_init_pio(unsigned long port, unsigned int stride) 17630925748SBenjamin Herrenschmidt { 17730925748SBenjamin Herrenschmidt if (!port) 17830925748SBenjamin Herrenschmidt return; 17930925748SBenjamin Herrenschmidt udbg_uart.pio_base = port; 18030925748SBenjamin Herrenschmidt udbg_uart_stride = stride; 18130925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_pio; 18230925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_pio; 18330925748SBenjamin Herrenschmidt udbg_use_uart(); 18430925748SBenjamin Herrenschmidt } 18530925748SBenjamin Herrenschmidt 18630925748SBenjamin Herrenschmidt static u8 udbg_uart_in_mmio(unsigned int reg) 18730925748SBenjamin Herrenschmidt { 18830925748SBenjamin Herrenschmidt return in_8(udbg_uart.mmio_base + (reg * udbg_uart_stride)); 18930925748SBenjamin Herrenschmidt } 19030925748SBenjamin Herrenschmidt 19130925748SBenjamin Herrenschmidt static void udbg_uart_out_mmio(unsigned int reg, u8 data) 19230925748SBenjamin Herrenschmidt { 19330925748SBenjamin Herrenschmidt out_8(udbg_uart.mmio_base + (reg * udbg_uart_stride), data); 19430925748SBenjamin Herrenschmidt } 19530925748SBenjamin Herrenschmidt 19630925748SBenjamin Herrenschmidt 19730925748SBenjamin Herrenschmidt void udbg_uart_init_mmio(void __iomem *addr, unsigned int stride) 19830925748SBenjamin Herrenschmidt { 19930925748SBenjamin Herrenschmidt if (!addr) 20030925748SBenjamin Herrenschmidt return; 20130925748SBenjamin Herrenschmidt udbg_uart.mmio_base = addr; 20230925748SBenjamin Herrenschmidt udbg_uart_stride = stride; 20330925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_mmio; 20430925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_mmio; 20530925748SBenjamin Herrenschmidt udbg_use_uart(); 20630925748SBenjamin Herrenschmidt } 20730925748SBenjamin Herrenschmidt 20830925748SBenjamin Herrenschmidt #ifdef CONFIG_PPC_MAPLE 20930925748SBenjamin Herrenschmidt 21030925748SBenjamin Herrenschmidt #define UDBG_UART_MAPLE_ADDR ((void __iomem *)0xf40003f8) 21130925748SBenjamin Herrenschmidt 21230925748SBenjamin Herrenschmidt static u8 udbg_uart_in_maple(unsigned int reg) 21330925748SBenjamin Herrenschmidt { 21430925748SBenjamin Herrenschmidt return real_readb(UDBG_UART_MAPLE_ADDR + reg); 21530925748SBenjamin Herrenschmidt } 21630925748SBenjamin Herrenschmidt 21730925748SBenjamin Herrenschmidt static void udbg_uart_out_maple(unsigned int reg, u8 val) 21830925748SBenjamin Herrenschmidt { 21930925748SBenjamin Herrenschmidt real_writeb(val, UDBG_UART_MAPLE_ADDR + reg); 22035cd8785SDavid Gibson } 22135cd8785SDavid Gibson 222296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void) 22335cd8785SDavid Gibson { 22430925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_maple; 22530925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_maple; 22630925748SBenjamin Herrenschmidt udbg_use_uart(); 22735cd8785SDavid Gibson } 22830925748SBenjamin Herrenschmidt 22935cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */ 23039c870d5SOlof Johansson 23139c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI 23230925748SBenjamin Herrenschmidt 23330925748SBenjamin Herrenschmidt #define UDBG_UART_PAS_ADDR ((void __iomem *)0xfcff03f8UL) 23430925748SBenjamin Herrenschmidt 23530925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pas(unsigned int reg) 23639c870d5SOlof Johansson { 23730925748SBenjamin Herrenschmidt return real_205_readb(UDBG_UART_PAS_ADDR + reg); 238af9c7249SAndrew Klossner } 239af9c7249SAndrew Klossner 24030925748SBenjamin Herrenschmidt static void udbg_uart_out_pas(unsigned int reg, u8 val) 241af9c7249SAndrew Klossner { 24230925748SBenjamin Herrenschmidt real_205_writeb(val, UDBG_UART_PAS_ADDR + reg); 24339c870d5SOlof Johansson } 24439c870d5SOlof Johansson 24530925748SBenjamin Herrenschmidt void __init udbg_init_pas_realmode(void) 24639c870d5SOlof Johansson { 24730925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_pas; 24830925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_pas; 24930925748SBenjamin Herrenschmidt udbg_use_uart(); 25039c870d5SOlof Johansson } 25130925748SBenjamin Herrenschmidt 25230925748SBenjamin Herrenschmidt #endif /* CONFIG_PPC_PASEMI */ 253d9b55a03SDavid Gibson 254d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x 25530925748SBenjamin Herrenschmidt 256d9b55a03SDavid Gibson #include <platforms/44x/44x.h> 257d9b55a03SDavid Gibson 25830925748SBenjamin Herrenschmidt static u8 udbg_uart_in_44x_as1(unsigned int reg) 259d9b55a03SDavid Gibson { 26030925748SBenjamin Herrenschmidt return as1_readb((void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 261af9c7249SAndrew Klossner } 262af9c7249SAndrew Klossner 26330925748SBenjamin Herrenschmidt static void udbg_uart_out_44x_as1(unsigned int reg, u8 val) 264af9c7249SAndrew Klossner { 26530925748SBenjamin Herrenschmidt as1_writeb(val, (void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 26670dea47dSHollis Blanchard } 26770dea47dSHollis Blanchard 268d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void) 269d9b55a03SDavid Gibson { 27030925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_44x_as1; 27130925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_44x_as1; 27230925748SBenjamin Herrenschmidt udbg_use_uart(); 273d9b55a03SDavid Gibson } 27430925748SBenjamin Herrenschmidt 275d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ 2769dae8afdSBenjamin Herrenschmidt 2779dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x 27830925748SBenjamin Herrenschmidt 27930925748SBenjamin Herrenschmidt static u8 udbg_uart_in_40x(unsigned int reg) 2809dae8afdSBenjamin Herrenschmidt { 28130925748SBenjamin Herrenschmidt return real_readb((void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 28230925748SBenjamin Herrenschmidt + reg); 283af9c7249SAndrew Klossner } 284af9c7249SAndrew Klossner 28530925748SBenjamin Herrenschmidt static void udbg_uart_out_40x(unsigned int reg, u8 val) 286af9c7249SAndrew Klossner { 28730925748SBenjamin Herrenschmidt real_writeb(val, (void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 28830925748SBenjamin Herrenschmidt + reg); 2899dae8afdSBenjamin Herrenschmidt } 2909dae8afdSBenjamin Herrenschmidt 2919dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void) 2929dae8afdSBenjamin Herrenschmidt { 29330925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_40x; 29430925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_40x; 29530925748SBenjamin Herrenschmidt udbg_use_uart(); 2969dae8afdSBenjamin Herrenschmidt } 29730925748SBenjamin Herrenschmidt 2989dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */ 299a0496d45SJack Miller 30030925748SBenjamin Herrenschmidt 301a0496d45SJack Miller #ifdef CONFIG_PPC_EARLY_DEBUG_WSP 30230925748SBenjamin Herrenschmidt 303a0496d45SJack Miller static void udbg_wsp_flush(void) 304a0496d45SJack Miller { 305a0496d45SJack Miller if (udbg_comport) { 306a0496d45SJack Miller while ((readb(&udbg_comport->lsr) & LSR_THRE) == 0) 307a0496d45SJack Miller /* wait for idle */; 308a0496d45SJack Miller } 309a0496d45SJack Miller } 310a0496d45SJack Miller 311a0496d45SJack Miller static void udbg_wsp_putc(char c) 312a0496d45SJack Miller { 313a0496d45SJack Miller if (udbg_comport) { 314a0496d45SJack Miller if (c == '\n') 315a0496d45SJack Miller udbg_wsp_putc('\r'); 316a0496d45SJack Miller udbg_wsp_flush(); 317a0496d45SJack Miller writeb(c, &udbg_comport->thr); eieio(); 318a0496d45SJack Miller } 319a0496d45SJack Miller } 320a0496d45SJack Miller 321a0496d45SJack Miller static int udbg_wsp_getc(void) 322a0496d45SJack Miller { 323a0496d45SJack Miller if (udbg_comport) { 324a0496d45SJack Miller while ((readb(&udbg_comport->lsr) & LSR_DR) == 0) 325a0496d45SJack Miller ; /* wait for char */ 326a0496d45SJack Miller return readb(&udbg_comport->rbr); 327a0496d45SJack Miller } 328a0496d45SJack Miller return -1; 329a0496d45SJack Miller } 330a0496d45SJack Miller 331a0496d45SJack Miller static int udbg_wsp_getc_poll(void) 332a0496d45SJack Miller { 333a0496d45SJack Miller if (udbg_comport) 334a0496d45SJack Miller if (readb(&udbg_comport->lsr) & LSR_DR) 335a0496d45SJack Miller return readb(&udbg_comport->rbr); 336a0496d45SJack Miller return -1; 337a0496d45SJack Miller } 338a0496d45SJack Miller 339a0496d45SJack Miller void __init udbg_init_wsp(void) 340a0496d45SJack Miller { 34130925748SBenjamin Herrenschmidt udbg_uart_init_mmio(WSP_UART_VIRT, 1); 34230925748SBenjamin Herrenschmidt udbg_uart_setup(57600, 50000000); 343a0496d45SJack Miller } 34430925748SBenjamin Herrenschmidt 345a0496d45SJack Miller #endif /* CONFIG_PPC_EARLY_DEBUG_WSP */ 346