Commit 74de2f94 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc64: properly build list of legacy serial ports from OF

This patch adds a ppc64 implementation of the routine providing
the list of default 8250 serial ports. It provides a empty list
by default unless the platform code fills it, and it provides
a generic function for user by Open Firmware based machines which
fills the list based on serial ports found in the OF device-tree.

It depends on the previous patch adding the generic support for
this to the 8250 driver.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 18bfebee
......@@ -81,6 +81,8 @@ extern void pSeries_get_rtc_time(struct rtc_time *rtc_time);
extern int pSeries_set_rtc_time(struct rtc_time *rtc_time);
extern void find_udbg_vterm(void);
extern void SystemReset_FWNMI(void), MachineCheck_FWNMI(void); /* from head.S */
extern void generic_find_legacy_serial_ports(unsigned int *default_speed);
int fwnmi_active; /* TRUE if an FWNMI handler is present */
unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address.
......@@ -247,75 +249,6 @@ static int __init pSeries_init_panel(void)
arch_initcall(pSeries_init_panel);
void __init pSeries_find_serial_port(void)
{
struct device_node *np;
unsigned long encode_phys_size = 32;
u32 *sizeprop;
struct isa_reg_property {
u32 space;
u32 address;
u32 size;
};
struct pci_reg_property {
struct pci_address addr;
u32 size_hi;
u32 size_lo;
};
DBG(" -> pSeries_find_serial_port()\n");
naca->serialPortAddr = 0;
np = of_find_node_by_path("/");
if (!np)
return;
sizeprop = (u32 *)get_property(np, "#size-cells", NULL);
if (sizeprop != NULL)
encode_phys_size = (*sizeprop) << 5;
for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
struct device_node *isa, *pci;
struct isa_reg_property *reg;
union pci_range *rangesp;
char *typep;
typep = (char *)get_property(np, "ibm,aix-loc", NULL);
if ((typep == NULL) || (typep && strcmp(typep, "S1")))
continue;
reg = (struct isa_reg_property *)get_property(np, "reg", NULL);
isa = of_get_parent(np);
if (!isa) {
DBG("no isa parent found\n");
break;
}
pci = of_get_parent(isa);
if (!pci) {
DBG("no pci parent found\n");
break;
}
rangesp = (union pci_range *)get_property(pci, "ranges", NULL);
if ( encode_phys_size == 32 )
naca->serialPortAddr = rangesp->pci32.phys+reg->address;
else {
naca->serialPortAddr =
((((unsigned long)rangesp->pci64.phys_hi) << 32)
|
(rangesp->pci64.phys_lo)) + reg->address;
}
break;
}
DBG(" <- pSeries_find_serial_port()\n");
}
/* Build up the firmware_features bitmask field
* using contents of device-tree/ibm,hypertas-functions.
* Ultimately this functionality may be moved into prom.c prom_init().
......@@ -395,6 +328,7 @@ static void __init pSeries_init_early(void)
{
void *comport;
int iommu_off = 0;
unsigned int default_speed;
DBG(" -> pSeries_init_early()\n");
......@@ -408,14 +342,14 @@ static void __init pSeries_init_early(void)
get_property(of_chosen, "linux,iommu-off", NULL));
}
pSeries_find_serial_port();
generic_find_legacy_serial_ports(&default_speed);
if (systemcfg->platform & PLATFORM_LPAR)
find_udbg_vterm();
else if (naca->serialPortAddr) {
/* Map the uart for udbg. */
comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE);
udbg_init_uart(comport);
udbg_init_uart(comport, default_speed);
ppc_md.udbg_putc = udbg_putc;
ppc_md.udbg_getc = udbg_getc;
......
......@@ -30,6 +30,8 @@
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/unistd.h>
#include <linux/serial.h>
#include <linux/8250.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/processor.h>
......@@ -51,6 +53,7 @@
#include <asm/system.h>
#include <asm/rtas.h>
#include <asm/iommu.h>
#include <asm/serial.h>
#ifdef DEBUG
#define DBG(fmt...) udbg_printf(fmt)
......@@ -1106,6 +1109,185 @@ __setup("spread_lpevents=", set_spread_lpevents );
__setup("decr_overclock_proc0=", set_decr_overclock_proc0 );
__setup("decr_overclock=", set_decr_overclock );
#ifndef CONFIG_PPC_ISERIES
/*
* This function can be used by platforms to "find" legacy serial ports.
* It works for "serial" nodes under an "isa" node, and will try to
* respect the "ibm,aix-loc" property if any. It works with up to 8
* ports.
*/
#define MAX_LEGACY_SERIAL_PORTS 8
static struct old_serial_port old_serial_ports[MAX_LEGACY_SERIAL_PORTS];
static unsigned int old_serial_count;
void __init generic_find_legacy_serial_ports(unsigned int *default_speed)
{
struct device_node *np;
u32 *sizeprop;
struct isa_reg_property {
u32 space;
u32 address;
u32 size;
};
struct pci_reg_property {
struct pci_address addr;
u32 size_hi;
u32 size_lo;
};
DBG(" -> generic_find_legacy_serial_port()\n");
naca->serialPortAddr = 0;
if (default_speed)
*default_speed = 0;
np = of_find_node_by_path("/");
if (!np)
return;
/* First fill our array */
for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
struct device_node *isa, *pci;
struct isa_reg_property *reg;
unsigned long phys_size, addr_size, io_base;
u32 *rangesp;
u32 *interrupts, *clk, *spd;
char *typep;
int index, rlen, rentsize;
/* Ok, first check if it's under an "isa" parent */
isa = of_get_parent(np);
if (!isa || strcmp(isa->name, "isa")) {
DBG("%s: no isa parent found\n", np->full_name);
continue;
}
/* Now look for an "ibm,aix-loc" property that gives us ordering
* if any...
*/
typep = (char *)get_property(np, "ibm,aix-loc", NULL);
/* Get the ISA port number */
reg = (struct isa_reg_property *)get_property(np, "reg", NULL);
if (reg == NULL)
goto next_port;
/* We assume the interrupt number isn't translated ... */
interrupts = (u32 *)get_property(np, "interrupts", NULL);
/* get clock freq. if present */
clk = (u32 *)get_property(np, "clock-frequency", NULL);
/* get default speed if present */
spd = (u32 *)get_property(np, "current-speed", NULL);
/* Default to locate at end of array */
index = old_serial_count; /* end of the array by default */
/* If we have a location index, then use it */
if (typep && *typep == 'S') {
index = simple_strtol(typep+1, NULL, 0) - 1;
/* if index is out of range, use end of array instead */
if (index >= MAX_LEGACY_SERIAL_PORTS)
index = old_serial_count;
/* if our index is still out of range, that mean that
* array is full, we could scan for a free slot but that
* make little sense to bother, just skip the port
*/
if (index >= MAX_LEGACY_SERIAL_PORTS)
goto next_port;
if (index >= old_serial_count)
old_serial_count = index + 1;
/* Check if there is a port who already claimed our slot */
if (old_serial_ports[index].port != 0) {
/* if we still have some room, move it, else override */
if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) {
DBG("Moved legacy port %d -> %d\n", index,
old_serial_count);
old_serial_ports[old_serial_count++] =
old_serial_ports[index];
} else {
DBG("Replacing legacy port %d\n", index);
}
}
}
if (index >= MAX_LEGACY_SERIAL_PORTS)
goto next_port;
if (index >= old_serial_count)
old_serial_count = index + 1;
/* Now fill the entry */
memset(&old_serial_ports[index], 0, sizeof(struct old_serial_port));
old_serial_ports[index].uart = 0;
old_serial_ports[index].baud_base = clk ? (*clk / 16) : BASE_BAUD;
old_serial_ports[index].port = reg->address;
old_serial_ports[index].irq = interrupts ? interrupts[0] : 0;
old_serial_ports[index].flags = ASYNC_BOOT_AUTOCONF;
DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n",
index,
old_serial_ports[index].port,
old_serial_ports[index].irq,
old_serial_ports[index].baud_base * 16);
/* Get phys address of IO reg for port 1 */
if (index != 0)
goto next_port;
pci = of_get_parent(isa);
if (!pci) {
DBG("%s: no pci parent found\n", np->full_name);
goto next_port;
}
rangesp = (u32 *)get_property(pci, "ranges", &rlen);
if (rangesp == NULL) {
of_node_put(pci);
goto next_port;
}
rlen /= 4;
/* we need the #size-cells of the PCI bridge node itself */
phys_size = 1;
sizeprop = (u32 *)get_property(pci, "#size-cells", NULL);
if (sizeprop != NULL)
phys_size = *sizeprop;
/* we need the parent #addr-cells */
addr_size = prom_n_addr_cells(pci);
rentsize = 3 + addr_size + phys_size;
io_base = 0;
for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) {
if (((rangesp[0] >> 24) & 0x3) != 1)
continue; /* not IO space */
io_base = rangesp[3];
if (addr_size == 2)
io_base = (io_base << 32) | rangesp[4];
}
if (io_base != 0) {
naca->serialPortAddr = io_base + reg->address;
if (default_speed && spd)
*default_speed = *spd;
}
of_node_put(pci);
next_port:
of_node_put(isa);
}
DBG(" <- generic_find_legacy_serial_port()\n");
}
struct old_serial_port *get_legacy_serial_ports(unsigned int *count)
{
*count = old_serial_count;
return old_serial_ports;
}
#else
struct old_serial_port *get_legacy_serial_ports(unsigned int *count)
{
*count = 0;
return 0;
}
#endif /* CONFIG_PPC_ISERIES */
EXPORT_SYMBOL(get_legacy_serial_ports);
#ifdef CONFIG_XMON
static int __init early_xmon(char *p)
{
......
......@@ -49,15 +49,28 @@ struct NS16550 {
static volatile struct NS16550 *udbg_comport;
void udbg_init_uart(void *comport)
void udbg_init_uart(void *comport, unsigned int speed)
{
u8 dll = 12;
switch(speed) {
case 115200:
dll = 1;
break;
case 57600:
dll = 2;
break;
case 38400:
dll = 3;
break;
}
if (comport) {
udbg_comport = (struct NS16550 *)comport;
udbg_comport->lcr = 0x00; eieio();
udbg_comport->ier = 0xFF; eieio();
udbg_comport->ier = 0x00; eieio();
udbg_comport->lcr = 0x80; eieio(); /* Access baud rate */
udbg_comport->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */
udbg_comport->dll = dll; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */
udbg_comport->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */
udbg_comport->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */
udbg_comport->mcr = 0x03; eieio(); /* RTS/DTR */
......
......@@ -18,113 +18,13 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define BASE_BAUD ( 1843200 / 16 )
#ifdef CONFIG_SERIAL_MANY_PORTS
#define RS_TABLE_SIZE 64
#else
#define RS_TABLE_SIZE 4
#endif
/* Standard COM flags (except for COM4, because of the 8514 problem) */
#ifdef CONFIG_SERIAL_DETECT_IRQ
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ)
#else
#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF
#endif
#ifdef CONFIG_SERIAL_MANY_PORTS
#define FOURPORT_FLAGS ASYNC_FOURPORT
#define ACCENT_FLAGS 0
#define BOCA_FLAGS 0
#define HUB6_FLAGS 0
#endif
/*
* The following define the access methods for the HUB6 card. All
* access is through two ports for all 24 possible chips. The card is
* selected through the high 2 bits, the port on that card with the
* "middle" 3 bits, and the register on that port with the bottom
* 3 bits.
*
* While the access port and interrupt is configurable, the default
* port locations are 0x302 for the port control register, and 0x303
* for the data read/write register. Normally, the interrupt is at irq3
* but can be anything from 3 to 7 inclusive. Note that using 3 will
* require disabling com2.
*/
#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
#define STD_SERIAL_PORT_DEFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \
{ 0, BASE_BAUD, 0x890, 0xf, STD_COM_FLAGS }, /* ttyS2 */ \
{ 0, BASE_BAUD, 0x898, 0xe, STD_COM_FLAGS }, /* ttyS3 */
#ifdef CONFIG_SERIAL_MANY_PORTS
#define EXTRA_SERIAL_PORT_DEFNS \
{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \
{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \
{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \
{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \
{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \
{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \
{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \
{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \
{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \
{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \
{ 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \
{ 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \
{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \
{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \
{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \
{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \
{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \
{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \
{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \
{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \
{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \
{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \
{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \
{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \
{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \
{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \
{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \
{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */
#else
#define EXTRA_SERIAL_PORT_DEFNS
#endif
/* You can have up to four HUB6's in the system, but I've only
* included two cards here for a total of twelve ports.
*/
#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS))
#define HUB6_SERIAL_PORT_DFNS \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */
#else
#define HUB6_SERIAL_PORT_DFNS
#endif
#define MCA_SERIAL_PORT_DFNS
/* Default baud base if not found in device-tree */
#define BASE_BAUD ( 1843200 / 16 )
#define SERIAL_PORT_DFNS \
STD_SERIAL_PORT_DEFNS \
EXTRA_SERIAL_PORT_DEFNS \
HUB6_SERIAL_PORT_DFNS \
MCA_SERIAL_PORT_DFNS
#define ARCH_HAS_GET_LEGACY_SERIAL_PORTS
struct old_serial_port;
extern struct old_serial_port *get_legacy_serial_ports(unsigned int *count);
#define UART_NR (8 + CONFIG_SERIAL_8250_NR_UARTS)
#endif /* _PPC64_SERIAL_H */
......@@ -10,7 +10,7 @@
* 2 of the License, or (at your option) any later version.
*/
void udbg_init_uart(void *comport);
void udbg_init_uart(void *comport, unsigned int speed);
void udbg_putc(unsigned char c);
unsigned char udbg_getc(void);
int udbg_getc_poll(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment