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 { 72cd32e2dcSAnton Blanchard if (!udbg_uart_in) 73cd32e2dcSAnton Blanchard return -1; 74cd32e2dcSAnton Blanchard 75cd32e2dcSAnton Blanchard if (!(udbg_uart_in(UART_LSR) & LSR_DR)) 7630925748SBenjamin Herrenschmidt return udbg_uart_in(UART_RBR); 77cd32e2dcSAnton Blanchard 7835cd8785SDavid Gibson return -1; 7935cd8785SDavid Gibson } 8035cd8785SDavid Gibson 8130925748SBenjamin Herrenschmidt static int udbg_uart_getc(void) 8235cd8785SDavid Gibson { 8330925748SBenjamin Herrenschmidt if (!udbg_uart_in) 84bb6b9b28SBenjamin Herrenschmidt return -1; 8530925748SBenjamin Herrenschmidt /* wait for char */ 8630925748SBenjamin Herrenschmidt while (!(udbg_uart_in(UART_LSR) & LSR_DR)) 8730925748SBenjamin Herrenschmidt cpu_relax(); 8830925748SBenjamin Herrenschmidt return udbg_uart_in(UART_RBR); 8935cd8785SDavid Gibson } 9035cd8785SDavid Gibson 9130925748SBenjamin Herrenschmidt static void udbg_use_uart(void) 9230925748SBenjamin Herrenschmidt { 9330925748SBenjamin Herrenschmidt udbg_putc = udbg_uart_putc; 9430925748SBenjamin Herrenschmidt udbg_flush = udbg_uart_flush; 9530925748SBenjamin Herrenschmidt udbg_getc = udbg_uart_getc; 9630925748SBenjamin Herrenschmidt udbg_getc_poll = udbg_uart_getc_poll; 9730925748SBenjamin Herrenschmidt } 9830925748SBenjamin Herrenschmidt 9930925748SBenjamin Herrenschmidt void udbg_uart_setup(unsigned int speed, unsigned int clock) 10035cd8785SDavid Gibson { 101171505daSBenjamin Herrenschmidt unsigned int dll, base_bauds; 102463ce0e1SBenjamin Herrenschmidt 10330925748SBenjamin Herrenschmidt if (!udbg_uart_out) 10430925748SBenjamin Herrenschmidt return; 10530925748SBenjamin Herrenschmidt 106171505daSBenjamin Herrenschmidt if (clock == 0) 107171505daSBenjamin Herrenschmidt clock = 1843200; 108463ce0e1SBenjamin Herrenschmidt if (speed == 0) 109463ce0e1SBenjamin Herrenschmidt speed = 9600; 110171505daSBenjamin Herrenschmidt 111171505daSBenjamin Herrenschmidt base_bauds = clock / 16; 112463ce0e1SBenjamin Herrenschmidt dll = base_bauds / speed; 11335cd8785SDavid Gibson 11430925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, 0x00); 11530925748SBenjamin Herrenschmidt udbg_uart_out(UART_IER, 0xff); 11630925748SBenjamin Herrenschmidt udbg_uart_out(UART_IER, 0x00); 11730925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, LCR_DLAB); 11830925748SBenjamin Herrenschmidt udbg_uart_out(UART_DLL, dll & 0xff); 11930925748SBenjamin Herrenschmidt udbg_uart_out(UART_DLM, dll >> 8); 120463ce0e1SBenjamin Herrenschmidt /* 8 data, 1 stop, no parity */ 12130925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, 0x3); 122463ce0e1SBenjamin Herrenschmidt /* RTS/DTR */ 12330925748SBenjamin Herrenschmidt udbg_uart_out(UART_MCR, 0x3); 124463ce0e1SBenjamin Herrenschmidt /* Clear & enable FIFOs */ 12530925748SBenjamin Herrenschmidt udbg_uart_out(UART_FCR, 0x7); 12635cd8785SDavid Gibson } 12735cd8785SDavid Gibson 12830925748SBenjamin Herrenschmidt unsigned int udbg_probe_uart_speed(unsigned int clock) 129463ce0e1SBenjamin Herrenschmidt { 130463ce0e1SBenjamin Herrenschmidt unsigned int dll, dlm, divisor, prescaler, speed; 131463ce0e1SBenjamin Herrenschmidt u8 old_lcr; 132463ce0e1SBenjamin Herrenschmidt 13330925748SBenjamin Herrenschmidt old_lcr = udbg_uart_in(UART_LCR); 134463ce0e1SBenjamin Herrenschmidt 135463ce0e1SBenjamin Herrenschmidt /* select divisor latch registers. */ 13630925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, old_lcr | LCR_DLAB); 137463ce0e1SBenjamin Herrenschmidt 138463ce0e1SBenjamin Herrenschmidt /* now, read the divisor */ 13930925748SBenjamin Herrenschmidt dll = udbg_uart_in(UART_DLL); 14030925748SBenjamin Herrenschmidt dlm = udbg_uart_in(UART_DLM); 141463ce0e1SBenjamin Herrenschmidt divisor = dlm << 8 | dll; 142463ce0e1SBenjamin Herrenschmidt 143463ce0e1SBenjamin Herrenschmidt /* check prescaling */ 14430925748SBenjamin Herrenschmidt if (udbg_uart_in(UART_MCR) & 0x80) 145463ce0e1SBenjamin Herrenschmidt prescaler = 4; 146463ce0e1SBenjamin Herrenschmidt else 147463ce0e1SBenjamin Herrenschmidt prescaler = 1; 148463ce0e1SBenjamin Herrenschmidt 149463ce0e1SBenjamin Herrenschmidt /* restore the LCR */ 15030925748SBenjamin Herrenschmidt udbg_uart_out(UART_LCR, old_lcr); 151463ce0e1SBenjamin Herrenschmidt 152463ce0e1SBenjamin Herrenschmidt /* calculate speed */ 153463ce0e1SBenjamin Herrenschmidt speed = (clock / prescaler) / (divisor * 16); 154463ce0e1SBenjamin Herrenschmidt 155463ce0e1SBenjamin Herrenschmidt /* sanity check */ 156bb5e6491Sroel kluin if (speed > (clock / 16)) 157463ce0e1SBenjamin Herrenschmidt speed = 9600; 158463ce0e1SBenjamin Herrenschmidt 159463ce0e1SBenjamin Herrenschmidt return speed; 160463ce0e1SBenjamin Herrenschmidt } 161463ce0e1SBenjamin Herrenschmidt 16230925748SBenjamin Herrenschmidt static union { 16330925748SBenjamin Herrenschmidt unsigned char __iomem *mmio_base; 16430925748SBenjamin Herrenschmidt unsigned long pio_base; 16530925748SBenjamin Herrenschmidt } udbg_uart; 16630925748SBenjamin Herrenschmidt 16730925748SBenjamin Herrenschmidt static unsigned int udbg_uart_stride = 1; 16830925748SBenjamin Herrenschmidt 16930925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pio(unsigned int reg) 17035cd8785SDavid Gibson { 17130925748SBenjamin Herrenschmidt return inb(udbg_uart.pio_base + (reg * udbg_uart_stride)); 172af9c7249SAndrew Klossner } 173af9c7249SAndrew Klossner 17430925748SBenjamin Herrenschmidt static void udbg_uart_out_pio(unsigned int reg, u8 data) 175af9c7249SAndrew Klossner { 17630925748SBenjamin Herrenschmidt outb(data, udbg_uart.pio_base + (reg * udbg_uart_stride)); 17735cd8785SDavid Gibson } 17830925748SBenjamin Herrenschmidt 17930925748SBenjamin Herrenschmidt void udbg_uart_init_pio(unsigned long port, unsigned int stride) 18030925748SBenjamin Herrenschmidt { 18130925748SBenjamin Herrenschmidt if (!port) 18230925748SBenjamin Herrenschmidt return; 18330925748SBenjamin Herrenschmidt udbg_uart.pio_base = port; 18430925748SBenjamin Herrenschmidt udbg_uart_stride = stride; 18530925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_pio; 18630925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_pio; 18730925748SBenjamin Herrenschmidt udbg_use_uart(); 18830925748SBenjamin Herrenschmidt } 18930925748SBenjamin Herrenschmidt 19030925748SBenjamin Herrenschmidt static u8 udbg_uart_in_mmio(unsigned int reg) 19130925748SBenjamin Herrenschmidt { 19230925748SBenjamin Herrenschmidt return in_8(udbg_uart.mmio_base + (reg * udbg_uart_stride)); 19330925748SBenjamin Herrenschmidt } 19430925748SBenjamin Herrenschmidt 19530925748SBenjamin Herrenschmidt static void udbg_uart_out_mmio(unsigned int reg, u8 data) 19630925748SBenjamin Herrenschmidt { 19730925748SBenjamin Herrenschmidt out_8(udbg_uart.mmio_base + (reg * udbg_uart_stride), data); 19830925748SBenjamin Herrenschmidt } 19930925748SBenjamin Herrenschmidt 20030925748SBenjamin Herrenschmidt 20130925748SBenjamin Herrenschmidt void udbg_uart_init_mmio(void __iomem *addr, unsigned int stride) 20230925748SBenjamin Herrenschmidt { 20330925748SBenjamin Herrenschmidt if (!addr) 20430925748SBenjamin Herrenschmidt return; 20530925748SBenjamin Herrenschmidt udbg_uart.mmio_base = addr; 20630925748SBenjamin Herrenschmidt udbg_uart_stride = stride; 20730925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_mmio; 20830925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_mmio; 20930925748SBenjamin Herrenschmidt udbg_use_uart(); 21030925748SBenjamin Herrenschmidt } 21130925748SBenjamin Herrenschmidt 21230925748SBenjamin Herrenschmidt #ifdef CONFIG_PPC_MAPLE 21330925748SBenjamin Herrenschmidt 21430925748SBenjamin Herrenschmidt #define UDBG_UART_MAPLE_ADDR ((void __iomem *)0xf40003f8) 21530925748SBenjamin Herrenschmidt 21630925748SBenjamin Herrenschmidt static u8 udbg_uart_in_maple(unsigned int reg) 21730925748SBenjamin Herrenschmidt { 21830925748SBenjamin Herrenschmidt return real_readb(UDBG_UART_MAPLE_ADDR + reg); 21930925748SBenjamin Herrenschmidt } 22030925748SBenjamin Herrenschmidt 22130925748SBenjamin Herrenschmidt static void udbg_uart_out_maple(unsigned int reg, u8 val) 22230925748SBenjamin Herrenschmidt { 22330925748SBenjamin Herrenschmidt real_writeb(val, UDBG_UART_MAPLE_ADDR + reg); 22435cd8785SDavid Gibson } 22535cd8785SDavid Gibson 226296167aeSMichael Ellerman void __init udbg_init_maple_realmode(void) 22735cd8785SDavid Gibson { 22830925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_maple; 22930925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_maple; 23030925748SBenjamin Herrenschmidt udbg_use_uart(); 23135cd8785SDavid Gibson } 23230925748SBenjamin Herrenschmidt 23335cd8785SDavid Gibson #endif /* CONFIG_PPC_MAPLE */ 23439c870d5SOlof Johansson 23539c870d5SOlof Johansson #ifdef CONFIG_PPC_PASEMI 23630925748SBenjamin Herrenschmidt 23730925748SBenjamin Herrenschmidt #define UDBG_UART_PAS_ADDR ((void __iomem *)0xfcff03f8UL) 23830925748SBenjamin Herrenschmidt 23930925748SBenjamin Herrenschmidt static u8 udbg_uart_in_pas(unsigned int reg) 24039c870d5SOlof Johansson { 24130925748SBenjamin Herrenschmidt return real_205_readb(UDBG_UART_PAS_ADDR + reg); 242af9c7249SAndrew Klossner } 243af9c7249SAndrew Klossner 24430925748SBenjamin Herrenschmidt static void udbg_uart_out_pas(unsigned int reg, u8 val) 245af9c7249SAndrew Klossner { 24630925748SBenjamin Herrenschmidt real_205_writeb(val, UDBG_UART_PAS_ADDR + reg); 24739c870d5SOlof Johansson } 24839c870d5SOlof Johansson 24930925748SBenjamin Herrenschmidt void __init udbg_init_pas_realmode(void) 25039c870d5SOlof Johansson { 25130925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_pas; 25230925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_pas; 25330925748SBenjamin Herrenschmidt udbg_use_uart(); 25439c870d5SOlof Johansson } 25530925748SBenjamin Herrenschmidt 25630925748SBenjamin Herrenschmidt #endif /* CONFIG_PPC_PASEMI */ 257d9b55a03SDavid Gibson 258d9b55a03SDavid Gibson #ifdef CONFIG_PPC_EARLY_DEBUG_44x 25930925748SBenjamin Herrenschmidt 260d9b55a03SDavid Gibson #include <platforms/44x/44x.h> 261d9b55a03SDavid Gibson 26230925748SBenjamin Herrenschmidt static u8 udbg_uart_in_44x_as1(unsigned int reg) 263d9b55a03SDavid Gibson { 26430925748SBenjamin Herrenschmidt return as1_readb((void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 265af9c7249SAndrew Klossner } 266af9c7249SAndrew Klossner 26730925748SBenjamin Herrenschmidt static void udbg_uart_out_44x_as1(unsigned int reg, u8 val) 268af9c7249SAndrew Klossner { 26930925748SBenjamin Herrenschmidt as1_writeb(val, (void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 27070dea47dSHollis Blanchard } 27170dea47dSHollis Blanchard 272d9b55a03SDavid Gibson void __init udbg_init_44x_as1(void) 273d9b55a03SDavid Gibson { 27430925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_44x_as1; 27530925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_44x_as1; 27630925748SBenjamin Herrenschmidt udbg_use_uart(); 277d9b55a03SDavid Gibson } 27830925748SBenjamin Herrenschmidt 279d9b55a03SDavid Gibson #endif /* CONFIG_PPC_EARLY_DEBUG_44x */ 2809dae8afdSBenjamin Herrenschmidt 2819dae8afdSBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_40x 28230925748SBenjamin Herrenschmidt 28330925748SBenjamin Herrenschmidt static u8 udbg_uart_in_40x(unsigned int reg) 2849dae8afdSBenjamin Herrenschmidt { 28530925748SBenjamin Herrenschmidt return real_readb((void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 28630925748SBenjamin Herrenschmidt + reg); 287af9c7249SAndrew Klossner } 288af9c7249SAndrew Klossner 28930925748SBenjamin Herrenschmidt static void udbg_uart_out_40x(unsigned int reg, u8 val) 290af9c7249SAndrew Klossner { 29130925748SBenjamin Herrenschmidt real_writeb(val, (void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 29230925748SBenjamin Herrenschmidt + reg); 2939dae8afdSBenjamin Herrenschmidt } 2949dae8afdSBenjamin Herrenschmidt 2959dae8afdSBenjamin Herrenschmidt void __init udbg_init_40x_realmode(void) 2969dae8afdSBenjamin Herrenschmidt { 29730925748SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_40x; 29830925748SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_40x; 29930925748SBenjamin Herrenschmidt udbg_use_uart(); 3009dae8afdSBenjamin Herrenschmidt } 30130925748SBenjamin Herrenschmidt 3029dae8afdSBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_40x */ 303