linux/arch/powerpc/kernel/udbg_16550.c
<<
>>
Prefs
   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 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 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 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 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 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