12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 235cd8785SDavid Gibson /* 325985edcSLucas De Marchi * udbg for NS16550 compatible serial ports 435cd8785SDavid Gibson * 535cd8785SDavid Gibson * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp 635cd8785SDavid Gibson */ 735cd8785SDavid Gibson #include <linux/types.h> 835cd8785SDavid Gibson #include <asm/udbg.h> 935cd8785SDavid Gibson #include <asm/io.h> 10a0496d45SJack Miller #include <asm/reg_a2.h> 1135cd8785SDavid Gibson 1235cd8785SDavid Gibson extern u8 real_readb(volatile u8 __iomem *addr); 1335cd8785SDavid Gibson extern void real_writeb(u8 data, volatile u8 __iomem *addr); 1439c870d5SOlof Johansson extern u8 real_205_readb(volatile u8 __iomem *addr); 1539c870d5SOlof Johansson extern void real_205_writeb(u8 data, volatile u8 __iomem *addr); 1635cd8785SDavid Gibson 1730925748SBenjamin Herrenschmidt #define UART_RBR 0 1830925748SBenjamin Herrenschmidt #define UART_IER 1 1930925748SBenjamin Herrenschmidt #define UART_FCR 2 2030925748SBenjamin Herrenschmidt #define UART_LCR 3 2130925748SBenjamin Herrenschmidt #define UART_MCR 4 2230925748SBenjamin Herrenschmidt #define UART_LSR 5 2330925748SBenjamin Herrenschmidt #define UART_MSR 6 2430925748SBenjamin Herrenschmidt #define UART_SCR 7 2530925748SBenjamin Herrenschmidt #define UART_THR UART_RBR 2630925748SBenjamin Herrenschmidt #define UART_IIR UART_FCR 2730925748SBenjamin Herrenschmidt #define UART_DLL UART_RBR 2830925748SBenjamin Herrenschmidt #define UART_DLM UART_IER 2930925748SBenjamin Herrenschmidt #define UART_DLAB UART_LCR 3035cd8785SDavid Gibson 3135cd8785SDavid Gibson #define LSR_DR 0x01 /* Data ready */ 3235cd8785SDavid Gibson #define LSR_OE 0x02 /* Overrun */ 3335cd8785SDavid Gibson #define LSR_PE 0x04 /* Parity error */ 3435cd8785SDavid Gibson #define LSR_FE 0x08 /* Framing error */ 3535cd8785SDavid Gibson #define LSR_BI 0x10 /* Break */ 3635cd8785SDavid Gibson #define LSR_THRE 0x20 /* Xmit holding register empty */ 3735cd8785SDavid Gibson #define LSR_TEMT 0x40 /* Xmitter empty */ 3835cd8785SDavid Gibson #define LSR_ERR 0x80 /* Error */ 3935cd8785SDavid Gibson 40463ce0e1SBenjamin Herrenschmidt #define LCR_DLAB 0x80 41463ce0e1SBenjamin Herrenschmidt 4230925748SBenjamin Herrenschmidt static u8 (*udbg_uart_in)(unsigned int reg); 4330925748SBenjamin Herrenschmidt static void (*udbg_uart_out)(unsigned int reg, u8 data); 4435cd8785SDavid Gibson 4530925748SBenjamin Herrenschmidt static void udbg_uart_flush(void) 4635cd8785SDavid Gibson { 4730925748SBenjamin Herrenschmidt if (!udbg_uart_in) 4830925748SBenjamin Herrenschmidt return; 4930925748SBenjamin Herrenschmidt 5030925748SBenjamin Herrenschmidt /* wait for idle */ 5130925748SBenjamin Herrenschmidt while ((udbg_uart_in(UART_LSR) & LSR_THRE) == 0) 5230925748SBenjamin Herrenschmidt cpu_relax(); 53af9c7249SAndrew Klossner } 54af9c7249SAndrew Klossner 5530925748SBenjamin Herrenschmidt static void udbg_uart_putc(char c) 56af9c7249SAndrew Klossner { 5730925748SBenjamin Herrenschmidt if (!udbg_uart_out) 5830925748SBenjamin Herrenschmidt return; 5930925748SBenjamin Herrenschmidt 6035cd8785SDavid Gibson if (c == '\n') 6130925748SBenjamin Herrenschmidt udbg_uart_putc('\r'); 6230925748SBenjamin Herrenschmidt udbg_uart_flush(); 6330925748SBenjamin Herrenschmidt udbg_uart_out(UART_THR, c); 6435cd8785SDavid Gibson } 6535cd8785SDavid Gibson 6630925748SBenjamin Herrenschmidt static int udbg_uart_getc_poll(void) 6735cd8785SDavid Gibson { 68cd32e2dcSAnton Blanchard if (!udbg_uart_in) 69cd32e2dcSAnton Blanchard return -1; 70cd32e2dcSAnton Blanchard 71cd32e2dcSAnton Blanchard if (!(udbg_uart_in(UART_LSR) & LSR_DR)) 7230925748SBenjamin Herrenschmidt return udbg_uart_in(UART_RBR); 73cd32e2dcSAnton Blanchard 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 87*d276960dSNick Child static void __init 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 95*d276960dSNick Child void __init 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 124*d276960dSNick Child unsigned int __init 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 175*d276960dSNick Child void __init 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 197*d276960dSNick Child void __init 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 */ 29948b545b8SBenjamin Herrenschmidt 30048b545b8SBenjamin Herrenschmidt #ifdef CONFIG_PPC_EARLY_DEBUG_MICROWATT 30148b545b8SBenjamin Herrenschmidt 30248b545b8SBenjamin Herrenschmidt #define UDBG_UART_MW_ADDR ((void __iomem *)0xc0002000) 30348b545b8SBenjamin Herrenschmidt 30448b545b8SBenjamin Herrenschmidt static u8 udbg_uart_in_isa300_rm(unsigned int reg) 30548b545b8SBenjamin Herrenschmidt { 30648b545b8SBenjamin Herrenschmidt uint64_t msr = mfmsr(); 30748b545b8SBenjamin Herrenschmidt uint8_t c; 30848b545b8SBenjamin Herrenschmidt 30948b545b8SBenjamin Herrenschmidt mtmsr(msr & ~(MSR_EE|MSR_DR)); 31048b545b8SBenjamin Herrenschmidt isync(); 31148b545b8SBenjamin Herrenschmidt eieio(); 31248b545b8SBenjamin Herrenschmidt c = __raw_rm_readb(UDBG_UART_MW_ADDR + (reg << 2)); 31348b545b8SBenjamin Herrenschmidt mtmsr(msr); 31448b545b8SBenjamin Herrenschmidt isync(); 31548b545b8SBenjamin Herrenschmidt return c; 31648b545b8SBenjamin Herrenschmidt } 31748b545b8SBenjamin Herrenschmidt 31848b545b8SBenjamin Herrenschmidt static void udbg_uart_out_isa300_rm(unsigned int reg, u8 val) 31948b545b8SBenjamin Herrenschmidt { 32048b545b8SBenjamin Herrenschmidt uint64_t msr = mfmsr(); 32148b545b8SBenjamin Herrenschmidt 32248b545b8SBenjamin Herrenschmidt mtmsr(msr & ~(MSR_EE|MSR_DR)); 32348b545b8SBenjamin Herrenschmidt isync(); 32448b545b8SBenjamin Herrenschmidt eieio(); 32548b545b8SBenjamin Herrenschmidt __raw_rm_writeb(val, UDBG_UART_MW_ADDR + (reg << 2)); 32648b545b8SBenjamin Herrenschmidt mtmsr(msr); 32748b545b8SBenjamin Herrenschmidt isync(); 32848b545b8SBenjamin Herrenschmidt } 32948b545b8SBenjamin Herrenschmidt 33048b545b8SBenjamin Herrenschmidt void __init udbg_init_debug_microwatt(void) 33148b545b8SBenjamin Herrenschmidt { 33248b545b8SBenjamin Herrenschmidt udbg_uart_in = udbg_uart_in_isa300_rm; 33348b545b8SBenjamin Herrenschmidt udbg_uart_out = udbg_uart_out_isa300_rm; 33448b545b8SBenjamin Herrenschmidt udbg_use_uart(); 33548b545b8SBenjamin Herrenschmidt } 33648b545b8SBenjamin Herrenschmidt 33748b545b8SBenjamin Herrenschmidt #endif /* CONFIG_PPC_EARLY_DEBUG_MICROWATT */ 338