[SERIAL] sunzilog: Convert to of_driver.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index 9f42677..cbdf9d6 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1,5 +1,4 @@
-/*
- * sunzilog.c
+/* sunzilog.c: Zilog serial driver for Sparc systems.
  *
  * Driver for Zilog serial chips found on Sun workstations and
  * servers.  This driver could actually be made more generic.
@@ -10,7 +9,7 @@
  * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their
  * work there.
  *
- *  Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ *  Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net)
  */
 
 #include <linux/config.h>
@@ -38,10 +37,8 @@
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#ifdef CONFIG_SPARC64
-#include <asm/fhc.h>
-#endif
-#include <asm/sbus.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
 
 #if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -65,7 +62,7 @@
 #define ZSDELAY()
 #define ZSDELAY_LONG()
 #define ZS_WSYNC(__channel) \
-	sbus_readb(&((__channel)->control))
+	readb(&((__channel)->control))
 #endif
 
 static int num_sunzilog;
@@ -107,7 +104,7 @@
 	unsigned char			prev_status;
 
 #ifdef CONFIG_SERIO
-	struct serio			*serio;
+	struct serio			serio;
 	int				serio_open;
 #endif
 };
@@ -138,9 +135,9 @@
 {
 	unsigned char retval;
 
-	sbus_writeb(reg, &channel->control);
+	writeb(reg, &channel->control);
 	ZSDELAY();
-	retval = sbus_readb(&channel->control);
+	retval = readb(&channel->control);
 	ZSDELAY();
 
 	return retval;
@@ -149,9 +146,9 @@
 static void write_zsreg(struct zilog_channel __iomem *channel,
 			unsigned char reg, unsigned char value)
 {
-	sbus_writeb(reg, &channel->control);
+	writeb(reg, &channel->control);
 	ZSDELAY();
-	sbus_writeb(value, &channel->control);
+	writeb(value, &channel->control);
 	ZSDELAY();
 }
 
@@ -162,17 +159,17 @@
 	for (i = 0; i < 32; i++) {
 		unsigned char regval;
 
-		regval = sbus_readb(&channel->control);
+		regval = readb(&channel->control);
 		ZSDELAY();
 		if (regval & Rx_CH_AV)
 			break;
 
 		regval = read_zsreg(channel, R1);
-		sbus_readb(&channel->data);
+		readb(&channel->data);
 		ZSDELAY();
 
 		if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) {
-			sbus_writeb(ERR_RES, &channel->control);
+			writeb(ERR_RES, &channel->control);
 			ZSDELAY();
 			ZS_WSYNC(channel);
 		}
@@ -194,7 +191,7 @@
 		udelay(100);
 	}
 
-	sbus_writeb(ERR_RES, &channel->control);
+	writeb(ERR_RES, &channel->control);
 	ZSDELAY();
 	ZS_WSYNC(channel);
 
@@ -291,7 +288,7 @@
 		/* Stop-A is handled by drivers/char/keyboard.c now. */
 #ifdef CONFIG_SERIO
 		if (up->serio_open)
-			serio_interrupt(up->serio, ch, 0, regs);
+			serio_interrupt(&up->serio, ch, 0, regs);
 #endif
 	} else if (ZS_IS_MOUSE(up)) {
 		int ret = suncore_mouse_baud_detection(ch, is_break);
@@ -306,7 +303,7 @@
 		case 0:
 #ifdef CONFIG_SERIO
 			if (up->serio_open)
-				serio_interrupt(up->serio, ch, 0, regs);
+				serio_interrupt(&up->serio, ch, 0, regs);
 #endif
 			break;
 		};
@@ -330,12 +327,12 @@
 
 		r1 = read_zsreg(channel, R1);
 		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
-			sbus_writeb(ERR_RES, &channel->control);
+			writeb(ERR_RES, &channel->control);
 			ZSDELAY();
 			ZS_WSYNC(channel);
 		}
 
