X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fserial%2Fsunsab.c;fp=drivers%2Fserial%2Fsunsab.c;h=85664228a0b6a56123c04c00b5cf57a49f122a03;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=cfe20f730436159f92b1422bd082079da595737b;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index cfe20f730..85664228a 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -1,7 +1,7 @@ /* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 2002 David S. Miller (davem@redhat.com) * * Rewrote buffer handling to use CIRC(Circular Buffer) macros. * Maxim Krasnyanskiy @@ -12,9 +12,10 @@ * Theodore Ts'o , 2001-Oct-12 * * Ported to new 2.5.x UART layer. - * David S. Miller + * David S. Miller */ +#include #include #include #include @@ -36,8 +37,8 @@ #include #include -#include -#include +#include +#include #if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -850,6 +851,7 @@ static struct uart_ops sunsab_pops = { static struct uart_driver sunsab_reg = { .owner = THIS_MODULE, .driver_name = "serial", + .devfs_name = "tts/", .dev_name = "ttyS", .major = TTY_MAJOR, }; @@ -859,9 +861,8 @@ static int num_channels; #ifdef CONFIG_SERIAL_SUNSAB_CONSOLE -static void sunsab_console_putchar(struct uart_port *port, int c) +static __inline__ void sunsab_console_putchar(struct uart_sunsab_port *up, char c) { - struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -875,8 +876,13 @@ static void sunsab_console_putchar(struct uart_port *port, int c) static void sunsab_console_write(struct console *con, const char *s, unsigned n) { struct uart_sunsab_port *up = &sunsab_ports[con->index]; + int i; - uart_console_write(&up->port, s, n, sunsab_console_putchar); + for (i = 0; i < n; i++) { + if (*s == '\n') + sunsab_console_putchar(up, '\r'); + sunsab_console_putchar(up, *s++); + } sunsab_tec_wait(up); } @@ -886,15 +892,6 @@ static int sunsab_console_setup(struct console *con, char *options) unsigned long flags; unsigned int baud, quot; - /* - * The console framework calls us for each and every port - * registered. Defer the console setup until the requested - * port has been properly discovered. A bit of a hack, - * though... - */ - if (up->port.type != PORT_SUNSAB) - return -1; - printk("Console: ttyS%d (SAB82532)\n", (sunsab_reg.minor - 64) + con->index); @@ -958,13 +955,14 @@ static struct console sunsab_console = { .index = -1, .data = &sunsab_reg, }; +#define SUNSAB_CONSOLE (&sunsab_console) -static inline struct console *SUNSAB_CONSOLE(void) +static void __init sunsab_console_init(void) { int i; if (con_is_present()) - return NULL; + return; for (i = 0; i < num_channels; i++) { int this_minor = sunsab_reg.minor + i; @@ -973,200 +971,208 @@ static inline struct console *SUNSAB_CONSOLE(void) break; } if (i == num_channels) - return NULL; + return; sunsab_console.index = i; - - return &sunsab_console; + register_console(&sunsab_console); } #else -#define SUNSAB_CONSOLE() (NULL) +#define SUNSAB_CONSOLE (NULL) #define sunsab_console_init() do { } while (0) #endif -static int __devinit sunsab_init_one(struct uart_sunsab_port *up, - struct of_device *op, - unsigned long offset, - int line) +static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg) { - up->port.line = line; - up->port.dev = &op->dev; - - up->port.mapbase = op->resource[0].start + offset; - up->port.membase = of_ioremap(&op->resource[0], offset, - sizeof(union sab82532_async_regs), - "sab"); - if (!up->port.membase) - return -ENOMEM; - up->regs = (union sab82532_async_regs __iomem *) up->port.membase; - - up->port.irq = op->irqs[0]; + struct linux_ebus *ebus; + struct linux_ebus_device *edev = NULL; + + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if (!strcmp(edev->prom_name, "se")) { + callback(edev, arg); + continue; + } else if (!strcmp(edev->prom_name, "serial")) { + char compat[32]; + int clen; + + /* On RIO this can be an SE, check it. We could + * just check ebus->is_rio, but this is more portable. + */ + clen = prom_getproperty(edev->prom_node, "compatible", + compat, sizeof(compat)); + if (clen > 0) { + if (strncmp(compat, "sab82532", 8) == 0) { + callback(edev, arg); + continue; + } + } + } + } + } +} - up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; - up->port.iotype = UPIO_MEM; +static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg) +{ + int *count_p = arg; - writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); + (*count_p)++; +} - up->port.ops = &sunsab_pops; - up->port.type = PORT_SUNSAB; - up->port.uartclk = SAB_BASE_BAUD; +static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg) +{ + int *instance_p = arg; + struct uart_sunsab_port *up; + unsigned long regs, offset; + int i; - up->type = readb(&up->regs->r.vstr) & 0x0f; - writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); - writeb(0xff, &up->regs->w.pim); - if ((up->port.line & 0x1) == 0) { - up->pvr_dsr_bit = (1 << 0); - up->pvr_dtr_bit = (1 << 1); - } else { - up->pvr_dsr_bit = (1 << 3); - up->pvr_dtr_bit = (1 << 2); - } - up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); - writeb(up->cached_pvr, &up->regs->w.pvr); - up->cached_mode = readb(&up->regs->rw.mode); - up->cached_mode |= SAB82532_MODE_FRTS; - writeb(up->cached_mode, &up->regs->rw.mode); - up->cached_mode |= SAB82532_MODE_RTS; - writeb(up->cached_mode, &up->regs->rw.mode); + /* Note: ports are located in reverse order */ + regs = edev->resource[0].start; + offset = sizeof(union sab82532_async_regs); + for (i = 0; i < 2; i++) { + up = &sunsab_ports[(*instance_p * 2) + 1 - i]; - up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; - up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; + memset(up, 0, sizeof(*up)); + up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs)); + up->port.irq = edev->irqs[0]; + up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; + up->port.mapbase = (unsigned long)up->regs; + up->port.iotype = UPIO_MEM; - if (!(up->port.line & 0x01)) { - int err; + writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); - err = request_irq(up->port.irq, sunsab_interrupt, - IRQF_SHARED, "sab", up); - if (err) { - of_iounmap(up->port.membase, - sizeof(union sab82532_async_regs)); - return err; - } + offset -= sizeof(union sab82532_async_regs); } - - return 0; + + (*instance_p)++; } -static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match) +static int __init probe_for_sabs(void) { - static int inst; - struct uart_sunsab_port *up; - int err; - - up = &sunsab_ports[inst * 2]; - - err = sunsab_init_one(&up[0], op, - 0, - (inst * 2) + 0); - if (err) - return err; - - err = sunsab_init_one(&up[1], op, - sizeof(union sab82532_async_regs), - (inst * 2) + 1); - if (err) { - of_iounmap(up[0].port.membase, - sizeof(union sab82532_async_regs)); - free_irq(up[0].port.irq, &up[0]); - return err; - } + int this_sab = 0; - uart_add_one_port(&sunsab_reg, &up[0].port); - uart_add_one_port(&sunsab_reg, &up[1].port); + /* Find device instances. */ + for_each_sab_edev(&sab_count_callback, &this_sab); + if (!this_sab) + return -ENODEV; - dev_set_drvdata(&op->dev, &up[0]); + /* Allocate tables. */ + sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2, + GFP_KERNEL); + if (!sunsab_ports) + return -ENOMEM; - inst++; + num_channels = this_sab * 2; + this_sab = 0; + for_each_sab_edev(&sab_attach_callback, &this_sab); return 0; } -static void __devexit sab_remove_one(struct uart_sunsab_port *up) +static void __init sunsab_init_hw(void) { - uart_remove_one_port(&sunsab_reg, &up->port); - if (!(up->port.line & 1)) - free_irq(up->port.irq, up); - of_iounmap(up->port.membase, - sizeof(union sab82532_async_regs)); -} - -static int __devexit sab_remove(struct of_device *op) -{ - struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); + int i; - sab_remove_one(&up[0]); - sab_remove_one(&up[1]); + for (i = 0; i < num_channels; i++) { + struct uart_sunsab_port *up = &sunsab_ports[i]; + + up->port.line = i; + up->port.ops = &sunsab_pops; + up->port.type = PORT_SUNSAB; + up->port.uartclk = SAB_BASE_BAUD; + + up->type = readb(&up->regs->r.vstr) & 0x0f; + writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); + writeb(0xff, &up->regs->w.pim); + if (up->port.line == 0) { + up->pvr_dsr_bit = (1 << 0); + up->pvr_dtr_bit = (1 << 1); + } else { + up->pvr_dsr_bit = (1 << 3); + up->pvr_dtr_bit = (1 << 2); + } + up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); + writeb(up->cached_pvr, &up->regs->w.pvr); + up->cached_mode = readb(&up->regs->rw.mode); + up->cached_mode |= SAB82532_MODE_FRTS; + writeb(up->cached_mode, &up->regs->rw.mode); + up->cached_mode |= SAB82532_MODE_RTS; + writeb(up->cached_mode, &up->regs->rw.mode); - dev_set_drvdata(&op->dev, NULL); + up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; + up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; - return 0; + if (!(up->port.line & 0x01)) { + if (request_irq(up->port.irq, sunsab_interrupt, + SA_SHIRQ, "serial(sab82532)", up)) { + printk("sunsab%d: can't get IRQ %x\n", + i, up->port.irq); + continue; + } + } + } } -static struct of_device_id sab_match[] = { - { - .name = "se", - }, - { - .name = "serial", - .compatible = "sab82532", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, sab_match); - -static struct of_platform_driver sab_driver = { - .name = "sab", - .match_table = sab_match, - .probe = sab_probe, - .remove = __devexit_p(sab_remove), -}; - static int __init sunsab_init(void) { - struct device_node *dp; - int err; - - num_channels = 0; - for_each_node_by_name(dp, "se") - num_channels += 2; - for_each_node_by_name(dp, "serial") { - if (of_device_is_compatible(dp, "sab82532")) - num_channels += 2; - } + int ret = probe_for_sabs(); + int i; - if (num_channels) { - sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * - num_channels, GFP_KERNEL); - if (!sunsab_ports) - return -ENOMEM; + if (ret < 0) + return ret; - sunsab_reg.minor = sunserial_current_minor; - sunsab_reg.nr = num_channels; + sunsab_init_hw(); - err = uart_register_driver(&sunsab_reg); - if (err) { - kfree(sunsab_ports); - sunsab_ports = NULL; + sunsab_reg.minor = sunserial_current_minor; + sunsab_reg.nr = num_channels; + sunsab_reg.cons = SUNSAB_CONSOLE; - return err; + ret = uart_register_driver(&sunsab_reg); + if (ret < 0) { + int i; + + for (i = 0; i < num_channels; i++) { + struct uart_sunsab_port *up = &sunsab_ports[i]; + + if (!(up->port.line & 0x01)) + free_irq(up->port.irq, up); + iounmap(up->regs); } + kfree(sunsab_ports); + sunsab_ports = NULL; + + return ret; + } + + sunserial_current_minor += num_channels; + + sunsab_console_init(); + + for (i = 0; i < num_channels; i++) { + struct uart_sunsab_port *up = &sunsab_ports[i]; - sunsab_reg.tty_driver->name_base = sunsab_reg.minor - 64; - sunsab_reg.cons = SUNSAB_CONSOLE(); - sunserial_current_minor += num_channels; + uart_add_one_port(&sunsab_reg, &up->port); } - return of_register_driver(&sab_driver, &of_bus_type); + return 0; } static void __exit sunsab_exit(void) { - of_unregister_driver(&sab_driver); - if (num_channels) { - sunserial_current_minor -= num_channels; - uart_unregister_driver(&sunsab_reg); + int i; + + for (i = 0; i < num_channels; i++) { + struct uart_sunsab_port *up = &sunsab_ports[i]; + + uart_remove_one_port(&sunsab_reg, &up->port); + + if (!(up->port.line & 0x01)) + free_irq(up->port.irq, up); + iounmap(up->regs); } + sunserial_current_minor -= num_channels; + uart_unregister_driver(&sunsab_reg); + kfree(sunsab_ports); sunsab_ports = NULL; }