Loading drivers/serial/sunsu.c +157 −334 Original line number Original line Diff line number Diff line Loading @@ -40,11 +40,8 @@ #include <asm/io.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/irq.h> #include <asm/oplib.h> #include <asm/prom.h> #include <asm/ebus.h> #include <asm/of_device.h> #ifdef CONFIG_SPARC64 #include <asm/isa.h> #endif #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #define SUPPORT_SYSRQ Loading Loading @@ -94,10 +91,10 @@ struct uart_sunsu_port { /* Probing information. */ /* Probing information. */ enum su_type su_type; enum su_type su_type; unsigned int type_probed; /* XXX Stupid */ unsigned int type_probed; /* XXX Stupid */ int port_node; unsigned long reg_size; #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO struct serio *serio; struct serio serio; int serio_open; int serio_open; #endif #endif }; }; Loading Loading @@ -509,7 +506,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. */ /* Stop-A is handled by drivers/char/keyboard.c now. */ if (up->su_type == SU_PORT_KBD) { if (up->su_type == SU_PORT_KBD) { #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs); #endif #endif } else if (up->su_type == SU_PORT_MS) { } else if (up->su_type == SU_PORT_MS) { int ret = suncore_mouse_baud_detection(ch, is_break); int ret = suncore_mouse_baud_detection(ch, is_break); Loading @@ -523,7 +520,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg case 0: case 0: #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs); #endif #endif break; break; }; }; Loading Loading @@ -1031,99 +1028,14 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) { { unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; 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; unsigned long flags; if (!up->port_node || !up->su_type) if (up->su_type == SU_PORT_NONE) return; return; up->type_probed = PORT_UNKNOWN; up->type_probed = PORT_UNKNOWN; up->port.iotype = UPIO_MEM; up->port.iotype = UPIO_MEM; /* * First we look for Ebus-bases su's */ for_each_ebus(ebus) { for_each_ebusdev(dev, ebus) { if (dev->prom_node->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->port.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->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->port.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->port.irq = IRQ_4M(13); #endif ebus_done: spin_lock_irqsave(&up->port.lock, flags); spin_lock_irqsave(&up->port.lock, flags); if (!(up->port.flags & UPF_BUGGY_UART)) { if (!(up->port.flags & UPF_BUGGY_UART)) { Loading Loading @@ -1269,18 +1181,13 @@ static struct uart_driver sunsu_reg = { .major = TTY_MAJOR, .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; int quot, baud; #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO struct serio *serio; struct serio *serio; #endif #endif spin_lock_init(&up->port.lock); 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; up->cflag = B1200 | CS8 | CLOCAL | CREAD; baud = 1200; baud = 1200; Loading @@ -1292,18 +1199,10 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) sunsu_autoconfig(up); sunsu_autoconfig(up); if (up->port.type == PORT_UNKNOWN) if (up->port.type == PORT_UNKNOWN) return -1; return -ENODEV; printk(KERN_INFO "su%d at 0x%p (irq = %d) is a %s\n", channel, up->port.membase, up->port.irq, sunsu_type(&up->port)); #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = &up->serio; if (serio) { memset(serio, 0, sizeof(*serio)); serio->port_data = up; serio->port_data = up; serio->id.type = SERIO_RS232; serio->id.type = SERIO_RS232; Loading @@ -1315,18 +1214,16 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) serio->id.extra = 1; serio->id.extra = 1; strlcpy(serio->name, "sums", sizeof(serio->name)); strlcpy(serio->name, "sums", sizeof(serio->name)); } } strlcpy(serio->phys, (channel == 0 ? "su/serio0" : "su/serio1"), strlcpy(serio->phys, (!(up->port.line & 1) ? "su/serio0" : "su/serio1"), sizeof(serio->phys)); sizeof(serio->phys)); serio->write = sunsu_serio_write; serio->write = sunsu_serio_write; serio->open = sunsu_serio_open; serio->open = sunsu_serio_open; serio->close = sunsu_serio_close; serio->close = sunsu_serio_close; serio->dev.parent = up->port.dev; serio_register_port(serio); serio_register_port(serio); } else { printk(KERN_WARNING "su%d: not enough memory for serio port\n", channel); } #endif #endif sunsu_change_speed(&up->port, up->cflag, 0, quot); sunsu_change_speed(&up->port, up->cflag, 0, quot); Loading Loading @@ -1458,22 +1355,20 @@ static struct console sunsu_cons = { * Register console. * Register console. */ */ static inline struct console *SUNSU_CONSOLE(void) static inline struct console *SUNSU_CONSOLE(int num_uart) { { int i; int i; if (con_is_present()) if (con_is_present()) return NULL; return NULL; for (i = 0; i < UART_NR; i++) { for (i = 0; i < num_uart; i++) { int this_minor = sunsu_reg.minor + i; int this_minor = sunsu_reg.minor + i; if ((this_minor - 64) == (serial_console - 1)) if ((this_minor - 64) == (serial_console - 1)) break; break; } } if (i == UART_NR) if (i == num_uart) return NULL; if (sunsu_ports[i].port_node == 0) return NULL; return NULL; sunsu_cons.index = i; sunsu_cons.index = i; Loading @@ -1481,252 +1376,180 @@ static inline struct console *SUNSU_CONSOLE(void) return &sunsu_cons; return &sunsu_cons; } } #else #else #define SUNSU_CONSOLE() (NULL) #define SUNSU_CONSOLE(num_uart) (NULL) #define sunsu_serial_console_init() do { } while (0) #define sunsu_serial_console_init() do { } while (0) #endif #endif static int __init sunsu_serial_init(void) static enum su_type __devinit su_get_type(struct device_node *dp) { { int instance, ret, i; struct device_node *ap = of_find_node_by_path("/aliases"); /* How many instances do we need? */ instance = 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) continue; spin_lock_init(&up->port.lock); if (ap) { up->port.flags |= UPF_BOOT_AUTOCONF; char *keyb = of_get_property(ap, "keyboard", NULL); up->port.type = PORT_UNKNOWN; char *ms = of_get_property(ap, "mouse", NULL); up->port.uartclk = (SU_BASE_BAUD * 16); sunsu_autoconfig(up); if (keyb) { if (up->port.type == PORT_UNKNOWN) if (dp == of_find_node_by_path(keyb)) continue; return SU_PORT_KBD; } if (ms) { if (dp == of_find_node_by_path(ms)) return SU_PORT_MS; } } up->port.line = instance++; return SU_PORT_PORT; up->port.ops = &sunsu_pops; } } sunsu_reg.minor = sunserial_current_minor; 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; int err; sunsu_reg.nr = instance; if (inst >= UART_NR) return -EINVAL; ret = uart_register_driver(&sunsu_reg); up = &sunsu_ports[inst]; if (ret < 0) up->port.line = inst; return ret; sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64; spin_lock_init(&up->port.lock); sunserial_current_minor += instance; up->su_type = su_get_type(dp); sunsu_reg.cons = SUNSU_CONSOLE(); rp = &op->resource[0]; up->port.mapbase = op->resource[0].start; for (i = 0; i < UART_NR; i++) { up->reg_size = (rp->end - rp->start) + 1; struct uart_sunsu_port *up = &sunsu_ports[i]; up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); if (!up->port.membase) return -ENOMEM; /* Do not register Keyboard/Mouse lines with UART up->port.irq = op->irqs[0]; * layer. */ if (up->su_type == SU_PORT_MS || up->su_type == SU_PORT_KBD) continue; if (up->port.type == PORT_UNKNOWN) up->port.dev = &op->dev; continue; uart_add_one_port(&sunsu_reg, &up->port); up->port.type = PORT_UNKNOWN; } up->port.uartclk = (SU_BASE_BAUD * 16); 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) goto out_unmap; } } static int su_node_ok(int node, char *name, int namelen) up->port.flags |= UPF_BOOT_AUTOCONF; { 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; } } return 1; } return 0; sunsu_autoconfig(up); } #define SU_PROPSIZE 128 err = -ENODEV; if (up->port.type == PORT_UNKNOWN) goto out_unmap; /* up->port.ops = &sunsu_pops; * 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 = uart_add_one_port(&sunsu_reg, &up->port); * We have several platforms which present 'su' in different parts if (err) * of the device tree. 'su' may be found under obio, ebus, isa and pci. goto out_unmap; * 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; if (t->devices >= UART_NR) dev_set_drvdata(&op->dev, up); return; for (; sunode != 0; sunode = prom_getsibling(sunode)) { inst++; len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE); if (len <= 1) return 0; continue; /* Broken PROM node */ out_unmap: if (su_node_ok(sunode, t->prop, len)) { of_iounmap(up->port.membase, up->reg_size); up = &sunsu_ports[t->devices]; return err; 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)); } } } } static int __init sunsu_probe(void) static int __devexit su_remove(struct of_device *dev) { { int node; struct uart_sunsu_port *up = dev_get_drvdata(&dev->dev);; int len; struct su_probe_scan scan; /* if (up->su_type == SU_PORT_MS || * First, we scan the tree. up->su_type == SU_PORT_KBD) { */ #ifdef CONFIG_SERIO scan.devices = 0; serio_unregister_port(&up->serio); scan.msx = -1; #endif scan.kbx = -1; } else if (up->port.type != PORT_UNKNOWN) scan.kbnode = 0; uart_remove_one_port(&sunsu_reg, &up->port); scan.msnode = 0; /* return 0; * 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); } } len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE); static struct of_device_id su_match[] = { if (len > 0) { { scan.prop[len] = 0; .name = "su", scan.msnode = prom_finddevice(scan.prop); }, } { } .name = "su_pnp", }, { .name = "serial", .compatible = "su", }, {}, }; MODULE_DEVICE_TABLE(of, su_match); su_probe_any(&scan, prom_getchild(prom_root_node)); static struct of_platform_driver su_driver = { .name = "su", .match_table = su_match, .probe = su_probe, .remove = __devexit_p(su_remove), }; /* static int num_uart; * 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); sunsu_ports[1].su_type = SU_PORT_KBD; static int __init sunsu_init(void) sunsu_ports[1].port_node = scan.kbnode; { sunsu_kbd_ms_init(&sunsu_ports[1], 1); struct device_node *dp; int err; return 0; 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.msx != -1 || scan.kbx != -1) { printk("sunsu_probe: cannot match keyboard and mouse, confused\n"); return -ENODEV; } } if (scan.devices == 0) if (num_uart) { return -ENODEV; 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); } /* err = of_register_driver(&su_driver, &of_bus_type); * Console must be initiated after the generic initialization. if (err && num_uart) */ uart_unregister_driver(&sunsu_reg); sunsu_serial_init(); return 0; return err; } } static void __exit sunsu_exit(void) static void __exit sunsu_exit(void) { { int i, saw_uart; if (num_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) uart_unregister_driver(&sunsu_reg); uart_unregister_driver(&sunsu_reg); } } module_init(sunsu_probe); module_init(sunsu_init); module_exit(sunsu_exit); module_exit(sunsu_exit); MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL"); Loading
drivers/serial/sunsu.c +157 −334 Original line number Original line Diff line number Diff line Loading @@ -40,11 +40,8 @@ #include <asm/io.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/irq.h> #include <asm/oplib.h> #include <asm/prom.h> #include <asm/ebus.h> #include <asm/of_device.h> #ifdef CONFIG_SPARC64 #include <asm/isa.h> #endif #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #define SUPPORT_SYSRQ Loading Loading @@ -94,10 +91,10 @@ struct uart_sunsu_port { /* Probing information. */ /* Probing information. */ enum su_type su_type; enum su_type su_type; unsigned int type_probed; /* XXX Stupid */ unsigned int type_probed; /* XXX Stupid */ int port_node; unsigned long reg_size; #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO struct serio *serio; struct serio serio; int serio_open; int serio_open; #endif #endif }; }; Loading Loading @@ -509,7 +506,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. */ /* Stop-A is handled by drivers/char/keyboard.c now. */ if (up->su_type == SU_PORT_KBD) { if (up->su_type == SU_PORT_KBD) { #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs); #endif #endif } else if (up->su_type == SU_PORT_MS) { } else if (up->su_type == SU_PORT_MS) { int ret = suncore_mouse_baud_detection(ch, is_break); int ret = suncore_mouse_baud_detection(ch, is_break); Loading @@ -523,7 +520,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg case 0: case 0: #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO serio_interrupt(up->serio, ch, 0, regs); serio_interrupt(&up->serio, ch, 0, regs); #endif #endif break; break; }; }; Loading Loading @@ -1031,99 +1028,14 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) { { unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; 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; unsigned long flags; if (!up->port_node || !up->su_type) if (up->su_type == SU_PORT_NONE) return; return; up->type_probed = PORT_UNKNOWN; up->type_probed = PORT_UNKNOWN; up->port.iotype = UPIO_MEM; up->port.iotype = UPIO_MEM; /* * First we look for Ebus-bases su's */ for_each_ebus(ebus) { for_each_ebusdev(dev, ebus) { if (dev->prom_node->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->port.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->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->port.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->port.irq = IRQ_4M(13); #endif ebus_done: spin_lock_irqsave(&up->port.lock, flags); spin_lock_irqsave(&up->port.lock, flags); if (!(up->port.flags & UPF_BUGGY_UART)) { if (!(up->port.flags & UPF_BUGGY_UART)) { Loading Loading @@ -1269,18 +1181,13 @@ static struct uart_driver sunsu_reg = { .major = TTY_MAJOR, .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; int quot, baud; #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO struct serio *serio; struct serio *serio; #endif #endif spin_lock_init(&up->port.lock); 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; up->cflag = B1200 | CS8 | CLOCAL | CREAD; baud = 1200; baud = 1200; Loading @@ -1292,18 +1199,10 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) sunsu_autoconfig(up); sunsu_autoconfig(up); if (up->port.type == PORT_UNKNOWN) if (up->port.type == PORT_UNKNOWN) return -1; return -ENODEV; printk(KERN_INFO "su%d at 0x%p (irq = %d) is a %s\n", channel, up->port.membase, up->port.irq, sunsu_type(&up->port)); #ifdef CONFIG_SERIO #ifdef CONFIG_SERIO up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = &up->serio; if (serio) { memset(serio, 0, sizeof(*serio)); serio->port_data = up; serio->port_data = up; serio->id.type = SERIO_RS232; serio->id.type = SERIO_RS232; Loading @@ -1315,18 +1214,16 @@ static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) serio->id.extra = 1; serio->id.extra = 1; strlcpy(serio->name, "sums", sizeof(serio->name)); strlcpy(serio->name, "sums", sizeof(serio->name)); } } strlcpy(serio->phys, (channel == 0 ? "su/serio0" : "su/serio1"), strlcpy(serio->phys, (!(up->port.line & 1) ? "su/serio0" : "su/serio1"), sizeof(serio->phys)); sizeof(serio->phys)); serio->write = sunsu_serio_write; serio->write = sunsu_serio_write; serio->open = sunsu_serio_open; serio->open = sunsu_serio_open; serio->close = sunsu_serio_close; serio->close = sunsu_serio_close; serio->dev.parent = up->port.dev; serio_register_port(serio); serio_register_port(serio); } else { printk(KERN_WARNING "su%d: not enough memory for serio port\n", channel); } #endif #endif sunsu_change_speed(&up->port, up->cflag, 0, quot); sunsu_change_speed(&up->port, up->cflag, 0, quot); Loading Loading @@ -1458,22 +1355,20 @@ static struct console sunsu_cons = { * Register console. * Register console. */ */ static inline struct console *SUNSU_CONSOLE(void) static inline struct console *SUNSU_CONSOLE(int num_uart) { { int i; int i; if (con_is_present()) if (con_is_present()) return NULL; return NULL; for (i = 0; i < UART_NR; i++) { for (i = 0; i < num_uart; i++) { int this_minor = sunsu_reg.minor + i; int this_minor = sunsu_reg.minor + i; if ((this_minor - 64) == (serial_console - 1)) if ((this_minor - 64) == (serial_console - 1)) break; break; } } if (i == UART_NR) if (i == num_uart) return NULL; if (sunsu_ports[i].port_node == 0) return NULL; return NULL; sunsu_cons.index = i; sunsu_cons.index = i; Loading @@ -1481,252 +1376,180 @@ static inline struct console *SUNSU_CONSOLE(void) return &sunsu_cons; return &sunsu_cons; } } #else #else #define SUNSU_CONSOLE() (NULL) #define SUNSU_CONSOLE(num_uart) (NULL) #define sunsu_serial_console_init() do { } while (0) #define sunsu_serial_console_init() do { } while (0) #endif #endif static int __init sunsu_serial_init(void) static enum su_type __devinit su_get_type(struct device_node *dp) { { int instance, ret, i; struct device_node *ap = of_find_node_by_path("/aliases"); /* How many instances do we need? */ instance = 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) continue; spin_lock_init(&up->port.lock); if (ap) { up->port.flags |= UPF_BOOT_AUTOCONF; char *keyb = of_get_property(ap, "keyboard", NULL); up->port.type = PORT_UNKNOWN; char *ms = of_get_property(ap, "mouse", NULL); up->port.uartclk = (SU_BASE_BAUD * 16); sunsu_autoconfig(up); if (keyb) { if (up->port.type == PORT_UNKNOWN) if (dp == of_find_node_by_path(keyb)) continue; return SU_PORT_KBD; } if (ms) { if (dp == of_find_node_by_path(ms)) return SU_PORT_MS; } } up->port.line = instance++; return SU_PORT_PORT; up->port.ops = &sunsu_pops; } } sunsu_reg.minor = sunserial_current_minor; 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; int err; sunsu_reg.nr = instance; if (inst >= UART_NR) return -EINVAL; ret = uart_register_driver(&sunsu_reg); up = &sunsu_ports[inst]; if (ret < 0) up->port.line = inst; return ret; sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64; spin_lock_init(&up->port.lock); sunserial_current_minor += instance; up->su_type = su_get_type(dp); sunsu_reg.cons = SUNSU_CONSOLE(); rp = &op->resource[0]; up->port.mapbase = op->resource[0].start; for (i = 0; i < UART_NR; i++) { up->reg_size = (rp->end - rp->start) + 1; struct uart_sunsu_port *up = &sunsu_ports[i]; up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); if (!up->port.membase) return -ENOMEM; /* Do not register Keyboard/Mouse lines with UART up->port.irq = op->irqs[0]; * layer. */ if (up->su_type == SU_PORT_MS || up->su_type == SU_PORT_KBD) continue; if (up->port.type == PORT_UNKNOWN) up->port.dev = &op->dev; continue; uart_add_one_port(&sunsu_reg, &up->port); up->port.type = PORT_UNKNOWN; } up->port.uartclk = (SU_BASE_BAUD * 16); 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) goto out_unmap; } } static int su_node_ok(int node, char *name, int namelen) up->port.flags |= UPF_BOOT_AUTOCONF; { 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; } } return 1; } return 0; sunsu_autoconfig(up); } #define SU_PROPSIZE 128 err = -ENODEV; if (up->port.type == PORT_UNKNOWN) goto out_unmap; /* up->port.ops = &sunsu_pops; * 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 = uart_add_one_port(&sunsu_reg, &up->port); * We have several platforms which present 'su' in different parts if (err) * of the device tree. 'su' may be found under obio, ebus, isa and pci. goto out_unmap; * 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; if (t->devices >= UART_NR) dev_set_drvdata(&op->dev, up); return; for (; sunode != 0; sunode = prom_getsibling(sunode)) { inst++; len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE); if (len <= 1) return 0; continue; /* Broken PROM node */ out_unmap: if (su_node_ok(sunode, t->prop, len)) { of_iounmap(up->port.membase, up->reg_size); up = &sunsu_ports[t->devices]; return err; 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)); } } } } static int __init sunsu_probe(void) static int __devexit su_remove(struct of_device *dev) { { int node; struct uart_sunsu_port *up = dev_get_drvdata(&dev->dev);; int len; struct su_probe_scan scan; /* if (up->su_type == SU_PORT_MS || * First, we scan the tree. up->su_type == SU_PORT_KBD) { */ #ifdef CONFIG_SERIO scan.devices = 0; serio_unregister_port(&up->serio); scan.msx = -1; #endif scan.kbx = -1; } else if (up->port.type != PORT_UNKNOWN) scan.kbnode = 0; uart_remove_one_port(&sunsu_reg, &up->port); scan.msnode = 0; /* return 0; * 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); } } len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE); static struct of_device_id su_match[] = { if (len > 0) { { scan.prop[len] = 0; .name = "su", scan.msnode = prom_finddevice(scan.prop); }, } { } .name = "su_pnp", }, { .name = "serial", .compatible = "su", }, {}, }; MODULE_DEVICE_TABLE(of, su_match); su_probe_any(&scan, prom_getchild(prom_root_node)); static struct of_platform_driver su_driver = { .name = "su", .match_table = su_match, .probe = su_probe, .remove = __devexit_p(su_remove), }; /* static int num_uart; * 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); sunsu_ports[1].su_type = SU_PORT_KBD; static int __init sunsu_init(void) sunsu_ports[1].port_node = scan.kbnode; { sunsu_kbd_ms_init(&sunsu_ports[1], 1); struct device_node *dp; int err; return 0; 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.msx != -1 || scan.kbx != -1) { printk("sunsu_probe: cannot match keyboard and mouse, confused\n"); return -ENODEV; } } if (scan.devices == 0) if (num_uart) { return -ENODEV; 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); } /* err = of_register_driver(&su_driver, &of_bus_type); * Console must be initiated after the generic initialization. if (err && num_uart) */ uart_unregister_driver(&sunsu_reg); sunsu_serial_init(); return 0; return err; } } static void __exit sunsu_exit(void) static void __exit sunsu_exit(void) { { int i, saw_uart; if (num_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) uart_unregister_driver(&sunsu_reg); uart_unregister_driver(&sunsu_reg); } } module_init(sunsu_probe); module_init(sunsu_init); module_exit(sunsu_exit); module_exit(sunsu_exit); MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");