-		ch = sbus_readb(&channel->control);
+		ch = readb(&channel->control);
 		ZSDELAY();
 
 		/* This funny hack depends upon BRK_ABRT not interfering
@@ -347,7 +344,7 @@
 		if (!(ch & Rx_CH_AV))
 			break;
 
-		ch = sbus_readb(&channel->data);
+		ch = readb(&channel->data);
 		ZSDELAY();
 
 		ch &= up->parity_mask;
@@ -406,10 +403,10 @@
 {
 	unsigned char status;
 
-	status = sbus_readb(&channel->control);
+	status = readb(&channel->control);
 	ZSDELAY();
 
-	sbus_writeb(RES_EXT_INT, &channel->control);
+	writeb(RES_EXT_INT, &channel->control);
 	ZSDELAY();
 	ZS_WSYNC(channel);
 
@@ -421,7 +418,7 @@
 			 * confusing the PROM.
 			 */
 			while (1) {
-				status = sbus_readb(&channel->control);
+				status = readb(&channel->control);
 				ZSDELAY();
 				if (!(status & BRK_ABRT))
 					break;
@@ -458,7 +455,7 @@
 	struct circ_buf *xmit;
 
 	if (ZS_IS_CONS(up)) {
-		unsigned char status = sbus_readb(&channel->control);
+		unsigned char status = readb(&channel->control);
 		ZSDELAY();
 
 		/* TX still busy?  Just wait for the next TX done interrupt.
@@ -487,7 +484,7 @@
 
 	if (up->port.x_char) {
 		up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
-		sbus_writeb(up->port.x_char, &channel->data);
+		writeb(up->port.x_char, &channel->data);
 		ZSDELAY();
 		ZS_WSYNC(channel);
 
@@ -506,7 +503,7 @@
 		goto ack_tx_int;
 
 	up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
-	sbus_writeb(xmit->buf[xmit->tail], &channel->data);
+	writeb(xmit->buf[xmit->tail], &channel->data);
 	ZSDELAY();
 	ZS_WSYNC(channel);
 
@@ -519,7 +516,7 @@
 	return;
 
 ack_tx_int:
-	sbus_writeb(RES_Tx_P, &channel->control);
+	writeb(RES_Tx_P, &channel->control);
 	ZSDELAY();
 	ZS_WSYNC(channel);
 }
@@ -540,7 +537,7 @@
 		/* Channel A */
 		tty = NULL;
 		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
-			sbus_writeb(RES_H_IUS, &channel->control);
+			writeb(RES_H_IUS, &channel->control);
 			ZSDELAY();
 			ZS_WSYNC(channel);
 
@@ -563,7 +560,7 @@
 		spin_lock(&up->port.lock);
 		tty = NULL;
 		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
-			sbus_writeb(RES_H_IUS, &channel->control);
+			writeb(RES_H_IUS, &channel->control);
 			ZSDELAY();
 			ZS_WSYNC(channel);
 
@@ -594,7 +591,7 @@
 	unsigned char status;
 
 	channel = ZILOG_CHANNEL_FROM_PORT(port);
-	status = sbus_readb(&channel->control);
+	status = readb(&channel->control);
 	ZSDELAY();
 
 	return status;
@@ -682,7 +679,7 @@
 	up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
 	up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
 
-	status = sbus_readb(&channel->control);
+	status = readb(&channel->control);
 	ZSDELAY();
 
 	/* TX busy?  Just wait for the TX done interrupt.  */
@@ -693,7 +690,7 @@
 	 * IRQ sending engine.
 	 */
 	if (port->x_char) {
-		sbus_writeb(port->x_char, &channel->data);
+		writeb(port->x_char, &channel->data);
 		ZSDELAY();
 		ZS_WSYNC(channel);
 
@@ -702,7 +699,7 @@
 	} else {
 		struct circ_buf *xmit = &port->info->xmit;
 
-		sbus_writeb(xmit->buf[xmit->tail], &channel->data);
+		writeb(xmit->buf[xmit->tail], &channel->data);
 		ZSDELAY();
 		ZS_WSYNC(channel);
 
@@ -779,7 +776,7 @@
 	struct zilog_channel __iomem *channel;
 
 	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
-	up->prev_status = sbus_readb(&channel->control);
+	up->prev_status = readb(&channel->control);
 
 	/* Enable receiver and transmitter.  */
 	up->curregs[R3] |= RxENAB;
@@ -963,7 +960,7 @@
 
 static const char *sunzilog_type(struct uart_port *port)
 {
-	return "SunZilog";
+	return "zs";
 }
 
 /* We do not request/release mappings of the registers here, this
@@ -1012,7 +1009,6 @@
 static struct zilog_layout __iomem **sunzilog_chip_regs;
 
 static struct uart_sunzilog_port *sunzilog_irq_chain;
-static int zilog_irq = -1;
 
 static struct uart_driver sunzilog_reg = {
 	.owner		=	THIS_MODULE,
@@ -1021,232 +1017,47 @@
 	.major		=	TTY_MAJOR,
 };
 
-static void * __init alloc_one_table(unsigned long size)
+static int __init sunzilog_alloc_tables(void)
 {
-	void *ret;
+	struct uart_sunzilog_port *up;
+	unsigned long size;
+	int i;
 
-	ret = kmalloc(size, GFP_KERNEL);
-	if (ret != NULL)
-		memset(ret, 0, size);
+	size = NUM_CHANNELS * sizeof(struct uart_sunzilog_port);
+	sunzilog_port_table = kzalloc(size, GFP_KERNEL);
+	if (!sunzilog_port_table)
+		return -ENOMEM;
 
-	return ret;
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		up = &sunzilog_port_table[i];
+
+		spin_lock_init(&up->port.lock);
+
+		if (i == 0)
+			sunzilog_irq_chain = up;
+
+		if (i < NUM_CHANNELS - 1)
+			up->next = up + 1;
+		else
+			up->next = NULL;
+	}
+
+	size = NUM_SUNZILOG * sizeof(struct zilog_layout __iomem *);
+	sunzilog_chip_regs = kzalloc(size, GFP_KERNEL);
+	if (!sunzilog_chip_regs) {
+		kfree(sunzilog_port_table);
+		sunzilog_irq_chain = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
 }
 
-static void __init sunzilog_alloc_tables(void)
+static void sunzilog_free_tables(void)
 {
-	sunzilog_port_table = 
-		alloc_one_table(NUM_CHANNELS * sizeof(struct uart_sunzilog_port));
-	sunzilog_chip_regs = 
-		alloc_one_table(NUM_SUNZILOG * sizeof(struct zilog_layout __iomem *));
-
-	if (sunzilog_port_table == NULL || sunzilog_chip_regs == NULL) {
-		prom_printf("SunZilog: Cannot allocate tables.\n");
-		prom_halt();
-	}
-}
-
-#ifdef CONFIG_SPARC64
-
-/* We used to attempt to use the address property of the Zilog device node
- * but that totally is not necessary on sparc64.
- */
-static struct zilog_layout __iomem * __init get_zs_sun4u(int chip, int zsnode)
-{
-	void __iomem *mapped_addr;
-	unsigned int sun4u_ino;
-	struct sbus_bus *sbus = NULL;
-	struct sbus_dev *sdev = NULL;
-	int err;
-
-	if (central_bus == NULL) {
-		for_each_sbus(sbus) {
-			for_each_sbusdev(sdev, sbus) {
-				if (sdev->prom_node == zsnode)
-					goto found;
-			}
-		}
-	}
- found:
-	if (sdev == NULL && central_bus == NULL) {
-		prom_printf("SunZilog: sdev&&central == NULL for "
-			    "Zilog %d in get_zs_sun4u.\n", chip);
-		prom_halt();
-	}
-	if (central_bus == NULL) {
-		mapped_addr =
-			sbus_ioremap(&sdev->resource[0], 0,
-				     PAGE_SIZE,
-				     "Zilog Registers");
-	} else {
-		struct linux_prom_registers zsregs[1];
-
-		err = prom_getproperty(zsnode, "reg",
-				       (char *) &zsregs[0],
-				       sizeof(zsregs));
-		if (err == -1) {
-			prom_printf("SunZilog: Cannot map "
-				    "Zilog %d regs on "
-				    "central bus.\n", chip);
-			prom_halt();
-		}
-		apply_fhc_ranges(central_bus->child,
-				 &zsregs[0], 1);
-		apply_central_ranges(central_bus, &zsregs[0], 1);
-		mapped_addr = (void __iomem *)
-			((((u64)zsregs[0].which_io)<<32UL) |
-			((u64)zsregs[0].phys_addr));
-	}
-
-	if (zilog_irq == -1) {
-		if (central_bus) {
-			unsigned long iclr, imap;
-
-			iclr = central_bus->child->fhc_regs.uregs
-				+ FHC_UREGS_ICLR;
-			imap = central_bus->child->fhc_regs.uregs
-				+ FHC_UREGS_IMAP;
-			zilog_irq = build_irq(0, iclr, imap);
-		} else {
-			err = prom_getproperty(zsnode, "interrupts",
-					       (char *) &sun4u_ino,
-					       sizeof(sun4u_ino));
-			zilog_irq = sbus_build_irq(sbus_root, sun4u_ino);
-		}
-	}
-
-	return (struct zilog_layout __iomem *) mapped_addr;
-}
-#else /* CONFIG_SPARC64 */
-
-/*
- * XXX The sun4d case is utterly screwed: it tries to re-walk the tree
- * (for the 3rd time) in order to find bootbus and cpu. Streamline it.
- */
-static struct zilog_layout __iomem * __init get_zs_sun4cmd(int chip, int node)
-{
-	struct linux_prom_irqs irq_info[2];
-	void __iomem *mapped_addr = NULL;
-	int zsnode, cpunode, bbnode;
-	struct linux_prom_registers zsreg[4];
-	struct resource res;
-
-	if (sparc_cpu_model == sun4d) {
-		int walk;
-
-		zsnode = 0;
-		bbnode = 0;
-		cpunode = 0;
-		for (walk = prom_getchild(prom_root_node);
-		     (walk = prom_searchsiblings(walk, "cpu-unit")) != 0;
-		     walk = prom_getsibling(walk)) {
-			bbnode = prom_getchild(walk);
-			if (bbnode &&
-			    (bbnode = prom_searchsiblings(bbnode, "bootbus"))) {
-				if ((zsnode = prom_getchild(bbnode)) == node) {
-					cpunode = walk;
-					break;
-				}
-			}
-		}
-		if (!walk) {
-			prom_printf("SunZilog: Cannot find the %d'th bootbus on sun4d.\n",
-				    (chip / 2));
-			prom_halt();
-		}
-
-		if (prom_getproperty(zsnode, "reg",
-				     (char *) zsreg, sizeof(zsreg)) == -1) {
-			prom_printf("SunZilog: Cannot map Zilog %d\n", chip);
-			prom_halt();
-		}
-		/* XXX Looks like an off by one? */
-		prom_apply_generic_ranges(bbnode, cpunode, zsreg, 1);
-		res.start = zsreg[0].phys_addr;
-		res.end = res.start + (8 - 1);
-		res.flags = zsreg[0].which_io | IORESOURCE_IO;
-		mapped_addr = sbus_ioremap(&res, 0, 8, "Zilog Serial");
-
-	} else {
-		zsnode = node;
-
-#if 0 /* XXX When was this used? */
-		if (prom_getintdefault(zsnode, "slave", -1) != chipid) {
-			zsnode = prom_getsibling(zsnode);
-			continue;
-		}
-#endif
-
-		/*
-		 * "address" is only present on ports that OBP opened
-		 * (from Mitch Bradley's "Hitchhiker's Guide to OBP").
-		 * We do not use it.
-		 */
-
-		if (prom_getproperty(zsnode, "reg",
-				     (char *) zsreg, sizeof(zsreg)) == -1) {
-			prom_printf("SunZilog: Cannot map Zilog %d\n", chip);
-			prom_halt();
-		}
-		if (sparc_cpu_model == sun4m)	/* Crude. Pass parent. XXX */
-			prom_apply_obio_ranges(zsreg, 1);
-		res.start = zsreg[0].phys_addr;
-		res.end = res.start + (8 - 1);
-		res.flags = zsreg[0].which_io | IORESOURCE_IO;
-		mapped_addr = sbus_ioremap(&res, 0, 8, "Zilog Serial");
-	}
-
-	if (prom_getproperty(zsnode, "intr",
-			     (char *) irq_info, sizeof(irq_info))
-		    % sizeof(struct linux_prom_irqs)) {
-		prom_printf("SunZilog: Cannot get IRQ property for Zilog %d.\n",
-			    chip);
-		prom_halt();
-	}
-	if (zilog_irq == -1) {
-		zilog_irq = irq_info[0].pri;
-	} else if (zilog_irq != irq_info[0].pri) {
-		/* XXX. Dumb. Should handle per-chip IRQ, for add-ons. */
-		prom_printf("SunZilog: Inconsistent IRQ layout for Zilog %d.\n",
-			    chip);
-		prom_halt();
-	}
-
-	return (struct zilog_layout __iomem *) mapped_addr;
-}
-#endif /* !(CONFIG_SPARC64) */
-
-/* Get the address of the registers for SunZilog instance CHIP.  */
-static struct zilog_layout __iomem * __init get_zs(int chip, int node)
-{
-	if (chip < 0 || chip >= NUM_SUNZILOG) {
-		prom_printf("SunZilog: Illegal chip number %d in get_zs.\n", chip);
-		prom_halt();
-	}
-
-#ifdef CONFIG_SPARC64
-	return get_zs_sun4u(chip, node);
-#else
-
-	if (sparc_cpu_model == sun4) {
-		struct resource res;
-
-		/* Not probe-able, hard code it. */
-		switch (chip) {
-		case 0:
-			res.start = 0xf1000000;
-			break;
-		case 1:
-			res.start = 0xf0000000;
-			break;
-		};
-		zilog_irq = 12;
-		res.end = (res.start + (8 - 1));
-		res.flags = IORESOURCE_IO;
-		return sbus_ioremap(&res, 0, 8, "SunZilog");
-	}
-
-	return get_zs_sun4cmd(chip, node);
-#endif
+	kfree(sunzilog_port_table);
+	sunzilog_irq_chain = NULL;
+	kfree(sunzilog_chip_regs);
 }
 
 #define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */
@@ -1260,7 +1071,7 @@
 	 * udelay with ZSDELAY as that is a NOP on some platforms.  -DaveM
 	 */
 	do {
-		unsigned char val = sbus_readb(&channel->control);
+		unsigned char val = readb(&channel->control);
 		if (val & Tx_BUF_EMP) {
 			ZSDELAY();
 			break;
@@ -1268,7 +1079,7 @@
 		udelay(5);
 	} while (--loops);
 
-	sbus_writeb(ch, &channel->data);
+	writeb(ch, &channel->data);
 	ZSDELAY();
 	ZS_WSYNC(channel);
 }
@@ -1385,28 +1196,6 @@
 	.data   =	&sunzilog_reg,
 };
 
-static int __init sunzilog_console_init(void)
-{
-	int i;
-
-	if (con_is_present())
-		return 0;
-
-	for (i = 0; i < NUM_CHANNELS; i++) {
-		int this_minor = sunzilog_reg.minor + i;
-
-		if ((this_minor - 64) == (serial_console - 1))
-			break;
-	}
-	if (i == NUM_CHANNELS)
-		return 0;
-
-	sunzilog_console.index = i;
-	sunzilog_port_table[i].flags |= SUNZILOG_FLAG_IS_CONS;
-	register_console(&sunzilog_console);
-	return 0;
-}
-
 static inline struct console *SUNZILOG_CONSOLE(void)
 {
 	int i;
@@ -1431,101 +1220,8 @@
 
 #else
 #define SUNZILOG_CONSOLE()	(NULL)
-#define sunzilog_console_init() do { } while (0)
 #endif
 
-/*
- * We scan the PROM tree recursively. This is the most reliable way
- * to find Zilog nodes on various platforms. However, we face an extreme
- * shortage of kernel stack, so we must be very careful. To that end,
- * we scan only to a certain depth, and we use a common property buffer
- * in the scan structure.
- */
-#define ZS_PROPSIZE  128
-#define ZS_SCAN_DEPTH	5
-
-struct zs_probe_scan {
-	int depth;
-	void (*scanner)(struct zs_probe_scan *t, int node);
-
-	int devices;
-	char prop[ZS_PROPSIZE];
-};
-
-static int __inline__ sunzilog_node_ok(int node, const char *name, int len)
-{
-	if (strncmp(name, "zs", len) == 0)
-		return 1;
-	/* Don't fold this procedure just yet. Compare to su_node_ok(). */
-	return 0;
-}
-
-static void __init sunzilog_scan(struct zs_probe_scan *t, int node)
-{
-	int len;
-
-	for (; node != 0; node = prom_getsibling(node)) {
-		len = prom_getproperty(node, "name", t->prop, ZS_PROPSIZE);
-		if (len <= 1)
-			continue;		/* Broken PROM node */
-		if (sunzilog_node_ok(node, t->prop, len)) {
-			(*t->scanner)(t, node);
-		} else {
-			if (t->depth < ZS_SCAN_DEPTH) {
-				t->depth++;
-				sunzilog_scan(t, prom_getchild(node));
-				--t->depth;
-			}
-		}
-	}
-}
-
-static void __init sunzilog_prepare(void)
-{
-	struct uart_sunzilog_port *up;
-	struct zilog_layout __iomem *rp;
-	int channel, chip;
-
-	/*
-	 * Temporary fix.
-	 */
-	for (channel = 0; channel < NUM_CHANNELS; channel++)
-		spin_lock_init(&sunzilog_port_table[channel].port.lock);
-
-	sunzilog_irq_chain = up = &sunzilog_port_table[0];
-	for (channel = 0; channel < NUM_CHANNELS - 1; channel++)
-		up[channel].next = &up[channel + 1];
-	up[channel].next = NULL;
-
-	for (chip = 0; chip < NUM_SUNZILOG; chip++) {
-		rp = sunzilog_chip_regs[chip];
-		up[(chip * 2) + 0].port.membase = (void __iomem *)&rp->channelA;
-		up[(chip * 2) + 1].port.membase = (void __iomem *)&rp->channelB;
-
-		/* Channel A */
-		up[(chip * 2) + 0].port.iotype = UPIO_MEM;
-		up[(chip * 2) + 0].port.irq = zilog_irq;
-		up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
-		up[(chip * 2) + 0].port.fifosize = 1;
-		up[(chip * 2) + 0].port.ops = &sunzilog_pops;
-		up[(chip * 2) + 0].port.type = PORT_SUNZILOG;
-		up[(chip * 2) + 0].port.flags = 0;
-		up[(chip * 2) + 0].port.line = (chip * 2) + 0;
-		up[(chip * 2) + 0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
-
-		/* Channel B */
-		up[(chip * 2) + 1].port.iotype = UPIO_MEM;
-		up[(chip * 2) + 1].port.irq = zilog_irq;
-		up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
-		up[(chip * 2) + 1].port.fifosize = 1;
-		up[(chip * 2) + 1].port.ops = &sunzilog_pops;
-		up[(chip * 2) + 1].port.type = PORT_SUNZILOG;
-		up[(chip * 2) + 1].port.flags = 0;
-		up[(chip * 2) + 1].port.line = (chip * 2) + 1;
-		up[(chip * 2) + 1].flags |= 0;
-	}
-}
-
 static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channel)
 {
 	int baud, brg;
@@ -1539,8 +1235,6 @@
 		up->cflag = B4800 | CS8 | CLOCAL | CREAD;
 		baud = 4800;
 	}
-	printk(KERN_INFO "zs%d at 0x%p (irq = %d) is a SunZilog\n",
-	       channel, up->port.membase, zilog_irq);
 
 	up->curregs[R15] = BRKIE;
 	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
@@ -1552,216 +1246,253 @@
 #ifdef CONFIG_SERIO
 static void __init sunzilog_register_serio(struct uart_sunzilog_port *up, int channel)
 {
-	struct serio *serio;
+	struct serio *serio = &up->serio;
 
-	up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
-	if (serio) {
-		memset(serio, 0, sizeof(*serio));
+	serio->port_data = up;
 
-		serio->port_data = up;
-
-		serio->id.type = SERIO_RS232;
-		if (channel == KEYBOARD_LINE) {
-			serio->id.proto = SERIO_SUNKBD;
-			strlcpy(serio->name, "zskbd", sizeof(serio->name));
-		} else {
-			serio->id.proto = SERIO_SUN;
-			serio->id.extra = 1;
-			strlcpy(serio->name, "zsms", sizeof(serio->name));
-		}
-		strlcpy(serio->phys,
-			(channel == KEYBOARD_LINE ? "zs/serio0" : "zs/serio1"),
-			sizeof(serio->phys));
-
-		serio->write = sunzilog_serio_write;
-		serio->open = sunzilog_serio_open;
-		serio->close = sunzilog_serio_close;
-
-		serio_register_port(serio);
+	serio->id.type = SERIO_RS232;
+	if (channel == KEYBOARD_LINE) {
+		serio->id.proto = SERIO_SUNKBD;
+		strlcpy(serio->name, "zskbd", sizeof(serio->name));
 	} else {
-		printk(KERN_WARNING "zs%d: not enough memory for serio port\n",
-			channel);
+		serio->id.proto = SERIO_SUN;
+		serio->id.extra = 1;
+		strlcpy(serio->name, "zsms", sizeof(serio->name));
 	}
+	strlcpy(serio->phys,
+		(channel == KEYBOARD_LINE ? "zs/serio0" : "zs/serio1"),
+		sizeof(serio->phys));
+
+	serio->write = sunzilog_serio_write;
+	serio->open = sunzilog_serio_open;
+	serio->close = sunzilog_serio_close;
+	serio->dev.parent = up->port.dev;
+
+	serio_register_port(serio);
 }
 #endif
 
-static void __init sunzilog_init_hw(void)
+static void __init sunzilog_init_hw(struct uart_sunzilog_port *up)
 {
-	int i;
+	struct zilog_channel __iomem *channel;
+	unsigned long flags;
+	int baud, brg;
 
-	for (i = 0; i < NUM_CHANNELS; i++) {
-		struct uart_sunzilog_port *up = &sunzilog_port_table[i];
-		struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
-		unsigned long flags;
-		int baud, brg;
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
 
-		spin_lock_irqsave(&up->port.lock, flags);
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (ZS_IS_CHANNEL_A(up)) {
+		write_zsreg(channel, R9, FHWRES);
+		ZSDELAY_LONG();
+		(void) read_zsreg(channel, R0);
+	}
 
-		if (ZS_IS_CHANNEL_A(up)) {
-			write_zsreg(channel, R9, FHWRES);
-			ZSDELAY_LONG();
-			(void) read_zsreg(channel, R0);
-		}
+	if (up->port.line == KEYBOARD_LINE ||
+	    up->port.line == MOUSE_LINE) {
+		sunzilog_init_kbdms(up, up->port.line);
+		up->curregs[R9] |= (NV | MIE);
+		write_zsreg(channel, R9, up->curregs[R9]);
+	} else {
+		/* Normal serial TTY. */
+		up->parity_mask = 0xff;
+		up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+		up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
+		up->curregs[R3] = RxENAB | Rx8;
+		up->curregs[R5] = TxENAB | Tx8;
+		up->curregs[R9] = NV | MIE;
+		up->curregs[R10] = NRZ;
+		up->curregs[R11] = TCBR | RCBR;
+		baud = 9600;
+		brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+		up->curregs[R12] = (brg & 0xff);
+		up->curregs[R13] = (brg >> 8) & 0xff;
+		up->curregs[R14] = BRSRC | BRENAB;
+		__load_zsregs(channel, up->curregs);
+		write_zsreg(channel, R9, up->curregs[R9]);
+	}
 
-		if (i == KEYBOARD_LINE || i == MOUSE_LINE) {
-			sunzilog_init_kbdms(up, i);
-			up->curregs[R9] |= (NV | MIE);
-			write_zsreg(channel, R9, up->curregs[R9]);
-		} else {
-			/* Normal serial TTY. */
-			up->parity_mask = 0xff;
-			up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
-			up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
-			up->curregs[R3] = RxENAB | Rx8;
-			up->curregs[R5] = TxENAB | Tx8;
-			up->curregs[R9] = NV | MIE;
-			up->curregs[R10] = NRZ;
-			up->curregs[R11] = TCBR | RCBR;
-			baud = 9600;
-			brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
-			up->curregs[R12] = (brg & 0xff);
-			up->curregs[R13] = (brg >> 8) & 0xff;
-			up->curregs[R14] = BRSRC | BRENAB;
-			__load_zsregs(channel, up->curregs);
-			write_zsreg(channel, R9, up->curregs[R9]);
-		}
-
-		spin_unlock_irqrestore(&up->port.lock, flags);
+	spin_unlock_irqrestore(&up->port.lock, flags);
 
 #ifdef CONFIG_SERIO
-		if (i == KEYBOARD_LINE || i == MOUSE_LINE)
-			sunzilog_register_serio(up, i);
+	if (up->port.line == KEYBOARD_LINE || up->port.line == MOUSE_LINE)
+		sunzilog_register_serio(up, up->port.line);
 #endif
-	}
 }
 
-static struct zilog_layout __iomem * __init get_zs(int chip, int node);
-
-static void __init sunzilog_scan_probe(struct zs_probe_scan *t, int node)
+static int __devinit zs_get_instance(struct device_node *dp)
 {
-	sunzilog_chip_regs[t->devices] = get_zs(t->devices, node);
-	t->devices++;
-}
-
-static int __init sunzilog_ports_init(void)
-{
-	struct zs_probe_scan scan;
 	int ret;
-	int uart_count;
-	int i;
 
-	printk(KERN_DEBUG "SunZilog: %d chips.\n", NUM_SUNZILOG);
+	ret = of_getintprop_default(dp, "slave", -1);
+	if (ret != -1)
+		return ret;
 
-	scan.scanner = sunzilog_scan_probe;
-	scan.depth = 0;
-	scan.devices = 0;
-	sunzilog_scan(&scan, prom_getchild(prom_root_node));
-
-	sunzilog_prepare();
-
-	if (request_irq(zilog_irq, sunzilog_interrupt, SA_SHIRQ,
-			"SunZilog", sunzilog_irq_chain)) {
-		prom_printf("SunZilog: Unable to register zs interrupt handler.\n");
-		prom_halt();
-	}
-
-	sunzilog_init_hw();
-
-	/* We can only init this once we have probed the Zilogs
-	 * in the system. Do not count channels assigned to keyboards
-	 * or mice when we are deciding how many ports to register.
-	 */
-	uart_count = 0;
-	for (i = 0; i < NUM_CHANNELS; i++) {
-		struct uart_sunzilog_port *up = &sunzilog_port_table[i];
-
-		if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up))
-			continue;
-
-		uart_count++;
-	}
-		
-	sunzilog_reg.nr = uart_count;
-	sunzilog_reg.minor = sunserial_current_minor;
-
-	ret = uart_register_driver(&sunzilog_reg);
-	if (ret == 0) {
-		sunzilog_reg.tty_driver->name_base = sunzilog_reg.minor - 64;
-		sunzilog_reg.cons = SUNZILOG_CONSOLE();
-
-		sunserial_current_minor += uart_count;
-
-		for (i = 0; i < NUM_CHANNELS; i++) {
-			struct uart_sunzilog_port *up = &sunzilog_port_table[i];
-
-			if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up))
-				continue;
-
-			if (uart_add_one_port(&sunzilog_reg, &up->port)) {
-				printk(KERN_ERR
-				    "SunZilog: failed to add port zs%d\n", i);
-			}
-		}
-	}
+	if (of_find_property(dp, "keyboard", NULL))
+		ret = 1;
+	else
+		ret = 0;
 
 	return ret;
 }
 
