Skip to contentSkip to conversation

ppc64: properly build list of legacy serial ports from OF tree

PATCH New Benjamin Herrenschmidt 1 participant 1 message Oct 23, 2004

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ppc64: properly build list of legacy serial ports from OF tree

Hi !

This patch applies on top of "8250: Let arch provide the list of leagacy ports dynamically".

It adds a generic ppc64 routine for use by platforms that will build the list of 8250 ports with proper IO address and irq numbers based on the OF device-tree, and will also provide the physical address where the primary one is, for use with the early boot console stuff.

Finally, it adds use of this new routine to the pSeries platform, replacing the old one that was there. It will be also used by the new eval board platform that is coming soon.

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.

arch/ppc64/kernel/setup.c +182 -0
Index: linux-work/arch/ppc64/kernel/setup.c
===================================================================
@@ -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("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)
 {
arch/ppc64/kernel/udbg.c +15 -2
Index: linux-work/arch/ppc64/kernel/udbg.c
===================================================================
@@ -49,15 +49,28 @@
 
 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 */
include/asm-ppc64/serial.h +6 -106

119 lines not on this page · raw message

arch/ppc64/kernel/pSeries_setup.c +5 -71

106 lines not on this page · raw message

include/asm-ppc64/udbg.h +1 -1
Index: linux-work/include/asm-ppc64/udbg.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);