diff options
Diffstat (limited to 'drivers/serial/sunzilog.c')
-rw-r--r-- | drivers/serial/sunzilog.c | 795 |
1 files changed, 270 insertions, 525 deletions
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index cd49ebbf4a4..a1456d9352c 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,10 +9,9 @@ * 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> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -38,10 +36,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 +61,7 @@ #define ZSDELAY() #define ZSDELAY_LONG() #define ZS_WSYNC(__channel) \ - sbus_readb(&((__channel)->control)) + readb(&((__channel)->control)) #endif static int num_sunzilog; @@ -107,7 +103,7 @@ struct uart_sunzilog_port { unsigned char prev_status; #ifdef CONFIG_SERIO - struct serio *serio; + struct serio serio; int serio_open; #endif }; @@ -138,9 +134,9 @@ static unsigned char read_zsreg(struct zilog_channel __iomem *channel, { 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 +145,9 @@ static unsigned char read_zsreg(struct zilog_channel __iomem *channel, 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 +158,17 @@ static void sunzilog_clear_fifo(struct zilog_channel __iomem *channel) 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 +190,7 @@ static void __load_zsregs(struct zilog_channel __iomem *channel, unsigned char * udelay(100); } - sbus_writeb(ERR_RES, &channel->control); + writeb(ERR_RES, &channel->control); ZSDELAY(); ZS_WSYNC(channel); @@ -291,7 +287,7 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, /* 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 +302,7 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, 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 +326,12 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, 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 +343,7 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, if (!(ch & Rx_CH_AV)) break; - ch = sbus_readb(&channel->data); + ch = readb(&channel->data); ZSDELAY(); ch &= up->parity_mask; @@ -406,10 +402,10 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, { 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 +417,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, * confusing the PROM. */ while (1) { - status = sbus_readb(&channel->control); + status = readb(&channel->control); ZSDELAY(); if (!(status & BRK_ABRT)) break; @@ -458,7 +454,7 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, 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 +483,7 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, 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 +502,7 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, 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 +515,7 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, return; ack_tx_int: - sbus_writeb(RES_Tx_P, &channel->control); + writeb(RES_Tx_P, &channel->control); ZSDELAY(); ZS_WSYNC(channel); } @@ -540,7 +536,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *reg /* 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 +559,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *reg 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 +590,7 @@ static __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *p unsigned char status; channel = ZILOG_CHANNEL_FROM_PORT(port); - status = sbus_readb(&channel->control); + status = readb(&channel->control); ZSDELAY(); return status; @@ -682,7 +678,7 @@ static void sunzilog_start_tx(struct uart_port *port) 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 +689,7 @@ static void sunzilog_start_tx(struct uart_port *port) * 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 +698,7 @@ static void sunzilog_start_tx(struct uart_port *port) } 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 +775,7 @@ static void __sunzilog_startup(struct uart_sunzilog_port *up) 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 +959,7 @@ sunzilog_set_termios(struct uart_port *port, struct termios *termios, 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,242 +1008,55 @@ static struct uart_sunzilog_port *sunzilog_port_table; 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, .driver_name = "ttyS", - .devfs_name = "tts/", .dev_name = "ttyS", .major = TTY_MAJOR, }; -static void * __init alloc_one_table(unsigned long size) -{ - void *ret; - - ret = kmalloc(size, GFP_KERNEL); - if (ret != NULL) - memset(ret, 0, size); - - return ret; -} - -static void __init sunzilog_alloc_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) +static int __init sunzilog_alloc_tables(void) { - 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&¢ral == 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(12, 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(); - } + struct uart_sunzilog_port *up; + unsigned long size; + int i; - 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"); + size = NUM_CHANNELS * sizeof(struct uart_sunzilog_port); + sunzilog_port_table = kzalloc(size, GFP_KERNEL); + if (!sunzilog_port_table) + return -ENOMEM; - } else { - zsnode = node; + for (i = 0; i < NUM_CHANNELS; i++) { + up = &sunzilog_port_table[i]; -#if 0 /* XXX When was this used? */ - if (prom_getintdefault(zsnode, "slave", -1) != chipid) { - zsnode = prom_getsibling(zsnode); - continue; - } -#endif + spin_lock_init(&up->port.lock); - /* - * "address" is only present on ports that OBP opened - * (from Mitch Bradley's "Hitchhiker's Guide to OBP"). - * We do not use it. - */ + if (i == 0) + sunzilog_irq_chain = up; - 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 (i < NUM_CHANNELS - 1) + up->next = up + 1; + else + up->next = NULL; } - 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(); + 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 (struct zilog_layout __iomem *) mapped_addr; + return 0; } -#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) +static void sunzilog_free_tables(void) { - 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 */ @@ -1261,7 +1070,7 @@ static void sunzilog_putchar(struct uart_port *port, int ch) * 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; @@ -1269,7 +1078,7 @@ static void sunzilog_putchar(struct uart_port *port, int ch) udelay(5); } while (--loops); - sbus_writeb(ch, &channel->data); + writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); } @@ -1386,28 +1195,6 @@ static struct console sunzilog_console = { .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; @@ -1432,101 +1219,8 @@ static inline struct console *SUNZILOG_CONSOLE(void) #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; @@ -1540,8 +1234,6 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe up->cflag = B4800 | CS8 | CLOCAL | CREAD; baud = 4800; } - printk(KERN_INFO "zs%d at 0x%p (irq = %s) is a SunZilog\n", - channel, up->port.membase, __irq_itoa(zilog_irq)); up->curregs[R15] = BRKIE; brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); @@ -1553,216 +1245,268 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe #ifdef CONFIG_SERIO static void __init sunzilog_register_serio(struct uart_sunzilog_port *up, int channel) { - struct serio *serio; - - up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); - if (serio) { - memset(serio, 0, sizeof(*serio)); - - 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)); + struct serio *serio = &up->serio; - serio->write = sunzilog_serio_write; - serio->open = sunzilog_serio_open; - serio->close = sunzilog_serio_close; + serio->port_data = up; - 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; - - 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; + struct zilog_channel __iomem *channel; + unsigned long flags; + int baud, brg; - spin_lock_irqsave(&up->port.lock, flags); + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - if (ZS_IS_CHANNEL_A(up)) { - write_zsreg(channel, R9, FHWRES); - ZSDELAY_LONG(); - (void) read_zsreg(channel, R0); - } + 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 (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]); - } + 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]); + } - 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) -{ - sunzilog_chip_regs[t->devices] = get_zs(t->devices, node); - t->devices++; } -static int __init sunzilog_ports_init(void) +static int __devinit zs_get_instance(struct device_node *dp) { - struct zs_probe_scan scan; int ret; - int uart_count; - int i; - - printk(KERN_DEBUG "SunZilog: %d chips.\n", NUM_SUNZILOG); - - 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(); - } + ret = of_getintprop_default(dp, "slave", -1); + if (ret != -1) + return ret; - sunzilog_init_hw(); + if (of_find_property(dp, "keyboard", NULL)) + ret = 1; + else + ret = 0; - /* 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]; + return ret; +} - if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) - continue; +static int zilog_irq = -1; - uart_count++; - } - - sunzilog_reg.nr = uart_count; - sunzilog_reg.minor = sunserial_current_minor; +static int __devinit zs_probe(struct of_device *dev, const struct of_device_id *match) +{ + 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; - ret = uart_register_driver(&sunzilog_reg); - if (ret == 0) { - sunzilog_reg.tty_driver->name_base = sunzilog_reg.minor - 64; - sunzilog_reg.cons = SUNZILOG_CONSOLE(); + sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0, + sizeof(struct zilog_layout), + "zs"); + if (!sunzilog_chip_regs[inst]) + return -ENOMEM; - sunserial_current_minor += uart_count; + rp = sunzilog_chip_regs[inst]; - for (i = 0; i < NUM_CHANNELS; i++) { - struct uart_sunzilog_port *up = &sunzilog_port_table[i]; + if (zilog_irq == -1) { + zilog_irq = op->irqs[0]; + err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, + "zs", sunzilog_irq_chain); + if (err) { + of_iounmap(rp, sizeof(struct zilog_layout)); - if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) - continue; + return err; + } + } - if (uart_add_one_port(&sunzilog_reg, &up->port)) { - printk(KERN_ERR - "SunZilog: failed to add port zs%d\n", i); - } + up = &sunzilog_port_table[inst * 2]; + + /* 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]); + + /* 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]); + + 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 ret; + dev_set_drvdata(&dev->dev, &up[0]); + + return 0; } -static void __init sunzilog_scan_count(struct zs_probe_scan *t, int node) +static void __devexit zs_remove_one(struct uart_sunzilog_port *up) { - t->devices++; + if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { +#ifdef CONFIG_SERIO + serio_unregister_port(&up->serio); +#endif + } else + uart_remove_one_port(&sunzilog_reg, &up->port); } -static int __init sunzilog_ports_count(void) +static int __devexit zs_remove(struct of_device *dev) { - struct zs_probe_scan scan; + struct uart_sunzilog_port *up = dev_get_drvdata(&dev->dev); + struct zilog_layout __iomem *regs; - /* Sun4 Zilog setup is hard coded, no probing to do. */ - if (sparc_cpu_model == sun4) - return 2; + zs_remove_one(&up[0]); + zs_remove_one(&up[1]); - scan.scanner = sunzilog_scan_count; - scan.depth = 0; - scan.devices = 0; + regs = sunzilog_chip_regs[up[0].port.line / 2]; + of_iounmap(regs, sizeof(struct zilog_layout)); - sunzilog_scan(&scan, prom_getchild(prom_root_node)); + dev_set_drvdata(&dev->dev, NULL); - return scan.devices; + 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 = sunzilog_ports_count(); - if (NUM_SUNZILOG == 0) - return -ENODEV; + NUM_SUNZILOG = 0; + for_each_node_by_name(dp, "zs") + NUM_SUNZILOG++; - sunzilog_alloc_tables(); + if (NUM_SUNZILOG) { + int uart_count; - sunzilog_ports_init(); + err = sunzilog_alloc_tables(); + if (err) + return err; - return 0; + /* 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; + } + + return of_register_driver(&zs_driver, &of_bus_type); } static void __exit sunzilog_exit(void) { - int i; + of_unregister_driver(&zs_driver); - 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)) { -#ifdef CONFIG_SERIO - if (up->serio) { - serio_unregister_port(up->serio); - up->serio = NULL; - } -#endif - } else - uart_remove_one_port(&sunzilog_reg, &up->port); + if (zilog_irq != -1) { + free_irq(zilog_irq, sunzilog_irq_chain); + zilog_irq = -1; } - uart_unregister_driver(&sunzilog_reg); + if (NUM_SUNZILOG) { + uart_unregister_driver(&sunzilog_reg); + sunzilog_free_tables(); + } } module_init(sunzilog_init); @@ -1770,4 +1514,5 @@ module_exit(sunzilog_exit); MODULE_AUTHOR("David S. Miller"); MODULE_DESCRIPTION("Sun Zilog serial port driver"); +MODULE_VERSION("2.0"); MODULE_LICENSE("GPL"); |