X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fserial%2Fsunsu.c;h=d3a5aeee73a34b33bb4736b751a2aacab8ec5e8a;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=2446dd664adf94003d4ca78e3f1a3d4c8d89c2c2;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 2446dd664..d3a5aeee7 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -12,10 +12,9 @@ * Theodore Ts'o , 2001-Oct-12 * * Converted to new 2.5.x UART layer. - * David S. Miller (davem@redhat.com), 2002-Jul-29 + * David S. Miller (davem@davemloft.net), 2002-Jul-29 */ -#include #include #include #include @@ -40,11 +39,8 @@ #include #include -#include -#include -#ifdef CONFIG_SPARC64 -#include -#endif +#include +#include #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -94,27 +90,24 @@ struct uart_sunsu_port { /* Probing information. */ enum su_type su_type; unsigned int type_probed; /* XXX Stupid */ - int port_node; - unsigned int irq; + unsigned long reg_size; #ifdef CONFIG_SERIO - struct serio *serio; + struct serio serio; int serio_open; #endif }; -#define _INLINE_ - -static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset) +static unsigned int serial_in(struct uart_sunsu_port *up, int offset) { offset <<= up->port.regshift; switch (up->port.iotype) { - case SERIAL_IO_HUB6: + case UPIO_HUB6: outb(up->port.hub6 - 1 + offset, up->port.iobase); return inb(up->port.iobase + 1); - case SERIAL_IO_MEM: + case UPIO_MEM: return readb(up->port.membase + offset); default: @@ -122,8 +115,7 @@ static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset) } } -static _INLINE_ void -serial_out(struct uart_sunsu_port *up, int offset, int value) +static void serial_out(struct uart_sunsu_port *up, int offset, int value) { #ifndef CONFIG_SPARC64 /* @@ -140,12 +132,12 @@ serial_out(struct uart_sunsu_port *up, int offset, int value) offset <<= up->port.regshift; switch (up->port.iotype) { - case SERIAL_IO_HUB6: + case UPIO_HUB6: outb(up->port.hub6 - 1 + offset, up->port.iobase); outb(value, up->port.iobase + 1); break; - case SERIAL_IO_MEM: + case UPIO_MEM: writeb(value, up->port.membase + offset); break; @@ -256,21 +248,30 @@ static void disable_rsa(struct uart_sunsu_port *up) } #endif /* CONFIG_SERIAL_8250_RSA */ -static void sunsu_stop_tx(struct uart_port *port, unsigned int tty_stop) +static inline void __stop_tx(struct uart_sunsu_port *p) +{ + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } +} + +static void sunsu_stop_tx(struct uart_port *port) { struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } - if (up->port.type == PORT_16C950 && tty_stop) { + __stop_tx(up); + + /* + * We really want to stop the transmitter from sending. + */ + if (up->port.type == PORT_16C950) { up->acr |= UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr); } } -static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start) +static void sunsu_start_tx(struct uart_port *port) { struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; @@ -278,10 +279,11 @@ static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start) up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); } + /* - * We only do this from uart_start + * Re-enable the transmitter if we disabled it. */ - if (tty_start && up->port.type == PORT_16C950) { + if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { up->acr &= ~UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr); } @@ -290,13 +292,10 @@ static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start) static void sunsu_stop_rx(struct uart_port *port) { struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&up->port.lock, flags); } static void sunsu_enable_ms(struct uart_port *port) @@ -310,23 +309,17 @@ static void sunsu_enable_ms(struct uart_port *port) spin_unlock_irqrestore(&up->port.lock, flags); } -static _INLINE_ struct tty_struct * +static struct tty_struct * receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs *regs) { struct tty_struct *tty = up->port.info->tty; - unsigned char ch; + unsigned char ch, flag; int max_count = 256; int saw_console_brk = 0; do { - if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { - tty->flip.work.func((void *)tty); - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - return tty; // if TTY_DONT_FLIP is set - } ch = serial_inp(up, UART_RX); - *tty->flip.char_buf_ptr = ch; - *tty->flip.flag_buf_ptr = TTY_NORMAL; + flag = TTY_NORMAL; up->port.icount.rx++; if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | @@ -368,31 +361,23 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs } if (*status & UART_LSR_BI) { - *tty->flip.flag_buf_ptr = TTY_BREAK; + flag = TTY_BREAK; } else if (*status & UART_LSR_PE) - *tty->flip.flag_buf_ptr = TTY_PARITY; + flag = TTY_PARITY; else if (*status & UART_LSR_FE) - *tty->flip.flag_buf_ptr = TTY_FRAME; + flag = TTY_FRAME; } if (uart_handle_sysrq_char(&up->port, ch, regs)) goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) { - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; - } - if ((*status & UART_LSR_OE) && - tty->flip.count < TTY_FLIPBUF_SIZE) { + if ((*status & up->port.ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + if (*status & UART_LSR_OE) /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character. */ - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; - } + tty_insert_flip_char(tty, 0, TTY_OVERRUN); ignore_char: *status = serial_inp(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); @@ -403,7 +388,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs return tty; } -static _INLINE_ void transmit_chars(struct uart_sunsu_port *up) +static void transmit_chars(struct uart_sunsu_port *up) { struct circ_buf *xmit = &up->port.info->xmit; int count; @@ -414,8 +399,12 @@ static _INLINE_ void transmit_chars(struct uart_sunsu_port *up) up->port.x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - sunsu_stop_tx(&up->port, 0); + if (uart_tx_stopped(&up->port)) { + sunsu_stop_tx(&up->port); + return; + } + if (uart_circ_empty(xmit)) { + __stop_tx(up); return; } @@ -432,10 +421,10 @@ static _INLINE_ void transmit_chars(struct uart_sunsu_port *up) uart_write_wakeup(&up->port); if (uart_circ_empty(xmit)) - sunsu_stop_tx(&up->port, 0); + __stop_tx(up); } -static _INLINE_ void check_modem_status(struct uart_sunsu_port *up) +static void check_modem_status(struct uart_sunsu_port *up) { int status; @@ -505,11 +494,7 @@ static void sunsu_change_mouse_baud(struct uart_sunsu_port *up) quot = up->port.uartclk / (16 * new_baud); - spin_unlock(&up->port.lock); - sunsu_change_speed(&up->port, up->cflag, 0, quot); - - spin_lock(&up->port.lock); } static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *regs, int is_break) @@ -520,7 +505,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg /* Stop-A is handled by drivers/char/keyboard.c now. */ if (up->su_type == SU_PORT_KBD) { #ifdef CONFIG_SERIO - serio_interrupt(up->serio, ch, 0, regs); + serio_interrupt(&up->serio, ch, 0, regs); #endif } else if (up->su_type == SU_PORT_MS) { int ret = suncore_mouse_baud_detection(ch, is_break); @@ -534,7 +519,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg case 0: #ifdef CONFIG_SERIO - serio_interrupt(up->serio, ch, 0, regs); + serio_interrupt(&up->serio, ch, 0, regs); #endif break; }; @@ -573,13 +558,10 @@ static unsigned int sunsu_tx_empty(struct uart_port *port) static unsigned int sunsu_get_mctrl(struct uart_port *port) { struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; unsigned char status; unsigned int ret; - spin_lock_irqsave(&up->port.lock, flags); status = serial_in(up, UART_MSR); - spin_unlock_irqrestore(&up->port.lock, flags); ret = 0; if (status & UART_MSR_DCD) @@ -655,7 +637,7 @@ static int sunsu_startup(struct uart_port *port) /* * Clear the FIFO buffers and disable them. - * (they will be reeanbled in set_termios()) + * (they will be reenabled in set_termios()) */ if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); @@ -677,21 +659,21 @@ static int sunsu_startup(struct uart_port *port) * if it is, then bail out, because there's likely no UART * here. */ - if (!(up->port.flags & ASYNC_BUGGY_UART) && + if (!(up->port.flags & UPF_BUGGY_UART) && (serial_inp(up, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!\n", up->port.line); return -ENODEV; } if (up->su_type != SU_PORT_PORT) { - retval = request_irq(up->irq, sunsu_kbd_ms_interrupt, - SA_SHIRQ, su_typev[up->su_type], up); + retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt, + IRQF_SHARED, su_typev[up->su_type], up); } else { - retval = request_irq(up->irq, sunsu_serial_interrupt, - SA_SHIRQ, su_typev[up->su_type], up); + retval = request_irq(up->port.irq, sunsu_serial_interrupt, + IRQF_SHARED, su_typev[up->su_type], up); } if (retval) { - printk("su: Cannot register IRQ %d\n", up->irq); + printk("su: Cannot register IRQ %d\n", up->port.irq); return retval; } @@ -715,7 +697,7 @@ static int sunsu_startup(struct uart_port *port) up->ier = UART_IER_RLSI | UART_IER_RDI; serial_outp(up, UART_IER, up->ier); - if (up->port.flags & ASYNC_FOURPORT) { + if (up->port.flags & UPF_FOURPORT) { unsigned int icp; /* * Enable interrupts on the AST Fourport board @@ -748,7 +730,7 @@ static void sunsu_shutdown(struct uart_port *port) serial_outp(up, UART_IER, 0); spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & ASYNC_FOURPORT) { + if (up->port.flags & UPF_FOURPORT) { /* reset interrupts on the AST Fourport board */ inb((up->port.iobase & 0xfe0) | 0x1f); up->port.mctrl |= TIOCM_OUT1; @@ -779,7 +761,7 @@ static void sunsu_shutdown(struct uart_port *port) */ (void) serial_in(up, UART_RX); - free_irq(up->irq, up); + free_irq(up->port.irq, up); } static void @@ -990,7 +972,7 @@ static struct uart_sunsu_port sunsu_ports[UART_NR]; #ifdef CONFIG_SERIO -static spinlock_t sunsu_serio_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(sunsu_serio_lock); static int sunsu_serio_write(struct serio *serio, unsigned char ch) { @@ -1045,102 +1027,17 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) { unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; - struct linux_ebus_device *dev = NULL; - struct linux_ebus *ebus; -#ifdef CONFIG_SPARC64 - struct sparc_isa_bridge *isa_br; - struct sparc_isa_device *isa_dev; -#endif -#ifndef CONFIG_SPARC64 - struct linux_prom_registers reg0; -#endif unsigned long flags; - if (!up->port_node || !up->su_type) + if (up->su_type == SU_PORT_NONE) return; up->type_probed = PORT_UNKNOWN; - up->port.iotype = SERIAL_IO_MEM; - - /* - * First we look for Ebus-bases su's - */ - for_each_ebus(ebus) { - for_each_ebusdev(dev, ebus) { - if (dev->prom_node == up->port_node) { - /* - * The EBus is broken on sparc; it delivers - * virtual addresses in resources. Oh well... - * This is correct on sparc64, though. - */ - up->port.membase = (char *) dev->resource[0].start; - /* - * This is correct on both architectures. - */ - up->port.mapbase = dev->resource[0].start; - up->irq = dev->irqs[0]; - goto ebus_done; - } - } - } - -#ifdef CONFIG_SPARC64 - for_each_isa(isa_br) { - for_each_isadev(isa_dev, isa_br) { - if (isa_dev->prom_node == up->port_node) { - /* Same on sparc64. Cool architecure... */ - up->port.membase = (char *) isa_dev->resource.start; - up->port.mapbase = isa_dev->resource.start; - up->irq = isa_dev->irq; - goto ebus_done; - } - } - } -#endif - -#ifdef CONFIG_SPARC64 - /* - * Not on Ebus, bailing. - */ - return; -#else - /* - * Not on Ebus, must be OBIO. - */ - if (prom_getproperty(up->port_node, "reg", - (char *)®0, sizeof(reg0)) == -1) { - prom_printf("sunsu: no \"reg\" property\n"); - return; - } - prom_apply_obio_ranges(®0, 1); - if (reg0.which_io != 0) { /* Just in case... */ - prom_printf("sunsu: bus number nonzero: 0x%x:%x\n", - reg0.which_io, reg0.phys_addr); - return; - } - up->port.mapbase = reg0.phys_addr; - if ((up->port.membase = ioremap(reg0.phys_addr, reg0.reg_size)) == 0) { - prom_printf("sunsu: Cannot map registers.\n"); - return; - } - - /* - * 0x20 is sun4m thing, Dave Redman heritage. - * See arch/sparc/kernel/irq.c. - */ -#define IRQ_4M(n) ((n)|0x20) - - /* - * There is no intr property on MrCoffee, so hardwire it. - */ - up->irq = IRQ_4M(13); -#endif - -ebus_done: + up->port.iotype = UPIO_MEM; spin_lock_irqsave(&up->port.lock, flags); - if (!(up->port.flags & ASYNC_BUGGY_UART)) { + if (!(up->port.flags & UPF_BUGGY_UART)) { /* * Do a simple existence test first; if we fail this, there's * no point trying anything else. @@ -1178,7 +1075,7 @@ ebus_done: * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope! */ - if (!(up->port.flags & ASYNC_SKIP_TEST)) { + if (!(up->port.flags & UPF_SKIP_TEST)) { serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(up, UART_MSR) & 0xF0; serial_outp(up, UART_MCR, save_mcr); @@ -1279,62 +1176,62 @@ out: static struct uart_driver sunsu_reg = { .owner = THIS_MODULE, .driver_name = "serial", - .devfs_name = "tts/", .dev_name = "ttyS", .major = TTY_MAJOR, }; -static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) +static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up) { + int quot, baud; +#ifdef CONFIG_SERIO struct serio *serio; +#endif - up->port.line = channel; - up->port.type = PORT_UNKNOWN; - up->port.uartclk = (SU_BASE_BAUD * 16); - - if (up->su_type == SU_PORT_KBD) + if (up->su_type == SU_PORT_KBD) { up->cflag = B1200 | CS8 | CLOCAL | CREAD; - else + baud = 1200; + } else { up->cflag = B4800 | CS8 | CLOCAL | CREAD; + baud = 4800; + } + quot = up->port.uartclk / (16 * baud); sunsu_autoconfig(up); if (up->port.type == PORT_UNKNOWN) - return -1; + return -ENODEV; - printk(KERN_INFO "su%d at 0x%p (irq = %s) is a %s\n", - channel, - up->port.membase, __irq_itoa(up->irq), - sunsu_type(&up->port)); + printk("%s: %s port at %lx, irq %u\n", + to_of_device(up->port.dev)->node->full_name, + (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse", + up->port.mapbase, up->port.irq); #ifdef CONFIG_SERIO - up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); - if (serio) { - memset(serio, 0, sizeof(*serio)); - - serio->port_data = up; - - serio->type = SERIO_RS232; - if (up->su_type == SU_PORT_KBD) { - serio->type |= SERIO_SUNKBD; - strlcpy(serio->name, "sukbd", sizeof(serio->name)); - } else { - serio->type |= (SERIO_SUN | (1 << 16)); - strlcpy(serio->name, "sums", sizeof(serio->name)); - } - strlcpy(serio->phys, (channel == 0 ? "su/serio0" : "su/serio1"), - sizeof(serio->phys)); + serio = &up->serio; + serio->port_data = up; - serio->write = sunsu_serio_write; - serio->open = sunsu_serio_open; - serio->close = sunsu_serio_close; - - serio_register_port(serio); + serio->id.type = SERIO_RS232; + if (up->su_type == SU_PORT_KBD) { + serio->id.proto = SERIO_SUNKBD; + strlcpy(serio->name, "sukbd", sizeof(serio->name)); } else { - printk(KERN_WARNING "su%d: not enough memory for serio port\n", - channel); + serio->id.proto = SERIO_SUN; + serio->id.extra = 1; + strlcpy(serio->name, "sums", sizeof(serio->name)); } + strlcpy(serio->phys, + (!(up->port.line & 1) ? "su/serio0" : "su/serio1"), + sizeof(serio->phys)); + + serio->write = sunsu_serio_write; + serio->open = sunsu_serio_open; + serio->close = sunsu_serio_close; + serio->dev.parent = up->port.dev; + + serio_register_port(serio); #endif + sunsu_change_speed(&up->port, up->cflag, 0, quot); + sunsu_startup(&up->port); return 0; } @@ -1369,7 +1266,7 @@ static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) } while ((status & BOTH_EMPTY) != BOTH_EMPTY); /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & ASYNC_CONS_FLOW) { + if (up->port.flags & UPF_CONS_FLOW) { tmout = 1000000; while (--tmout && ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) @@ -1377,6 +1274,14 @@ static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) } } +static void sunsu_console_putchar(struct uart_port *port, int ch) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + /* * Print a string to the serial port trying not to disturb * any possible real use of the port... @@ -1386,7 +1291,6 @@ static void sunsu_console_write(struct console *co, const char *s, { struct uart_sunsu_port *up = &sunsu_ports[co->index]; unsigned int ier; - int i; /* * First save the UER then disable the interrupts @@ -1394,22 +1298,7 @@ static void sunsu_console_write(struct console *co, const char *s, ier = serial_in(up, UART_IER); serial_out(up, UART_IER, 0); - /* - * Now, do each character - */ - for (i = 0; i < count; i++, s++) { - wait_for_xmitr(up); - - /* - * Send the character out. - * If a LF, also do CR... - */ - serial_out(up, UART_TX, *s); - if (*s == 10) { - wait_for_xmitr(up); - serial_out(up, UART_TX, 13); - } - } + uart_console_write(&up->port, s, count, sunsu_console_putchar); /* * Finally, wait for transmitter to become empty @@ -1425,7 +1314,7 @@ static void sunsu_console_write(struct console *co, const char *s, * - initialize the serial port * Return non-zero if we didn't find a serial port. */ -static int __init sunsu_console_setup(struct console *co, char *options) +static int sunsu_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 9600; @@ -1465,276 +1354,229 @@ static struct console sunsu_cons = { .index = -1, .data = &sunsu_reg, }; -#define SUNSU_CONSOLE (&sunsu_cons) /* * Register console. */ -static int __init sunsu_serial_console_init(void) +static inline struct console *SUNSU_CONSOLE(int num_uart) { int i; if (con_is_present()) - return 0; + return NULL; - for (i = 0; i < UART_NR; i++) { + for (i = 0; i < num_uart; i++) { int this_minor = sunsu_reg.minor + i; if ((this_minor - 64) == (serial_console - 1)) break; } - if (i == UART_NR) - return 0; - if (sunsu_ports[i].port_node == 0) - return 0; + if (i == num_uart) + return NULL; sunsu_cons.index = i; - register_console(&sunsu_cons); - return 0; + + return &sunsu_cons; } #else -#define SUNSU_CONSOLE (NULL) +#define SUNSU_CONSOLE(num_uart) (NULL) #define sunsu_serial_console_init() do { } while (0) #endif -static int __init sunsu_serial_init(void) +static enum su_type __devinit su_get_type(struct device_node *dp) { - int instance, ret, i; - - /* How many instances do we need? */ - instance = 0; - for (i = 0; i < UART_NR; i++) { - struct uart_sunsu_port *up = &sunsu_ports[i]; + struct device_node *ap = of_find_node_by_path("/aliases"); - if (up->su_type == SU_PORT_MS || - up->su_type == SU_PORT_KBD) - continue; + if (ap) { + char *keyb = of_get_property(ap, "keyboard", NULL); + char *ms = of_get_property(ap, "mouse", NULL); - up->port.flags |= ASYNC_BOOT_AUTOCONF; - up->port.type = PORT_UNKNOWN; - up->port.uartclk = (SU_BASE_BAUD * 16); + if (keyb) { + if (dp == of_find_node_by_path(keyb)) + return SU_PORT_KBD; + } + if (ms) { + if (dp == of_find_node_by_path(ms)) + return SU_PORT_MS; + } + } - sunsu_autoconfig(up); - if (up->port.type == PORT_UNKNOWN) - continue; + return SU_PORT_PORT; +} - up->port.line = instance++; - up->port.ops = &sunsu_pops; +static int __devinit su_probe(struct of_device *op, const struct of_device_id *match) +{ + static int inst; + struct device_node *dp = op->node; + struct uart_sunsu_port *up; + struct resource *rp; + enum su_type type; + int err; + + type = su_get_type(dp); + if (type == SU_PORT_PORT) { + if (inst >= UART_NR) + return -EINVAL; + up = &sunsu_ports[inst]; + } else { + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (!up) + return -ENOMEM; } - sunsu_reg.minor = sunserial_current_minor; - sunserial_current_minor += instance; + up->port.line = inst; - sunsu_reg.nr = instance; - sunsu_reg.cons = SUNSU_CONSOLE; + spin_lock_init(&up->port.lock); - ret = uart_register_driver(&sunsu_reg); - if (ret < 0) - return ret; + up->su_type = type; - for (i = 0; i < UART_NR; i++) { - struct uart_sunsu_port *up = &sunsu_ports[i]; + rp = &op->resource[0]; + up->port.mapbase = rp->start; + up->reg_size = (rp->end - rp->start) + 1; + up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); + if (!up->port.membase) { + if (type != SU_PORT_PORT) + kfree(up); + return -ENOMEM; + } - /* Do not register Keyboard/Mouse lines with UART - * layer. - */ - if (up->su_type == SU_PORT_MS || - up->su_type == SU_PORT_KBD) - continue; + up->port.irq = op->irqs[0]; - if (up->port.type == PORT_UNKNOWN) - continue; + up->port.dev = &op->dev; - uart_add_one_port(&sunsu_reg, &up->port); - } - - return 0; -} + up->port.type = PORT_UNKNOWN; + up->port.uartclk = (SU_BASE_BAUD * 16); -static int su_node_ok(int node, char *name, int namelen) -{ - if (strncmp(name, "su", namelen) == 0 || - strncmp(name, "su_pnp", namelen) == 0) - return 1; - - if (strncmp(name, "serial", namelen) == 0) { - char compat[32]; - int clen; - - /* Is it _really_ a 'su' device? */ - clen = prom_getproperty(node, "compatible", compat, sizeof(compat)); - if (clen > 0) { - if (strncmp(compat, "sab82532", 8) == 0) { - /* Nope, Siemens serial, not for us. */ - return 0; - } + err = 0; + if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) { + err = sunsu_kbd_ms_init(up); + if (err) { + kfree(up); + goto out_unmap; } - return 1; + dev_set_drvdata(&op->dev, up); + + return 0; } - return 0; -} + up->port.flags |= UPF_BOOT_AUTOCONF; -#define SU_PROPSIZE 128 + sunsu_autoconfig(up); -/* - * Scan status structure. - * "prop" is a local variable but it eats stack to keep it in each - * stack frame of a recursive procedure. - */ -struct su_probe_scan { - int msnode, kbnode; /* PROM nodes for mouse and keyboard */ - int msx, kbx; /* minors for mouse and keyboard */ - int devices; /* scan index */ - char prop[SU_PROPSIZE]; -}; + err = -ENODEV; + if (up->port.type == PORT_UNKNOWN) + goto out_unmap; -/* - * We have several platforms which present 'su' in different parts - * of the device tree. 'su' may be found under obio, ebus, isa and pci. - * We walk over the tree and find them wherever PROM hides them. - */ -static void __init su_probe_any(struct su_probe_scan *t, int sunode) -{ - struct uart_sunsu_port *up; - int len; + up->port.ops = &sunsu_pops; - if (t->devices >= UART_NR) - return; + err = uart_add_one_port(&sunsu_reg, &up->port); + if (err) + goto out_unmap; - for (; sunode != 0; sunode = prom_getsibling(sunode)) { - len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE); - if (len <= 1) - continue; /* Broken PROM node */ - - if (su_node_ok(sunode, t->prop, len)) { - up = &sunsu_ports[t->devices]; - if (t->kbnode != 0 && sunode == t->kbnode) { - t->kbx = t->devices; - up->su_type = SU_PORT_KBD; - } else if (t->msnode != 0 && sunode == t->msnode) { - t->msx = t->devices; - up->su_type = SU_PORT_MS; - } else { -#ifdef CONFIG_SPARC64 - /* - * Do not attempt to use the truncated - * keyboard/mouse ports as serial ports - * on Ultras with PC keyboard attached. - */ - if (prom_getbool(sunode, "mouse")) - continue; - if (prom_getbool(sunode, "keyboard")) - continue; -#endif - up->su_type = SU_PORT_PORT; - } - up->port_node = sunode; - ++t->devices; - } else { - su_probe_any(t, prom_getchild(sunode)); - } - } + dev_set_drvdata(&op->dev, up); + + inst++; + + return 0; + +out_unmap: + of_iounmap(up->port.membase, up->reg_size); + return err; } -static int __init sunsu_probe(void) +static int __devexit su_remove(struct of_device *dev) { - int node; - int len; - struct su_probe_scan scan; + struct uart_sunsu_port *up = dev_get_drvdata(&dev->dev);; - /* - * First, we scan the tree. - */ - scan.devices = 0; - scan.msx = -1; - scan.kbx = -1; - scan.kbnode = 0; - scan.msnode = 0; + if (up->su_type == SU_PORT_MS || + up->su_type == SU_PORT_KBD) { +#ifdef CONFIG_SERIO + serio_unregister_port(&up->serio); +#endif + kfree(up); + } else if (up->port.type != PORT_UNKNOWN) { + uart_remove_one_port(&sunsu_reg, &up->port); + } - /* - * Get the nodes for keyboard and mouse from 'aliases'... - */ - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "aliases"); - if (node != 0) { - len = prom_getproperty(node, "keyboard", scan.prop, SU_PROPSIZE); - if (len > 0) { - scan.prop[len] = 0; - scan.kbnode = prom_finddevice(scan.prop); - } + dev_set_drvdata(&dev->dev, NULL); - len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE); - if (len > 0) { - scan.prop[len] = 0; - scan.msnode = prom_finddevice(scan.prop); - } - } + return 0; +} - su_probe_any(&scan, prom_getchild(prom_root_node)); +static struct of_device_id su_match[] = { + { + .name = "su", + }, + { + .name = "su_pnp", + }, + { + .name = "serial", + .compatible = "su", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, su_match); - /* - * Second, we process the special case of keyboard and mouse. - * - * Currently if we got keyboard and mouse hooked to "su" ports - * we do not use any possible remaining "su" as a serial port. - * Thus, we ignore values of .msx and .kbx, then compact ports. - */ - if (scan.msx != -1 && scan.kbx != -1) { - sunsu_ports[0].su_type = SU_PORT_MS; - sunsu_ports[0].port_node = scan.msnode; - sunsu_kbd_ms_init(&sunsu_ports[0], 0); +static struct of_platform_driver su_driver = { + .name = "su", + .match_table = su_match, + .probe = su_probe, + .remove = __devexit_p(su_remove), +}; - sunsu_ports[1].su_type = SU_PORT_KBD; - sunsu_ports[1].port_node = scan.kbnode; - sunsu_kbd_ms_init(&sunsu_ports[1], 1); +static int num_uart; - return 0; - } +static int __init sunsu_init(void) +{ + struct device_node *dp; + int err; - if (scan.msx != -1 || scan.kbx != -1) { - printk("sunsu_probe: cannot match keyboard and mouse, confused\n"); - return -ENODEV; + num_uart = 0; + for_each_node_by_name(dp, "su") { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + for_each_node_by_name(dp, "su_pnp") { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + for_each_node_by_name(dp, "serial") { + if (of_device_is_compatible(dp, "su")) { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } } - if (scan.devices == 0) - return -ENODEV; + if (num_uart) { + sunsu_reg.minor = sunserial_current_minor; + sunsu_reg.nr = num_uart; + err = uart_register_driver(&sunsu_reg); + if (err) + return err; + sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64; + sunserial_current_minor += num_uart; + sunsu_reg.cons = SUNSU_CONSOLE(num_uart); + } - /* - * Console must be initiated after the generic initialization. - */ - sunsu_serial_init(); - sunsu_serial_console_init(); + err = of_register_driver(&su_driver, &of_bus_type); + if (err && num_uart) + uart_unregister_driver(&sunsu_reg); - return 0; + return err; } static void __exit sunsu_exit(void) { - int i, saw_uart; - - saw_uart = 0; - for (i = 0; i < UART_NR; i++) { - struct uart_sunsu_port *up = &sunsu_ports[i]; - - if (up->su_type == SU_PORT_MS || - up->su_type == SU_PORT_KBD) { -#ifdef CONFIG_SERIO - if (up->serio) { - serio_unregister_port(up->serio); - up->serio = NULL; - } -#endif - } else if (up->port.type != PORT_UNKNOWN) { - uart_remove_one_port(&sunsu_reg, &up->port); - saw_uart++; - } - } - - if (saw_uart) + if (num_uart) uart_unregister_driver(&sunsu_reg); } -module_init(sunsu_probe); +module_init(sunsu_init); module_exit(sunsu_exit); + +MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller"); +MODULE_DESCRIPTION("Sun SU serial port driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL");