udbg_16550.c (7128B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * udbg for NS16550 compatible serial ports 4 * 5 * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp 6 */ 7#include <linux/types.h> 8#include <asm/udbg.h> 9#include <asm/io.h> 10#include <asm/reg_a2.h> 11 12extern u8 real_readb(volatile u8 __iomem *addr); 13extern void real_writeb(u8 data, volatile u8 __iomem *addr); 14extern u8 real_205_readb(volatile u8 __iomem *addr); 15extern void real_205_writeb(u8 data, volatile u8 __iomem *addr); 16 17#define UART_RBR 0 18#define UART_IER 1 19#define UART_FCR 2 20#define UART_LCR 3 21#define UART_MCR 4 22#define UART_LSR 5 23#define UART_MSR 6 24#define UART_SCR 7 25#define UART_THR UART_RBR 26#define UART_IIR UART_FCR 27#define UART_DLL UART_RBR 28#define UART_DLM UART_IER 29#define UART_DLAB UART_LCR 30 31#define LSR_DR 0x01 /* Data ready */ 32#define LSR_OE 0x02 /* Overrun */ 33#define LSR_PE 0x04 /* Parity error */ 34#define LSR_FE 0x08 /* Framing error */ 35#define LSR_BI 0x10 /* Break */ 36#define LSR_THRE 0x20 /* Xmit holding register empty */ 37#define LSR_TEMT 0x40 /* Xmitter empty */ 38#define LSR_ERR 0x80 /* Error */ 39 40#define LCR_DLAB 0x80 41 42static u8 (*udbg_uart_in)(unsigned int reg); 43static void (*udbg_uart_out)(unsigned int reg, u8 data); 44 45static void udbg_uart_flush(void) 46{ 47 if (!udbg_uart_in) 48 return; 49 50 /* wait for idle */ 51 while ((udbg_uart_in(UART_LSR) & LSR_THRE) == 0) 52 cpu_relax(); 53} 54 55static void udbg_uart_putc(char c) 56{ 57 if (!udbg_uart_out) 58 return; 59 60 if (c == '\n') 61 udbg_uart_putc('\r'); 62 udbg_uart_flush(); 63 udbg_uart_out(UART_THR, c); 64} 65 66static int udbg_uart_getc_poll(void) 67{ 68 if (!udbg_uart_in) 69 return -1; 70 71 if (!(udbg_uart_in(UART_LSR) & LSR_DR)) 72 return udbg_uart_in(UART_RBR); 73 74 return -1; 75} 76 77static int udbg_uart_getc(void) 78{ 79 if (!udbg_uart_in) 80 return -1; 81 /* wait for char */ 82 while (!(udbg_uart_in(UART_LSR) & LSR_DR)) 83 cpu_relax(); 84 return udbg_uart_in(UART_RBR); 85} 86 87static void __init udbg_use_uart(void) 88{ 89 udbg_putc = udbg_uart_putc; 90 udbg_flush = udbg_uart_flush; 91 udbg_getc = udbg_uart_getc; 92 udbg_getc_poll = udbg_uart_getc_poll; 93} 94 95void __init udbg_uart_setup(unsigned int speed, unsigned int clock) 96{ 97 unsigned int dll, base_bauds; 98 99 if (!udbg_uart_out) 100 return; 101 102 if (clock == 0) 103 clock = 1843200; 104 if (speed == 0) 105 speed = 9600; 106 107 base_bauds = clock / 16; 108 dll = base_bauds / speed; 109 110 udbg_uart_out(UART_LCR, 0x00); 111 udbg_uart_out(UART_IER, 0xff); 112 udbg_uart_out(UART_IER, 0x00); 113 udbg_uart_out(UART_LCR, LCR_DLAB); 114 udbg_uart_out(UART_DLL, dll & 0xff); 115 udbg_uart_out(UART_DLM, dll >> 8); 116 /* 8 data, 1 stop, no parity */ 117 udbg_uart_out(UART_LCR, 0x3); 118 /* RTS/DTR */ 119 udbg_uart_out(UART_MCR, 0x3); 120 /* Clear & enable FIFOs */ 121 udbg_uart_out(UART_FCR, 0x7); 122} 123 124unsigned int __init udbg_probe_uart_speed(unsigned int clock) 125{ 126 unsigned int dll, dlm, divisor, prescaler, speed; 127 u8 old_lcr; 128 129 old_lcr = udbg_uart_in(UART_LCR); 130 131 /* select divisor latch registers. */ 132 udbg_uart_out(UART_LCR, old_lcr | LCR_DLAB); 133 134 /* now, read the divisor */ 135 dll = udbg_uart_in(UART_DLL); 136 dlm = udbg_uart_in(UART_DLM); 137 divisor = dlm << 8 | dll; 138 139 /* check prescaling */ 140 if (udbg_uart_in(UART_MCR) & 0x80) 141 prescaler = 4; 142 else 143 prescaler = 1; 144 145 /* restore the LCR */ 146 udbg_uart_out(UART_LCR, old_lcr); 147 148 /* calculate speed */ 149 speed = (clock / prescaler) / (divisor * 16); 150 151 /* sanity check */ 152 if (speed > (clock / 16)) 153 speed = 9600; 154 155 return speed; 156} 157 158static union { 159 unsigned char __iomem *mmio_base; 160 unsigned long pio_base; 161} udbg_uart; 162 163static unsigned int udbg_uart_stride = 1; 164 165static u8 udbg_uart_in_pio(unsigned int reg) 166{ 167 return inb(udbg_uart.pio_base + (reg * udbg_uart_stride)); 168} 169 170static void udbg_uart_out_pio(unsigned int reg, u8 data) 171{ 172 outb(data, udbg_uart.pio_base + (reg * udbg_uart_stride)); 173} 174 175void __init udbg_uart_init_pio(unsigned long port, unsigned int stride) 176{ 177 if (!port) 178 return; 179 udbg_uart.pio_base = port; 180 udbg_uart_stride = stride; 181 udbg_uart_in = udbg_uart_in_pio; 182 udbg_uart_out = udbg_uart_out_pio; 183 udbg_use_uart(); 184} 185 186static u8 udbg_uart_in_mmio(unsigned int reg) 187{ 188 return in_8(udbg_uart.mmio_base + (reg * udbg_uart_stride)); 189} 190 191static void udbg_uart_out_mmio(unsigned int reg, u8 data) 192{ 193 out_8(udbg_uart.mmio_base + (reg * udbg_uart_stride), data); 194} 195 196 197void __init udbg_uart_init_mmio(void __iomem *addr, unsigned int stride) 198{ 199 if (!addr) 200 return; 201 udbg_uart.mmio_base = addr; 202 udbg_uart_stride = stride; 203 udbg_uart_in = udbg_uart_in_mmio; 204 udbg_uart_out = udbg_uart_out_mmio; 205 udbg_use_uart(); 206} 207 208#ifdef CONFIG_PPC_MAPLE 209 210#define UDBG_UART_MAPLE_ADDR ((void __iomem *)0xf40003f8) 211 212static u8 udbg_uart_in_maple(unsigned int reg) 213{ 214 return real_readb(UDBG_UART_MAPLE_ADDR + reg); 215} 216 217static void udbg_uart_out_maple(unsigned int reg, u8 val) 218{ 219 real_writeb(val, UDBG_UART_MAPLE_ADDR + reg); 220} 221 222void __init udbg_init_maple_realmode(void) 223{ 224 udbg_uart_in = udbg_uart_in_maple; 225 udbg_uart_out = udbg_uart_out_maple; 226 udbg_use_uart(); 227} 228 229#endif /* CONFIG_PPC_MAPLE */ 230 231#ifdef CONFIG_PPC_PASEMI 232 233#define UDBG_UART_PAS_ADDR ((void __iomem *)0xfcff03f8UL) 234 235static u8 udbg_uart_in_pas(unsigned int reg) 236{ 237 return real_205_readb(UDBG_UART_PAS_ADDR + reg); 238} 239 240static void udbg_uart_out_pas(unsigned int reg, u8 val) 241{ 242 real_205_writeb(val, UDBG_UART_PAS_ADDR + reg); 243} 244 245void __init udbg_init_pas_realmode(void) 246{ 247 udbg_uart_in = udbg_uart_in_pas; 248 udbg_uart_out = udbg_uart_out_pas; 249 udbg_use_uart(); 250} 251 252#endif /* CONFIG_PPC_PASEMI */ 253 254#ifdef CONFIG_PPC_EARLY_DEBUG_44x 255 256#include <platforms/44x/44x.h> 257 258static u8 udbg_uart_in_44x_as1(unsigned int reg) 259{ 260 return as1_readb((void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 261} 262 263static void udbg_uart_out_44x_as1(unsigned int reg, u8 val) 264{ 265 as1_writeb(val, (void __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR + reg); 266} 267 268void __init udbg_init_44x_as1(void) 269{ 270 udbg_uart_in = udbg_uart_in_44x_as1; 271 udbg_uart_out = udbg_uart_out_44x_as1; 272 udbg_use_uart(); 273} 274 275#endif /* CONFIG_PPC_EARLY_DEBUG_44x */ 276 277#ifdef CONFIG_PPC_EARLY_DEBUG_40x 278 279static u8 udbg_uart_in_40x(unsigned int reg) 280{ 281 return real_readb((void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 282 + reg); 283} 284 285static void udbg_uart_out_40x(unsigned int reg, u8 val) 286{ 287 real_writeb(val, (void __iomem *)CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR 288 + reg); 289} 290 291void __init udbg_init_40x_realmode(void) 292{ 293 udbg_uart_in = udbg_uart_in_40x; 294 udbg_uart_out = udbg_uart_out_40x; 295 udbg_use_uart(); 296} 297 298#endif /* CONFIG_PPC_EARLY_DEBUG_40x */ 299 300#ifdef CONFIG_PPC_EARLY_DEBUG_MICROWATT 301 302#define UDBG_UART_MW_ADDR ((void __iomem *)0xc0002000) 303 304static u8 udbg_uart_in_isa300_rm(unsigned int reg) 305{ 306 uint64_t msr = mfmsr(); 307 uint8_t c; 308 309 mtmsr(msr & ~(MSR_EE|MSR_DR)); 310 isync(); 311 eieio(); 312 c = __raw_rm_readb(UDBG_UART_MW_ADDR + (reg << 2)); 313 mtmsr(msr); 314 isync(); 315 return c; 316} 317 318static void udbg_uart_out_isa300_rm(unsigned int reg, u8 val) 319{ 320 uint64_t msr = mfmsr(); 321 322 mtmsr(msr & ~(MSR_EE|MSR_DR)); 323 isync(); 324 eieio(); 325 __raw_rm_writeb(val, UDBG_UART_MW_ADDR + (reg << 2)); 326 mtmsr(msr); 327 isync(); 328} 329 330void __init udbg_init_debug_microwatt(void) 331{ 332 udbg_uart_in = udbg_uart_in_isa300_rm; 333 udbg_uart_out = udbg_uart_out_isa300_rm; 334 udbg_use_uart(); 335} 336 337#endif /* CONFIG_PPC_EARLY_DEBUG_MICROWATT */