-static void __init sunzilog_scan_count(struct zs_probe_scan *t, int node)
+static int __devinit zs_probe(struct of_device *dev, const struct of_device_id *match)
 {
-	t->devices++;
-}
+	static int zilog_irq = -1;
+	struct of_device *op = to_of_device(&dev->dev);
+	struct uart_sunzilog_port *up;
+	struct zilog_layout __iomem *rp;
+	int inst = zs_get_instance(dev->node);
+	int err;
 
-static int __init sunzilog_ports_count(void)
-{
-	struct zs_probe_scan scan;
+	sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0,
+					      sizeof(struct zilog_layout),
+					      "zs");
+	if (!sunzilog_chip_regs[inst])
+		return -ENOMEM;
 
-	/* Sun4 Zilog setup is hard coded, no probing to do.  */
-	if (sparc_cpu_model == sun4)
-		return 2;
+	rp = sunzilog_chip_regs[inst];
 
-	scan.scanner = sunzilog_scan_count;
-	scan.depth = 0;
-	scan.devices = 0;
+	if (zilog_irq == -1) {
+		zilog_irq = op->irqs[0];
+		err = request_irq(zilog_irq, sunzilog_interrupt, SA_SHIRQ,
+				  "zs", sunzilog_irq_chain);
+		if (err) {
+			of_iounmap(rp, sizeof(struct zilog_layout));
 
-	sunzilog_scan(&scan, prom_getchild(prom_root_node));
+			return err;
+		}
+	}
 
