![Benjamin Herrenschmidt](/assets/img/avatar_default.png)
This moves the discovery of legacy serial ports to a separate file, makes it common to ppc32 and ppc64, and reworks it to use the new OF address translators to get to the ports early. This new version can also detect some PCI serial cards using legacy chips and will probably match those discovered port with the default console choice. Only ppc64 gets udbg still yet, unifying udbg isn't finished yet. It also adds some speed-probing code to udbg so that the default console can come up at the same speed it was set to by the firmware. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
167 lines
3.8 KiB
C
167 lines
3.8 KiB
C
/*
|
|
* udbg for for NS16550 compatable serial ports
|
|
*
|
|
* Copyright (C) 2001-2005 PPC 64 Team, IBM Corp
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
#include <linux/config.h>
|
|
#include <linux/types.h>
|
|
#include <asm/udbg.h>
|
|
#include <asm/io.h>
|
|
|
|
extern u8 real_readb(volatile u8 __iomem *addr);
|
|
extern void real_writeb(u8 data, volatile u8 __iomem *addr);
|
|
|
|
struct NS16550 {
|
|
/* this struct must be packed */
|
|
unsigned char rbr; /* 0 */
|
|
unsigned char ier; /* 1 */
|
|
unsigned char fcr; /* 2 */
|
|
unsigned char lcr; /* 3 */
|
|
unsigned char mcr; /* 4 */
|
|
unsigned char lsr; /* 5 */
|
|
unsigned char msr; /* 6 */
|
|
unsigned char scr; /* 7 */
|
|
};
|
|
|
|
#define thr rbr
|
|
#define iir fcr
|
|
#define dll rbr
|
|
#define dlm ier
|
|
#define dlab lcr
|
|
|
|
#define LSR_DR 0x01 /* Data ready */
|
|
#define LSR_OE 0x02 /* Overrun */
|
|
#define LSR_PE 0x04 /* Parity error */
|
|
#define LSR_FE 0x08 /* Framing error */
|
|
#define LSR_BI 0x10 /* Break */
|
|
#define LSR_THRE 0x20 /* Xmit holding register empty */
|
|
#define LSR_TEMT 0x40 /* Xmitter empty */
|
|
#define LSR_ERR 0x80 /* Error */
|
|
|
|
#define LCR_DLAB 0x80
|
|
|
|
static volatile struct NS16550 __iomem *udbg_comport;
|
|
|
|
static void udbg_550_putc(unsigned char c)
|
|
{
|
|
if (udbg_comport) {
|
|
while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
|
|
/* wait for idle */;
|
|
out_8(&udbg_comport->thr, c);
|
|
if (c == '\n')
|
|
udbg_550_putc('\r');
|
|
}
|
|
}
|
|
|
|
static int udbg_550_getc_poll(void)
|
|
{
|
|
if (udbg_comport) {
|
|
if ((in_8(&udbg_comport->lsr) & LSR_DR) != 0)
|
|
return in_8(&udbg_comport->rbr);
|
|
else
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static unsigned char udbg_550_getc(void)
|
|
{
|
|
if (udbg_comport) {
|
|
while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
|
|
/* wait for char */;
|
|
return in_8(&udbg_comport->rbr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void udbg_init_uart(void __iomem *comport, unsigned int speed,
|
|
unsigned int clock)
|
|
{
|
|
unsigned int dll, base_bauds = clock / 16;
|
|
|
|
if (speed == 0)
|
|
speed = 9600;
|
|
dll = base_bauds / speed;
|
|
|
|
if (comport) {
|
|
udbg_comport = (struct NS16550 __iomem *)comport;
|
|
out_8(&udbg_comport->lcr, 0x00);
|
|
out_8(&udbg_comport->ier, 0xff);
|
|
out_8(&udbg_comport->ier, 0x00);
|
|
out_8(&udbg_comport->lcr, LCR_DLAB);
|
|
out_8(&udbg_comport->dll, dll & 0xff);
|
|
out_8(&udbg_comport->dlm, dll >> 8);
|
|
/* 8 data, 1 stop, no parity */
|
|
out_8(&udbg_comport->lcr, 0x03);
|
|
/* RTS/DTR */
|
|
out_8(&udbg_comport->mcr, 0x03);
|
|
/* Clear & enable FIFOs */
|
|
out_8(&udbg_comport->fcr ,0x07);
|
|
udbg_putc = udbg_550_putc;
|
|
udbg_getc = udbg_550_getc;
|
|
udbg_getc_poll = udbg_550_getc_poll;
|
|
}
|
|
}
|
|
|
|
unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
|
|
{
|
|
unsigned int dll, dlm, divisor, prescaler, speed;
|
|
u8 old_lcr;
|
|
volatile struct NS16550 __iomem *port = comport;
|
|
|
|
old_lcr = in_8(&port->lcr);
|
|
|
|
/* select divisor latch registers. */
|
|
out_8(&port->lcr, LCR_DLAB);
|
|
|
|
/* now, read the divisor */
|
|
dll = in_8(&port->dll);
|
|
dlm = in_8(&port->dlm);
|
|
divisor = dlm << 8 | dll;
|
|
|
|
/* check prescaling */
|
|
if (in_8(&port->mcr) & 0x80)
|
|
prescaler = 4;
|
|
else
|
|
prescaler = 1;
|
|
|
|
/* restore the LCR */
|
|
out_8(&port->lcr, old_lcr);
|
|
|
|
/* calculate speed */
|
|
speed = (clock / prescaler) / (divisor * 16);
|
|
|
|
/* sanity check */
|
|
if (speed < 9600 || speed > 115200)
|
|
speed = 9600;
|
|
|
|
return speed;
|
|
}
|
|
|
|
#ifdef CONFIG_PPC_MAPLE
|
|
void udbg_maple_real_putc(unsigned char c)
|
|
{
|
|
if (udbg_comport) {
|
|
while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
|
|
/* wait for idle */;
|
|
real_writeb(c, &udbg_comport->thr); eieio();
|
|
if (c == '\n')
|
|
udbg_maple_real_putc('\r');
|
|
}
|
|
}
|
|
|
|
void udbg_init_maple_realmode(void)
|
|
{
|
|
udbg_comport = (volatile struct NS16550 __iomem *)0xf40003f8;
|
|
|
|
udbg_putc = udbg_maple_real_putc;
|
|
udbg_getc = NULL;
|
|
udbg_getc_poll = NULL;
|
|
}
|
|
#endif /* CONFIG_PPC_MAPLE */
|