-	return scan.devices;
-}
+	up = &sunzilog_port_table[inst * 2];
 
-static int __init sunzilog_init(void)
-{
+	/* Channel A */
+	up[0].port.mapbase = op->resource[0].start + 0x00;
+	up[0].port.membase = (void __iomem *) &rp->channelA;
+	up[0].port.iotype = UPIO_MEM;
+	up[0].port.irq = op->irqs[0];
+	up[0].port.uartclk = ZS_CLOCK;
+	up[0].port.fifosize = 1;
+	up[0].port.ops = &sunzilog_pops;
+	up[0].port.type = PORT_SUNZILOG;
+	up[0].port.flags = 0;
+	up[0].port.line = (inst * 2) + 0;
+	up[0].port.dev = &op->dev;
+	up[0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
+	if (inst == 1)
+		up[0].flags |= SUNZILOG_FLAG_CONS_KEYB;
+	sunzilog_init_hw(&up[0]);
 
-	NUM_SUNZILOG = sunzilog_ports_count();
-	if (NUM_SUNZILOG == 0)
-		return -ENODEV;
+	/* Channel B */
+	up[1].port.mapbase = op->resource[0].start + 0x04;
+	up[1].port.membase = (void __iomem *) &rp->channelB;
+	up[1].port.iotype = UPIO_MEM;
+	up[1].port.irq = op->irqs[0];
+	up[1].port.uartclk = ZS_CLOCK;
+	up[1].port.fifosize = 1;
+	up[1].port.ops = &sunzilog_pops;
+	up[1].port.type = PORT_SUNZILOG;
+	up[1].port.flags = 0;
+	up[1].port.line = (inst * 2) + 1;
+	up[1].port.dev = &op->dev;
+	up[1].flags |= 0;
+	if (inst == 1)
+		up[1].flags |= SUNZILOG_FLAG_CONS_MOUSE;
+	sunzilog_init_hw(&up[1]);
 
-	sunzilog_alloc_tables();
-
-	sunzilog_ports_init();
+	if (inst != 1) {
+		err = uart_add_one_port(&sunzilog_reg, &up[0].port);
+		if (err) {
+			of_iounmap(rp, sizeof(struct zilog_layout));
+			return err;
+		}
+		err = uart_add_one_port(&sunzilog_reg, &up[1].port);
+		if (err) {
+			uart_remove_one_port(&sunzilog_reg, &up[0].port);
+			of_iounmap(rp, sizeof(struct zilog_layout));
+			return err;
+		}
+	}
 
 	return 0;
 }
 
-static void __exit sunzilog_exit(void)
+static int __devexit zs_remove(struct of_device *dev)
 {
-	int i;
+	struct uart_sunzilog_port *up = dev_get_drvdata(&dev->dev);
+	struct zilog_channel __iomem *channel;
 
-	for (i = 0; i < NUM_CHANNELS; i++) {
-		struct uart_sunzilog_port *up = &sunzilog_port_table[i];
-
-		if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) {
+	if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) {
 #ifdef CONFIG_SERIO
-			if (up->serio) {
-				serio_unregister_port(up->serio);
-				up->serio = NULL;
-			}
+		serio_unregister_port(&up->serio);
 #endif
-		} else
-			uart_remove_one_port(&sunzilog_reg, &up->port);
+	} else
+		uart_remove_one_port(&sunzilog_reg, &up->port);
+
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+	of_iounmap(channel, sizeof(struct zilog_channel));
+
+	return 0;
+}
+
+static struct of_device_id zs_match[] = {
+	{
+		.name = "zs",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, zs_match);
+
+static struct of_platform_driver zs_driver = {
+	.name		= "zs",
+	.match_table	= zs_match,
+	.probe		= zs_probe,
+	.remove		= __devexit_p(zs_remove),
+};
+
+static int __init sunzilog_init(void)
+{
+	struct device_node *dp;
+	int err;
+
+	NUM_SUNZILOG = 0;
+	for_each_node_by_name(dp, "zs")
+		NUM_SUNZILOG++;
+
+	if (NUM_SUNZILOG) {
+		int uart_count;
+
+		err = sunzilog_alloc_tables();
+		if (err)
+			return err;
+
+		/* Subtract 1 for keyboard, 1 for mouse.  */
+		uart_count = (NUM_SUNZILOG * 2) - 2;
+
+		sunzilog_reg.nr = uart_count;
+		sunzilog_reg.minor = sunserial_current_minor;
+		err = uart_register_driver(&sunzilog_reg);
+		if (err) {
+			sunzilog_free_tables();
+			return err;
+		}
+		sunzilog_reg.tty_driver->name_base = sunzilog_reg.minor - 64;
+		sunzilog_reg.cons = SUNZILOG_CONSOLE();
+
+		sunserial_current_minor += uart_count;
 	}
 
-	uart_unregister_driver(&sunzilog_reg);
+	return of_register_driver(&zs_driver, &of_bus_type);
+}
+
+static void __exit sunzilog_exit(void)
+{
+	of_unregister_driver(&zs_driver);
+
+	if (NUM_SUNZILOG) {
+		uart_unregister_driver(&sunzilog_reg);
+		sunzilog_free_tables();
+	}
 }
 
 module_init(sunzilog_init);