diff options
Diffstat (limited to 'drivers')
37 files changed, 1509 insertions, 513 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6cc4358f68c1..35819e312624 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(); + tty_unlock(tty); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e61cabdd69df..69e9ca2dd4b3 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } @@ -3289,6 +3289,7 @@ static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, static int __init cy_detect_isa(void) { #ifdef CONFIG_ISA + struct cyclades_card *card; unsigned short cy_isa_irq, nboard; void __iomem *cy_isa_address; unsigned short i, j, cy_isa_nchan; @@ -3349,7 +3350,8 @@ static int __init cy_detect_isa(void) } /* fill the next cy_card structure available */ for (j = 0; j < NR_CARDS; j++) { - if (cy_card[j].base_addr == NULL) + card = &cy_card[j]; + if (card->base_addr == NULL) break; } if (j == NR_CARDS) { /* no more cy_cards available */ @@ -3363,7 +3365,7 @@ static int __init cy_detect_isa(void) /* allocate IRQ */ if (request_irq(cy_isa_irq, cyy_interrupt, - 0, "Cyclom-Y", &cy_card[j])) { + 0, "Cyclom-Y", card)) { printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " "could not allocate IRQ#%d.\n", (unsigned long)cy_isa_address, cy_isa_irq); @@ -3372,16 +3374,16 @@ static int __init cy_detect_isa(void) } /* set cy_card */ - cy_card[j].base_addr = cy_isa_address; - cy_card[j].ctl_addr.p9050 = NULL; - cy_card[j].irq = (int)cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; - cy_card[j].nports = cy_isa_nchan; - if (cy_init_card(&cy_card[j])) { - cy_card[j].base_addr = NULL; - free_irq(cy_isa_irq, &cy_card[j]); + card->base_addr = cy_isa_address; + card->ctl_addr.p9050 = NULL; + card->irq = (int)cy_isa_irq; + card->bus_index = 0; + card->first_line = cy_next_channel; + card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; + card->nports = cy_isa_nchan; + if (cy_init_card(card)) { + card->base_addr = NULL; + free_irq(cy_isa_irq, card); iounmap(cy_isa_address); continue; } @@ -3695,6 +3697,7 @@ err: static int __devinit cy_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct cyclades_card *card; void __iomem *addr0 = NULL, *addr2 = NULL; char *card_name = NULL; u32 uninitialized_var(mailbox); @@ -3829,7 +3832,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } /* fill the next cy_card structure available */ for (card_no = 0; card_no < NR_CARDS; card_no++) { - if (cy_card[card_no].base_addr == NULL) + card = &cy_card[card_no]; + if (card->base_addr == NULL) break; } if (card_no == NR_CARDS) { /* no more cy_cards available */ @@ -3843,27 +3847,26 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { /* allocate IRQ */ retval = request_irq(irq, cyy_interrupt, - IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); + IRQF_SHARED, "Cyclom-Y", card); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ\n"); goto err_unmap; } - cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; + card->num_chips = nchan / CyPORTS_PER_CHIP; } else { struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; struct ZFW_CTRL __iomem *zfw_ctrl; zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - cy_card[card_no].hw_ver = mailbox; - cy_card[card_no].num_chips = (unsigned int)-1; - cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; + card->hw_ver = mailbox; + card->num_chips = (unsigned int)-1; + card->board_ctrl = &zfw_ctrl->board_ctrl; #ifdef CONFIG_CYZ_INTR /* allocate IRQ only if board has an IRQ */ if (irq != 0 && irq != 255) { retval = request_irq(irq, cyz_interrupt, - IRQF_SHARED, "Cyclades-Z", - &cy_card[card_no]); + IRQF_SHARED, "Cyclades-Z", card); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ\n"); goto err_unmap; @@ -3873,17 +3876,17 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } /* set cy_card */ - cy_card[card_no].base_addr = addr2; - cy_card[card_no].ctl_addr.p9050 = addr0; - cy_card[card_no].irq = irq; - cy_card[card_no].bus_index = 1; - cy_card[card_no].first_line = cy_next_channel; - cy_card[card_no].nports = nchan; - retval = cy_init_card(&cy_card[card_no]); + card->base_addr = addr2; + card->ctl_addr.p9050 = addr0; + card->irq = irq; + card->bus_index = 1; + card->first_line = cy_next_channel; + card->nports = nchan; + retval = cy_init_card(card); if (retval) goto err_null; - pci_set_drvdata(pdev, &cy_card[card_no]); + pci_set_drvdata(pdev, card); if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { @@ -3915,8 +3918,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, return 0; err_null: - cy_card[card_no].base_addr = NULL; - free_irq(irq, &cy_card[card_no]); + card->base_addr = NULL; + free_irq(irq, card); err_unmap: iounmap(addr0); if (addr2) diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c31459a2f..1e6405070ce6 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ee1c268f5f9d..4f34491b65c6 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1432,6 +1432,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, */ if (tty->receive_room < TTY_THRESHOLD_THROTTLE) tty_throttle(tty); + + /* FIXME: there is a tiny race here if the receive room check runs + before the other work executes and empties the buffer (upping + the receiving room and unthrottling. We then throttle and get + stuck. This has been observed and traced down by Vincent Pillet/ + We need to address this when we sort out out the rx path locking */ } int is_ignored(int sig) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 5505ffc91da4..a0ca0830cbcf 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&devpts_mutex); } #endif - tty_unlock(); + tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(); + tty_lock(tty); } } @@ -282,60 +283,98 @@ done: return 0; } -/* Traditional BSD devices */ -#ifdef CONFIG_LEGACY_PTYS - -static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, + bool legacy) { struct tty_struct *o_tty; + struct tty_port *ports[2]; int idx = tty->index; - int retval; + int retval = -ENOMEM; o_tty = alloc_tty_struct(); - if (!o_tty) - return -ENOMEM; + ports[0] = kmalloc(sizeof **ports, GFP_KERNEL); + ports[1] = kmalloc(sizeof **ports, GFP_KERNEL); + if (!o_tty || !ports[0] || !ports[1]) + goto err_free_tty; if (!try_module_get(driver->other->owner)) { /* This cannot in fact currently happen */ - retval = -ENOMEM; goto err_free_tty; } initialize_tty_struct(o_tty, driver->other, idx); - /* We always use new tty termios data so we can do this - the easy way .. */ - retval = tty_init_termios(tty); - if (retval) - goto err_deinit_tty; - - retval = tty_init_termios(o_tty); - if (retval) - goto err_free_termios; + if (legacy) { + /* We always use new tty termios data so we can do this + the easy way .. */ + retval = tty_init_termios(tty); + if (retval) + goto err_deinit_tty; + + retval = tty_init_termios(o_tty); + if (retval) + goto err_free_termios; + + driver->other->ttys[idx] = o_tty; + driver->ttys[idx] = tty; + } else { + tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (tty->termios == NULL) + goto err_deinit_tty; + *tty->termios = driver->init_termios; + tty->termios_locked = tty->termios + 1; + + o_tty->termios = kzalloc(sizeof(struct ktermios[2]), + GFP_KERNEL); + if (o_tty->termios == NULL) + goto err_free_termios; + *o_tty->termios = driver->other->init_termios; + o_tty->termios_locked = o_tty->termios + 1; + } /* * Everything allocated ... set up the o_tty structure. */ - driver->other->ttys[idx] = o_tty; tty_driver_kref_get(driver->other); if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; + tty_port_init(ports[0]); + tty_port_init(ports[1]); + o_tty->port = ports[0]; + tty->port = ports[1]; tty_driver_kref_get(driver); tty->count++; - driver->ttys[idx] = tty; return 0; err_free_termios: - tty_free_termios(tty); + if (legacy) + tty_free_termios(tty); + else + kfree(tty->termios); err_deinit_tty: deinitialize_tty_struct(o_tty); module_put(o_tty->driver->owner); err_free_tty: + kfree(ports[0]); + kfree(ports[1]); free_tty_struct(o_tty); return retval; } +static void pty_cleanup(struct tty_struct *tty) +{ + kfree(tty->port); +} + +/* Traditional BSD devices */ +#ifdef CONFIG_LEGACY_PTYS + +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + return pty_common_install(driver, tty, true); +} + static int pty_bsd_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -366,6 +405,7 @@ static const struct tty_operations master_pty_ops_bsd = { .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, .ioctl = pty_bsd_ioctl, + .cleanup = pty_cleanup, .resize = pty_resize }; @@ -379,6 +419,7 @@ static const struct tty_operations slave_pty_ops_bsd = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, + .cleanup = pty_cleanup, .resize = pty_resize }; @@ -509,66 +550,17 @@ static void pty_unix98_shutdown(struct tty_struct *tty) static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) { - struct tty_struct *o_tty; - int idx = tty->index; - - o_tty = alloc_tty_struct(); - if (!o_tty) - return -ENOMEM; - if (!try_module_get(driver->other->owner)) { - /* This cannot in fact currently happen */ - goto err_free_tty; - } - initialize_tty_struct(o_tty, driver->other, idx); - - tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (tty->termios == NULL) - goto err_free_mem; - *tty->termios = driver->init_termios; - tty->termios_locked = tty->termios + 1; - - o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (o_tty->termios == NULL) - goto err_free_mem; - *o_tty->termios = driver->other->init_termios; - o_tty->termios_locked = o_tty->termios + 1; - - tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; - /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - tty_driver_kref_get(driver); - tty->count++; - return 0; -err_free_mem: - deinitialize_tty_struct(o_tty); - kfree(o_tty->termios); - kfree(tty->termios); - module_put(o_tty->driver->owner); -err_free_tty: - free_tty_struct(o_tty); - return -ENOMEM; -} - -static void ptm_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) -{ + return pty_common_install(driver, tty, false); } -static void pts_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { } static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, - .remove = ptm_unix98_remove, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -579,13 +571,14 @@ static const struct tty_operations ptm_unix98_ops = { .set_termios = pty_set_termios, .ioctl = pty_unix98_ioctl, .shutdown = pty_unix98_shutdown, + .cleanup = pty_cleanup, .resize = pty_resize }; static const struct tty_operations pty_unix98_ops = { .lookup = pts_unix98_lookup, .install = pty_unix98_install, - .remove = pts_unix98_remove, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -594,7 +587,8 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .shutdown = pty_unix98_shutdown + .shutdown = pty_unix98_shutdown, + .cleanup = pty_cleanup, }; /** @@ -622,26 +616,26 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); + mutex_unlock(&devpts_mutex); if (index < 0) { retval = index; goto err_file; } mutex_lock(&tty_mutex); - mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&devpts_mutex); - tty_lock(); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -654,15 +648,15 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: tty_free_file(filp); return retval; diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 6e1958a325bd..44f52c6f15b9 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -282,6 +282,14 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, }, + [PORT_LPC3220] = { + .name = "LPC3220", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO | + UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -2194,6 +2202,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; + int fifo_bug = 0; switch (termios->c_cflag & CSIZE) { case CS5: @@ -2213,8 +2222,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & CSTOPB) cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) + if (termios->c_cflag & PARENB) { cval |= UART_LCR_PARITY; + if (up->bugs & UART_BUG_PARITY) + fifo_bug = 1; + } if (!(termios->c_cflag & PARODD)) cval |= UART_LCR_EPAR; #ifdef CMSPAR @@ -2238,7 +2250,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { fcr = uart_config[port->type].fcr; - if (baud < 2400) { + if (baud < 2400 || fifo_bug) { fcr &= ~UART_FCR_TRIGGER_MASK; fcr |= UART_FCR_TRIGGER_1; } @@ -2971,36 +2983,36 @@ void serial8250_resume_port(int line) static int __devinit serial8250_probe(struct platform_device *dev) { struct plat_serial8250_port *p = dev->dev.platform_data; - struct uart_port port; + struct uart_8250_port uart; int ret, i, irqflag = 0; - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); if (share_irqs) irqflag = IRQF_SHARED; for (i = 0; p && p->flags != 0; p++, i++) { - port.iobase = p->iobase; - port.membase = p->membase; - port.irq = p->irq; - port.irqflags = p->irqflags; - port.uartclk = p->uartclk; - port.regshift = p->regshift; - port.iotype = p->iotype; - port.flags = p->flags; - port.mapbase = p->mapbase; - port.hub6 = p->hub6; - port.private_data = p->private_data; - port.type = p->type; - port.serial_in = p->serial_in; - port.serial_out = p->serial_out; - port.handle_irq = p->handle_irq; - port.handle_break = p->handle_break; - port.set_termios = p->set_termios; - port.pm = p->pm; - port.dev = &dev->dev; - port.irqflags |= irqflag; - ret = serial8250_register_port(&port); + uart.port.iobase = p->iobase; + uart.port.membase = p->membase; + uart.port.irq = p->irq; + uart.port.irqflags = p->irqflags; + uart.port.uartclk = p->uartclk; + uart.port.regshift = p->regshift; + uart.port.iotype = p->iotype; + uart.port.flags = p->flags; + uart.port.mapbase = p->mapbase; + uart.port.hub6 = p->hub6; + uart.port.private_data = p->private_data; + uart.port.type = p->type; + uart.port.serial_in = p->serial_in; + uart.port.serial_out = p->serial_out; + uart.port.handle_irq = p->handle_irq; + uart.port.handle_break = p->handle_break; + uart.port.set_termios = p->set_termios; + uart.port.pm = p->pm; + uart.port.dev = &dev->dev; + uart.port.irqflags |= irqflag; + ret = serial8250_register_8250_port(&uart); if (ret < 0) { dev_err(&dev->dev, "unable to register port at index %d " "(IO%lx MEM%llx IRQ%d): %d\n", i, @@ -3073,7 +3085,7 @@ static struct platform_driver serial8250_isa_driver = { static struct platform_device *serial8250_isa_devs; /* - * serial8250_register_port and serial8250_unregister_port allows for + * serial8250_register_8250_port and serial8250_unregister_port allows for * 16x50 serial ports to be configured at run-time, to support PCMCIA * modems and PCI multiport cards. */ @@ -3147,6 +3159,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.regshift = up->port.regshift; uart->port.iotype = up->port.iotype; uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF; + uart->bugs = up->bugs; uart->port.mapbase = up->port.mapbase; uart->port.private_data = up->port.private_data; if (up->port.dev) @@ -3190,29 +3203,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up) EXPORT_SYMBOL(serial8250_register_8250_port); /** - * serial8250_register_port - register a serial port - * @port: serial port template - * - * Configure the serial port specified by the request. If the - * port exists and is in use, it is hung up and unregistered - * first. - * - * The port is then probed and if necessary the IRQ is autodetected - * If this fails an error is returned. - * - * On success the port is ready to use and the line number is returned. - */ -int serial8250_register_port(struct uart_port *port) -{ - struct uart_8250_port up; - - memset(&up, 0, sizeof(up)); - memcpy(&up.port, port, sizeof(*port)); - return serial8250_register_8250_port(&up); -} -EXPORT_SYMBOL(serial8250_register_port); - -/** * serial8250_unregister_port - remove a 16x50 serial port at runtime * @line: serial line number * diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index f9719d167c8d..c335b2b23a5f 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -78,6 +78,7 @@ struct serial8250_config { #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ #define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ #define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ +#define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */ #define PROBE_RSA (1 << 0) #define PROBE_ANY (~0) diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c index b0ce8c56f1a4..857498312a9a 100644 --- a/drivers/tty/serial/8250/8250_acorn.c +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -43,7 +43,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; struct serial_card_type *type = id->data; - struct uart_port port; + struct uart_8250_port uart; unsigned long bus_addr; unsigned int i; @@ -62,19 +62,19 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) ecard_set_drvdata(ec, info); - memset(&port, 0, sizeof(struct uart_port)); - port.irq = ec->irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - port.uartclk = type->uartclk; - port.iotype = UPIO_MEM; - port.regshift = 2; - port.dev = &ec->dev; + memset(&uart, 0, sizeof(struct uart_8250_port)); + uart.port.irq = ec->irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = type->uartclk; + uart.port.iotype = UPIO_MEM; + uart.port.regshift = 2; + uart.port.dev = &ec->dev; for (i = 0; i < info->num_ports; i ++) { - port.membase = info->vaddr + type->offset[i]; - port.mapbase = bus_addr + type->offset[i]; + uart.port.membase = info->vaddr + type->offset[i]; + uart.port.mapbase = bus_addr + type->offset[i]; - info->ports[i] = serial8250_register_port(&port); + info->ports[i] = serial8250_register_8250_port(&uart); } return 0; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f574eef3075f..afb955fdef03 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -89,7 +89,7 @@ static int dw8250_handle_irq(struct uart_port *p) static int __devinit dw8250_probe(struct platform_device *pdev) { - struct uart_port port = {}; + struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct device_node *np = pdev->dev.of_node; @@ -104,28 +104,28 @@ static int __devinit dw8250_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - port.private_data = data; - - spin_lock_init(&port.lock); - port.mapbase = regs->start; - port.irq = irq->start; - port.handle_irq = dw8250_handle_irq; - port.type = PORT_8250; - port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | + uart.port.private_data = data; + + spin_lock_init(&uart.port.lock); + uart.port.mapbase = regs->start; + uart.port.irq = irq->start; + uart.port.handle_irq = dw8250_handle_irq; + uart.port.type = PORT_8250; + uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; - port.dev = &pdev->dev; + uart.port.dev = &pdev->dev; - port.iotype = UPIO_MEM; - port.serial_in = dw8250_serial_in; - port.serial_out = dw8250_serial_out; + uart.port.iotype = UPIO_MEM; + uart.port.serial_in = dw8250_serial_in; + uart.port.serial_out = dw8250_serial_out; if (!of_property_read_u32(np, "reg-io-width", &val)) { switch (val) { case 1: break; case 4: - port.iotype = UPIO_MEM32; - port.serial_in = dw8250_serial_in32; - port.serial_out = dw8250_serial_out32; + uart.port.iotype = UPIO_MEM32; + uart.port.serial_in = dw8250_serial_in32; + uart.port.serial_out = dw8250_serial_out32; break; default: dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n", @@ -135,15 +135,15 @@ static int __devinit dw8250_probe(struct platform_device *pdev) } if (!of_property_read_u32(np, "reg-shift", &val)) - port.regshift = val; + uart.port.regshift = val; if (of_property_read_u32(np, "clock-frequency", &val)) { dev_err(&pdev->dev, "no clock-frequency property set\n"); return -EINVAL; } - port.uartclk = val; + uart.uart.port.uartclk = val; - data->line = serial8250_register_port(&port); + data->line = serial8250_register_8250_port(&uart); if (data->line < 0) return data->line; diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index d8c0ffbfa6e3..097dff9c08ad 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -26,7 +26,7 @@ static int __init serial_init_chip(struct parisc_device *dev) { - struct uart_port port; + struct uart_8250_port uart; unsigned long address; int err; @@ -48,21 +48,21 @@ static int __init serial_init_chip(struct parisc_device *dev) if (dev->id.sversion != 0x8d) address += 0x800; - memset(&port, 0, sizeof(port)); - port.iotype = UPIO_MEM; + memset(&uart, 0, sizeof(uart)); + uart.port.iotype = UPIO_MEM; /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - port.uartclk = 7272727; - port.mapbase = address; - port.membase = ioremap_nocache(address, 16); - port.irq = dev->irq; - port.flags = UPF_BOOT_AUTOCONF; - port.dev = &dev->dev; - - err = serial8250_register_port(&port); + uart.port.uartclk = 7272727; + uart.port.mapbase = address; + uart.port.membase = ioremap_nocache(address, 16); + uart.port.irq = dev->irq; + uart.port.flags = UPF_BOOT_AUTOCONF; + uart.port.dev = &dev->dev; + + err = serial8250_register_8250_port(&uart); if (err < 0) { printk(KERN_WARNING - "serial8250_register_port returned error %d\n", err); - iounmap(port.membase); + "serial8250_register_8250_port returned error %d\n", err); + iounmap(uart.port.membase); return err; } diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c index c13438c93012..8f1dd2cc00a8 100644 --- a/drivers/tty/serial/8250/8250_hp300.c +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -171,7 +171,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d, return 0; } #endif - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); /* Memory mapped I/O */ port.iotype = UPIO_MEM; @@ -182,7 +182,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d, port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); port.regshift = 1; port.dev = &d->dev; - line = serial8250_register_port(&port); + line = serial8250_register_8250_port(&uart); if (line < 0) { printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" @@ -210,7 +210,7 @@ static int __init hp300_8250_init(void) #ifdef CONFIG_HPAPCI int line; unsigned long base; - struct uart_port uport; + struct uart_8250_port uart; struct hp300_port *port; int i; #endif @@ -248,26 +248,26 @@ static int __init hp300_8250_init(void) if (!port) return -ENOMEM; - memset(&uport, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); /* Memory mapped I/O */ - uport.iotype = UPIO_MEM; - uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ | UPF_BOOT_AUTOCONF; /* XXX - no interrupt support yet */ - uport.irq = 0; - uport.uartclk = HPAPCI_BAUD_BASE * 16; - uport.mapbase = base; - uport.membase = (char *)(base + DIO_VIRADDRBASE); - uport.regshift = 2; + uart.port.irq = 0; + uart.port.uartclk = HPAPCI_BAUD_BASE * 16; + uart.port.mapbase = base; + uart.port.membase = (char *)(base + DIO_VIRADDRBASE); + uart.port.regshift = 2; - line = serial8250_register_port(&uport); + line = serial8250_register_8250_port(&uart); if (line < 0) { printk(KERN_NOTICE "8250_hp300: register_serial() APCI" - " %d irq %d failed\n", i, uport.irq); + " %d irq %d failed\n", i, uart.port.irq); kfree(port); continue; } diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 28e7c7cce893..62e10fe747a8 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -44,7 +44,7 @@ struct pci_serial_quirk { int (*init)(struct pci_dev *dev); int (*setup)(struct serial_private *, const struct pciserial_board *, - struct uart_port *, int); + struct uart_8250_port *, int); void (*exit)(struct pci_dev *dev); }; @@ -59,7 +59,7 @@ struct serial_private { }; static int pci_default_setup(struct serial_private*, - const struct pciserial_board*, struct uart_port*, int); + const struct pciserial_board*, struct uart_8250_port *, int); static void moan_device(const char *str, struct pci_dev *dev) { @@ -74,7 +74,7 @@ static void moan_device(const char *str, struct pci_dev *dev) } static int -setup_port(struct serial_private *priv, struct uart_port *port, +setup_port(struct serial_private *priv, struct uart_8250_port *port, int bar, int offset, int regshift) { struct pci_dev *dev = priv->dev; @@ -93,17 +93,17 @@ setup_port(struct serial_private *priv, struct uart_port *port, if (!priv->remapped_bar[bar]) return -ENOMEM; - port->iotype = UPIO_MEM; - port->iobase = 0; - port->mapbase = base + offset; - port->membase = priv->remapped_bar[bar] + offset; - port->regshift = regshift; + port->port.iotype = UPIO_MEM; + port->port.iobase = 0; + port->port.mapbase = base + offset; + port->port.membase = priv->remapped_bar[bar] + offset; + port->port.regshift = regshift; } else { - port->iotype = UPIO_PORT; - port->iobase = base + offset; - port->mapbase = 0; - port->membase = NULL; - port->regshift = 0; + port->port.iotype = UPIO_PORT; + port->port.iobase = base + offset; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; } return 0; } @@ -113,7 +113,7 @@ setup_port(struct serial_private *priv, struct uart_port *port, */ static int addidata_apci7800_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; bar = FL_GET_BASE(board->flags); @@ -140,7 +140,7 @@ static int addidata_apci7800_setup(struct serial_private *priv, */ static int afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -195,7 +195,7 @@ static int pci_hp_diva_init(struct pci_dev *dev) static int pci_hp_diva_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int offset = board->first_offset; unsigned int bar = FL_GET_BASE(board->flags); @@ -370,7 +370,7 @@ static void __devexit pci_ni8430_exit(struct pci_dev *dev) /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ static int sbs_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -525,7 +525,7 @@ static int pci_siig_init(struct pci_dev *dev) static int pci_siig_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; @@ -619,7 +619,7 @@ static int pci_timedia_init(struct pci_dev *dev) static int pci_timedia_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; @@ -653,7 +653,7 @@ pci_timedia_setup(struct serial_private *priv, static int titan_400l_800l_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -754,7 +754,7 @@ static int pci_ni8430_init(struct pci_dev *dev) static int pci_ni8430_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { void __iomem *p; unsigned long base, len; @@ -781,7 +781,7 @@ pci_ni8430_setup(struct serial_private *priv, static int pci_netmos_9900_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar; @@ -1032,10 +1032,17 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) return number_uarts; } -static int -pci_default_setup(struct serial_private *priv, +static int pci_asix_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + port->bugs |= UART_BUG_PARITY; + return pci_default_setup(priv, board, port, idx); +} + +static int pci_default_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { unsigned int bar, offset = board->first_offset, maxnr; @@ -1057,15 +1064,15 @@ pci_default_setup(struct serial_private *priv, static int ce4100_serial_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { int ret; ret = setup_port(priv, port, 0, 0, board->reg_shift); - port->iotype = UPIO_MEM32; - port->type = PORT_XSCALE; - port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); - port->regshift = 2; + port->port.iotype = UPIO_MEM32; + port->port.type = PORT_XSCALE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->port.regshift = 2; return ret; } @@ -1073,16 +1080,16 @@ ce4100_serial_setup(struct serial_private *priv, static int pci_omegapci_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { return setup_port(priv, port, 2, idx * 8, 0); } static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_NO_TXEN_TEST; + port->port.flags |= UPF_NO_TXEN_TEST; printk(KERN_DEBUG "serial8250: skipping TxEn test for device " "[%04x:%04x] subsystem [%04x:%04x]\n", priv->dev->vendor, @@ -1131,11 +1138,11 @@ static unsigned int kt_serial_in(struct uart_port *p, int offset) static int kt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_BUG_THRE; - port->serial_in = kt_serial_in; - port->handle_break = kt_handle_break; + port->port.flags |= UPF_BUG_THRE; + port->port.serial_in = kt_serial_in; + port->port.handle_break = kt_handle_break; return skip_tx_en_setup(priv, board, port, idx); } @@ -1151,9 +1158,9 @@ static int pci_eg20t_init(struct pci_dev *dev) static int pci_xr17c154_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) + struct uart_8250_port *port, int idx) { - port->flags |= UPF_EXAR_EFR; + port->port.flags |= UPF_EXAR_EFR; return pci_default_setup(priv, board, port, idx); } @@ -1187,6 +1194,7 @@ pci_xr17c154_setup(struct serial_private *priv, #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d +#define PCI_VENDOR_ID_ASIX 0x9710 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1726,7 +1734,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_omegapci_setup, - }, + }, + /* + * ASIX devices with FIFO bug + */ + { + .vendor = PCI_VENDOR_ID_ASIX, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_asix_setup, + }, /* * Default "match everything" terminator entry */ @@ -1887,7 +1905,6 @@ enum pci_board_num_t { pbn_panacom, pbn_panacom2, pbn_panacom4, - pbn_exsys_4055, pbn_plx_romulus, pbn_oxsemi, pbn_oxsemi_1_4000000, @@ -2393,13 +2410,6 @@ static struct pciserial_board pci_boards[] __devinitdata = { .reg_shift = 7, }, - [pbn_exsys_4055] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - /* I think this entry is broken - the first_offset looks wrong --rmk */ [pbn_plx_romulus] = { .flags = FL_BASE2, @@ -2728,7 +2738,7 @@ serial_pci_matches(const struct pciserial_board *board, struct serial_private * pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) { - struct uart_port serial_port; + struct uart_8250_port uart; struct serial_private *priv; struct pci_serial_quirk *quirk; int rc, nr_ports, i; @@ -2768,22 +2778,22 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) priv->dev = dev; priv->quirk = quirk; - memset(&serial_port, 0, sizeof(struct uart_port)); - serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - serial_port.uartclk = board->base_baud * 16; - serial_port.irq = get_pci_irq(dev, board); - serial_port.dev = &dev->dev; + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = board->base_baud * 16; + uart.port.irq = get_pci_irq(dev, board); + uart.port.dev = &dev->dev; for (i = 0; i < nr_ports; i++) { - if (quirk->setup(priv, board, &serial_port, i)) + if (quirk->setup(priv, board, &uart, i)) break; #ifdef SERIAL_DEBUG_PCI printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", - serial_port.iobase, serial_port.irq, serial_port.iotype); + uart.port.iobase, uart.port.irq, uart.port.iotype); #endif - priv->line[i] = serial8250_register_port(&serial_port); + priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) { printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); break; @@ -3193,7 +3203,7 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, - pbn_exsys_4055 }, + pbn_b2_4_115200 }, /* * Megawolf Romulus PCI Serial Card, from Mike Hudson * (Exoray@isys.ca) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index a2f236510ff1..fde5aa60d51e 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -424,7 +424,7 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) static int __devinit serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - struct uart_port port; + struct uart_8250_port uart; int ret, line, flags = dev_id->driver_data; if (flags & UNKNOWN_DEV) { @@ -433,32 +433,32 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) return ret; } - memset(&port, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); if (pnp_irq_valid(dev, 0)) - port.irq = pnp_irq(dev, 0); + uart.port.irq = pnp_irq(dev, 0); if (pnp_port_valid(dev, 0)) { - port.iobase = pnp_port_start(dev, 0); - port.iotype = UPIO_PORT; + uart.port.iobase = pnp_port_start(dev, 0); + uart.port.iotype = UPIO_PORT; } else if (pnp_mem_valid(dev, 0)) { - port.mapbase = pnp_mem_start(dev, 0); - port.iotype = UPIO_MEM; - port.flags = UPF_IOREMAP; + uart.port.mapbase = pnp_mem_start(dev, 0); + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_IOREMAP; } else return -ENODEV; #ifdef SERIAL_DEBUG_PNP printk(KERN_DEBUG "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", - port.iobase, port.mapbase, port.irq, port.iotype); + uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype); #endif - port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - port.flags |= UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &dev->dev; + uart.port.flags |= UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; - line = serial8250_register_port(&port); + line = serial8250_register_8250_port(&uart); if (line < 0) return -ENODEV; diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 29b695d041ec..b7d48b346393 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -73,7 +73,7 @@ struct serial_quirk { unsigned int prodid; int multi; /* 1 = multifunction, > 1 = # ports */ void (*config)(struct pcmcia_device *); - void (*setup)(struct pcmcia_device *, struct uart_port *); + void (*setup)(struct pcmcia_device *, struct uart_8250_port *); void (*wakeup)(struct pcmcia_device *); int (*post)(struct pcmcia_device *); }; @@ -105,9 +105,9 @@ struct serial_cfg_mem { * Elan VPU16551 UART with 14.7456MHz oscillator * manfid 0x015D, 0x4C45 */ -static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) +static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart) { - port->uartclk = 14745600; + uart->port.uartclk = 14745600; } static int quirk_post_ibm(struct pcmcia_device *link) @@ -343,25 +343,25 @@ static void serial_detach(struct pcmcia_device *link) static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, unsigned int iobase, int irq) { - struct uart_port port; + struct uart_8250_port uart; int line; - memset(&port, 0, sizeof (struct uart_port)); - port.iobase = iobase; - port.irq = irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &handle->dev; + memset(&uart, 0, sizeof(uart)); + uart.port.iobase = iobase; + uart.port.irq = irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &handle->dev; if (buggy_uart) - port.flags |= UPF_BUGGY_UART; + uart.port.flags |= UPF_BUGGY_UART; if (info->quirk && info->quirk->setup) - info->quirk->setup(handle, &port); + info->quirk->setup(handle, &uart); - line = serial8250_register_port(&port); + line = serial8250_register_8250_port(&uart); if (line < 0) { - printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " - "0x%04lx, irq %d failed\n", (u_long)iobase, irq); + pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n", + (unsigned long)iobase, irq); return -EINVAL; } diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 070b442c1f81..00207865ec55 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -704,6 +704,25 @@ config SERIAL_PNX8XXX_CONSOLE If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 and you want to use serial console, say Y. Otherwise, say N. +config SERIAL_HS_LPC32XX + tristate "LPC32XX high speed serial port support" + depends on ARCH_LPC32XX && OF + select SERIAL_CORE + help + Support for the LPC32XX high speed serial ports (up to 900kbps). + Those are UARTs completely different from the Standard UARTs on the + LPC32XX SoC. + Choose M or Y here to build this driver. + +config SERIAL_HS_LPC32XX_CONSOLE + bool "Enable LPC32XX high speed UART serial console" + depends on SERIAL_HS_LPC32XX + select SERIAL_CORE_CONSOLE + help + If you would like to be able to use one of the high speed serial + ports on the LPC32XX as the console, you can do so by answering + Y to this option. + config SERIAL_CORE tristate diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7257c5d898ae..8a5df3804e5f 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o +obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o obj-$(CONFIG_SERIAL_DZ) += dz.o obj-$(CONFIG_SERIAL_ZS) += zs.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index c17923ec6e95..b81cc31f2657 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1215,14 +1215,14 @@ static irqreturn_t pl011_int(int irq, void *dev_id) return IRQ_RETVAL(handled); } -static unsigned int pl01x_tx_empty(struct uart_port *port) +static unsigned int pl011_tx_empty(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status = readw(uap->port.membase + UART01x_FR); return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; } -static unsigned int pl01x_get_mctrl(struct uart_port *port) +static unsigned int pl011_get_mctrl(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int result = 0; @@ -1285,7 +1285,7 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) } #ifdef CONFIG_CONSOLE_POLL -static int pl010_get_poll_char(struct uart_port *port) +static int pl011_get_poll_char(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; @@ -1297,7 +1297,7 @@ static int pl010_get_poll_char(struct uart_port *port) return readw(uap->port.membase + UART01x_DR); } -static void pl010_put_poll_char(struct uart_port *port, +static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1637,7 +1637,7 @@ static const char *pl011_type(struct uart_port *port) /* * Release the memory region(s) being used by 'port' */ -static void pl010_release_port(struct uart_port *port) +static void pl011_release_port(struct uart_port *port) { release_mem_region(port->mapbase, SZ_4K); } @@ -1645,7 +1645,7 @@ static void pl010_release_port(struct uart_port *port) /* * Request the memory region(s) being used by 'port' */ -static int pl010_request_port(struct uart_port *port) +static int pl011_request_port(struct uart_port *port) { return request_mem_region(port->mapbase, SZ_4K, "uart-pl011") != NULL ? 0 : -EBUSY; @@ -1654,18 +1654,18 @@ static int pl010_request_port(struct uart_port *port) /* * Configure/autoconfigure the port. */ -static void pl010_config_port(struct uart_port *port, int flags) +static void pl011_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) { port->type = PORT_AMBA; - pl010_request_port(port); + pl011_request_port(port); } } /* * verify the new serial_struct (for TIOCSSERIAL). */ -static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) +static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) { int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) @@ -1678,9 +1678,9 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) } static struct uart_ops amba_pl011_pops = { - .tx_empty = pl01x_tx_empty, + .tx_empty = pl011_tx_empty, .set_mctrl = pl011_set_mctrl, - .get_mctrl = pl01x_get_mctrl, + .get_mctrl = pl011_get_mctrl, .stop_tx = pl011_stop_tx, .start_tx = pl011_start_tx, .stop_rx = pl011_stop_rx, @@ -1691,13 +1691,13 @@ static struct uart_ops amba_pl011_pops = { .flush_buffer = pl011_dma_flush_buffer, .set_termios = pl011_set_termios, .type = pl011_type, - .release_port = pl010_release_port, - .request_port = pl010_request_port, - .config_port = pl010_config_port, - .verify_port = pl010_verify_port, + .release_port = pl011_release_port, + .request_port = pl011_request_port, + .config_port = pl011_config_port, + .verify_port = pl011_verify_port, #ifdef CONFIG_CONSOLE_POLL - .poll_get_char = pl010_get_poll_char, - .poll_put_char = pl010_put_poll_char, + .poll_get_char = pl011_get_poll_char, + .poll_put_char = pl011_put_poll_char, #endif }; diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 80b6b1b1f725..7264d4d26717 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 4ef747307ecb..0af4eec8c7b1 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -207,7 +207,7 @@ struct imx_port { unsigned short trcv_delay; /* transceiver delay */ struct clk *clk_ipg; struct clk *clk_per; - struct imx_uart_data *devdata; + const struct imx_uart_data *devdata; }; struct imx_port_ucrs { diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c new file mode 100644 index 000000000000..ba3af3bf6d43 --- /dev/null +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -0,0 +1,823 @@ +/* + * High Speed Serial Ports on NXP LPC32xx SoC + * + * Authors: Kevin Wells <kevin.wells@nxp.com> + * Roland Stigge <stigge@antcom.de> + * + * Copyright (C) 2010 NXP Semiconductors + * Copyright (C) 2012 Roland Stigge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/nmi.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <mach/platform.h> +#include <mach/hardware.h> + +/* + * High Speed UART register offsets + */ +#define LPC32XX_HSUART_FIFO(x) ((x) + 0x00) +#define LPC32XX_HSUART_LEVEL(x) ((x) + 0x04) +#define LPC32XX_HSUART_IIR(x) ((x) + 0x08) +#define LPC32XX_HSUART_CTRL(x) ((x) + 0x0C) +#define LPC32XX_HSUART_RATE(x) ((x) + 0x10) + +#define LPC32XX_HSU_BREAK_DATA (1 << 10) +#define LPC32XX_HSU_ERROR_DATA (1 << 9) +#define LPC32XX_HSU_RX_EMPTY (1 << 8) + +#define LPC32XX_HSU_TX_LEV(n) (((n) >> 8) & 0xFF) +#define LPC32XX_HSU_RX_LEV(n) ((n) & 0xFF) + +#define LPC32XX_HSU_TX_INT_SET (1 << 6) +#define LPC32XX_HSU_RX_OE_INT (1 << 5) +#define LPC32XX_HSU_BRK_INT (1 << 4) +#define LPC32XX_HSU_FE_INT (1 << 3) +#define LPC32XX_HSU_RX_TIMEOUT_INT (1 << 2) +#define LPC32XX_HSU_RX_TRIG_INT (1 << 1) +#define LPC32XX_HSU_TX_INT (1 << 0) + +#define LPC32XX_HSU_HRTS_INV (1 << 21) +#define LPC32XX_HSU_HRTS_TRIG_8B (0x0 << 19) +#define LPC32XX_HSU_HRTS_TRIG_16B (0x1 << 19) +#define LPC32XX_HSU_HRTS_TRIG_32B (0x2 << 19) +#define LPC32XX_HSU_HRTS_TRIG_48B (0x3 << 19) +#define LPC32XX_HSU_HRTS_EN (1 << 18) +#define LPC32XX_HSU_TMO_DISABLED (0x0 << 16) +#define LPC32XX_HSU_TMO_INACT_4B (0x1 << 16) +#define LPC32XX_HSU_TMO_INACT_8B (0x2 << 16) +#define LPC32XX_HSU_TMO_INACT_16B (0x3 << 16) +#define LPC32XX_HSU_HCTS_INV (1 << 15) +#define LPC32XX_HSU_HCTS_EN (1 << 14) +#define LPC32XX_HSU_OFFSET(n) ((n) << 9) +#define LPC32XX_HSU_BREAK (1 << 8) +#define LPC32XX_HSU_ERR_INT_EN (1 << 7) +#define LPC32XX_HSU_RX_INT_EN (1 << 6) +#define LPC32XX_HSU_TX_INT_EN (1 << 5) +#define LPC32XX_HSU_RX_TL1B (0x0 << 2) +#define LPC32XX_HSU_RX_TL4B (0x1 << 2) +#define LPC32XX_HSU_RX_TL8B (0x2 << 2) +#define LPC32XX_HSU_RX_TL16B (0x3 << 2) +#define LPC32XX_HSU_RX_TL32B (0x4 << 2) +#define LPC32XX_HSU_RX_TL48B (0x5 << 2) +#define LPC32XX_HSU_TX_TLEMPTY (0x0 << 0) +#define LPC32XX_HSU_TX_TL0B (0x0 << 0) +#define LPC32XX_HSU_TX_TL4B (0x1 << 0) +#define LPC32XX_HSU_TX_TL8B (0x2 << 0) +#define LPC32XX_HSU_TX_TL16B (0x3 << 0) + +#define MODNAME "lpc32xx_hsuart" + +struct lpc32xx_hsuart_port { + struct uart_port port; +}; + +#define FIFO_READ_LIMIT 128 +#define MAX_PORTS 3 +#define LPC32XX_TTY_NAME "ttyTX" +static struct lpc32xx_hsuart_port lpc32xx_hs_ports[MAX_PORTS]; + +#ifdef CONFIG_SERIAL_HS_LPC32XX_CONSOLE +static void wait_for_xmit_empty(struct uart_port *port) +{ + unsigned int timeout = 10000; + + do { + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( + port->membase))) == 0) + break; + if (--timeout == 0) + break; + udelay(1); + } while (1); +} + +static void wait_for_xmit_ready(struct uart_port *port) +{ + unsigned int timeout = 10000; + + while (1) { + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( + port->membase))) < 32) + break; + if (--timeout == 0) + break; + udelay(1); + } +} + +static void lpc32xx_hsuart_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmit_ready(port); + writel((u32)ch, LPC32XX_HSUART_FIFO(port->membase)); +} + +static void lpc32xx_hsuart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct lpc32xx_hsuart_port *up = &lpc32xx_hs_ports[co->index]; + unsigned long flags; + int locked = 1; + + touch_nmi_watchdog(); + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar); + wait_for_xmit_empty(&up->port); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init lpc32xx_hsuart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= MAX_PORTS) + co->index = 0; + + port = &lpc32xx_hs_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver lpc32xx_hsuart_reg; +static struct console lpc32xx_hsuart_console = { + .name = LPC32XX_TTY_NAME, + .write = lpc32xx_hsuart_console_write, + .device = uart_console_device, + .setup = lpc32xx_hsuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpc32xx_hsuart_reg, +}; + +static int __init lpc32xx_hsuart_console_init(void) +{ + register_console(&lpc32xx_hsuart_console); + return 0; +} +console_initcall(lpc32xx_hsuart_console_init); + +#define LPC32XX_HSUART_CONSOLE (&lpc32xx_hsuart_console) +#else +#define LPC32XX_HSUART_CONSOLE NULL +#endif + +static struct uart_driver lpc32xx_hs_reg = { + .owner = THIS_MODULE, + .driver_name = MODNAME, + .dev_name = LPC32XX_TTY_NAME, + .nr = MAX_PORTS, + .cons = LPC32XX_HSUART_CONSOLE, +}; +static int uarts_registered; + +static unsigned int __serial_get_clock_div(unsigned long uartclk, + unsigned long rate) +{ + u32 div, goodrate, hsu_rate, l_hsu_rate, comprate; + u32 rate_diff; + + /* Find the closest divider to get the desired clock rate */ + div = uartclk / rate; + goodrate = hsu_rate = (div / 14) - 1; + if (hsu_rate != 0) + hsu_rate--; + + /* Tweak divider */ + l_hsu_rate = hsu_rate + 3; + rate_diff = 0xFFFFFFFF; + + while (hsu_rate < l_hsu_rate) { + comprate = uartclk / ((hsu_rate + 1) * 14); + if (abs(comprate - rate) < rate_diff) { + goodrate = hsu_rate; + rate_diff = abs(comprate - rate); + } + + hsu_rate++; + } + if (hsu_rate > 0xFF) + hsu_rate = 0xFF; + + return goodrate; +} + +static void __serial_uart_flush(struct uart_port *port) +{ + u32 tmp; + int cnt = 0; + + while ((readl(LPC32XX_HSUART_LEVEL(port->membase)) > 0) && + (cnt++ < FIFO_READ_LIMIT)) + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); +} + +static void __serial_lpc32xx_rx(struct uart_port *port) +{ + unsigned int tmp, flag; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) { + /* Discard data: no tty available */ + while (!(readl(LPC32XX_HSUART_FIFO(port->membase)) & + LPC32XX_HSU_RX_EMPTY)) + ; + + return; + } + + /* Read data from FIFO and push into terminal */ + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); + while (!(tmp & LPC32XX_HSU_RX_EMPTY)) { + flag = TTY_NORMAL; + port->icount.rx++; + + if (tmp & LPC32XX_HSU_ERROR_DATA) { + /* Framing error */ + writel(LPC32XX_HSU_FE_INT, + LPC32XX_HSUART_IIR(port->membase)); + port->icount.frame++; + flag = TTY_FRAME; + tty_insert_flip_char(tty, 0, TTY_FRAME); + } + + tty_insert_flip_char(tty, (tmp & 0xFF), flag); + + tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); + } + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static void __serial_lpc32xx_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int tmp; + + if (port->x_char) { + writel((u32)port->x_char, LPC32XX_HSUART_FIFO(port->membase)); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + goto exit_tx; + + /* Transfer data */ + while (LPC32XX_HSU_TX_LEV(readl( + LPC32XX_HSUART_LEVEL(port->membase))) < 64) { + writel((u32) xmit->buf[xmit->tail], + LPC32XX_HSUART_FIFO(port->membase)); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + +exit_tx: + if (uart_circ_empty(xmit)) { + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + } +} + +static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + u32 status; + + spin_lock(&port->lock); + + /* Read UART status and clear latched interrupts */ + status = readl(LPC32XX_HSUART_IIR(port->membase)); + + if (status & LPC32XX_HSU_BRK_INT) { + /* Break received */ + writel(LPC32XX_HSU_BRK_INT, LPC32XX_HSUART_IIR(port->membase)); + port->icount.brk++; + uart_handle_break(port); + } + + /* Framing error */ + if (status & LPC32XX_HSU_FE_INT) + writel(LPC32XX_HSU_FE_INT, LPC32XX_HSUART_IIR(port->membase)); + + if (status & LPC32XX_HSU_RX_OE_INT) { + /* Receive FIFO overrun */ + writel(LPC32XX_HSU_RX_OE_INT, + LPC32XX_HSUART_IIR(port->membase)); + port->icount.overrun++; + if (tty) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_schedule_flip(tty); + } + } + + /* Data received? */ + if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) { + __serial_lpc32xx_rx(port); + if (tty) + tty_flip_buffer_push(tty); + } + + /* Transmit data request? */ + if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) { + writel(LPC32XX_HSU_TX_INT, LPC32XX_HSUART_IIR(port->membase)); + __serial_lpc32xx_tx(port); + } + + spin_unlock(&port->lock); + tty_kref_put(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int serial_lpc32xx_tx_empty(struct uart_port *port) +{ + unsigned int ret = 0; + + if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(port->membase))) == 0) + ret = TIOCSER_TEMT; + + return ret; +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + /* No signals are supported on HS UARTs */ +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int serial_lpc32xx_get_mctrl(struct uart_port *port) +{ + /* No signals are supported on HS UARTs */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_stop_tx(struct uart_port *port) +{ + u32 tmp; + + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_start_tx(struct uart_port *port) +{ + u32 tmp; + + __serial_lpc32xx_tx(port); + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp |= LPC32XX_HSU_TX_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_stop_rx(struct uart_port *port) +{ + u32 tmp; + + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + writel((LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT | + LPC32XX_HSU_FE_INT), LPC32XX_HSUART_IIR(port->membase)); +} + +/* port->lock held by caller. */ +static void serial_lpc32xx_enable_ms(struct uart_port *port) +{ + /* Modem status is not supported */ +} + +/* port->lock is not held. */ +static void serial_lpc32xx_break_ctl(struct uart_port *port, + int break_state) +{ + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&port->lock, flags); + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + if (break_state != 0) + tmp |= LPC32XX_HSU_BREAK; + else + tmp &= ~LPC32XX_HSU_BREAK; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */ +static void lpc32xx_loopback_set(resource_size_t mapbase, int state) +{ + int bit; + u32 tmp; + + switch (mapbase) { + case LPC32XX_HS_UART1_BASE: + bit = 0; + break; + case LPC32XX_HS_UART2_BASE: + bit = 1; + break; + case LPC32XX_HS_UART7_BASE: + bit = 6; + break; + default: + WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase); + return; + } + + tmp = readl(LPC32XX_UARTCTL_CLOOP); + if (state) + tmp |= (1 << bit); + else + tmp &= ~(1 << bit); + writel(tmp, LPC32XX_UARTCTL_CLOOP); +} + +/* port->lock is not held. */ +static int serial_lpc32xx_startup(struct uart_port *port) +{ + int retval; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&port->lock, flags); + + __serial_uart_flush(port); + + writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | + LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), + LPC32XX_HSUART_IIR(port->membase)); + + writel(0xFF, LPC32XX_HSUART_RATE(port->membase)); + + /* + * Set receiver timeout, HSU offset of 20, no break, no interrupts, + * and default FIFO trigger levels + */ + tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */ + + spin_unlock_irqrestore(&port->lock, flags); + + retval = request_irq(port->irq, serial_lpc32xx_interrupt, + 0, MODNAME, port); + if (!retval) + writel((tmp | LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN), + LPC32XX_HSUART_CTRL(port->membase)); + + return retval; +} + +/* port->lock is not held. */ +static void serial_lpc32xx_shutdown(struct uart_port *port) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + lpc32xx_loopback_set(port->mapbase, 1); /* go to loopback mode */ + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +/* port->lock is not held. */ +static void serial_lpc32xx_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, quot; + u32 tmp; + + /* Always 8-bit, no parity, 1 stop bit */ + termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); + termios->c_cflag |= CS8; + + termios->c_cflag &= ~(HUPCL | CMSPAR | CLOCAL | CRTSCTS); + + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / 14); + + quot = __serial_get_clock_div(port->uartclk, baud); + + spin_lock_irqsave(&port->lock, flags); + + /* Ignore characters? */ + tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); + if ((termios->c_cflag & CREAD) == 0) + tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); + else + tmp |= LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN; + writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); + + writel(quot, LPC32XX_HSUART_RATE(port->membase)); + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *serial_lpc32xx_type(struct uart_port *port) +{ + return MODNAME; +} + +static void serial_lpc32xx_release_port(struct uart_port *port) +{ + if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, SZ_4K); + } +} + +static int serial_lpc32xx_request_port(struct uart_port *port) +{ + int ret = -ENODEV; + + if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { + ret = 0; + + if (!request_mem_region(port->mapbase, SZ_4K, MODNAME)) + ret = -EBUSY; + else if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, SZ_4K); + if (!port->membase) { + release_mem_region(port->mapbase, SZ_4K); + ret = -ENOMEM; + } + } + } + + return ret; +} + +static void serial_lpc32xx_config_port(struct uart_port *port, int uflags) +{ + int ret; + + ret = serial_lpc32xx_request_port(port); + if (ret < 0) + return; + port->type = PORT_UART00; + port->fifosize = 64; + + __serial_uart_flush(port); + + writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | + LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), + LPC32XX_HSUART_IIR(port->membase)); + + writel(0xFF, LPC32XX_HSUART_RATE(port->membase)); + + /* Set receiver timeout, HSU offset of 20, no break, no interrupts, + and default FIFO trigger levels */ + writel(LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | + LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B, + LPC32XX_HSUART_CTRL(port->membase)); +} + +static int serial_lpc32xx_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UART00) + ret = -EINVAL; + + return ret; +} + +static struct uart_ops serial_lpc32xx_pops = { + .tx_empty = serial_lpc32xx_tx_empty, + .set_mctrl = serial_lpc32xx_set_mctrl, + .get_mctrl = serial_lpc32xx_get_mctrl, + .stop_tx = serial_lpc32xx_stop_tx, + .start_tx = serial_lpc32xx_start_tx, + .stop_rx = serial_lpc32xx_stop_rx, + .enable_ms = serial_lpc32xx_enable_ms, + .break_ctl = serial_lpc32xx_break_ctl, + .startup = serial_lpc32xx_startup, + .shutdown = serial_lpc32xx_shutdown, + .set_termios = serial_lpc32xx_set_termios, + .type = serial_lpc32xx_type, + .release_port = serial_lpc32xx_release_port, + .request_port = serial_lpc32xx_request_port, + .config_port = serial_lpc32xx_config_port, + .verify_port = serial_lpc32xx_verify_port, +}; + +/* + * Register a set of serial devices attached to a platform device + */ +static int __devinit serial_hs_lpc32xx_probe(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = &lpc32xx_hs_ports[uarts_registered]; + int ret = 0; + struct resource *res; + + if (uarts_registered >= MAX_PORTS) { + dev_err(&pdev->dev, + "Error: Number of possible ports exceeded (%d)!\n", + uarts_registered + 1); + return -ENXIO; + } + + memset(p, 0, sizeof(*p)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Error getting mem resource for HS UART port %d\n", + uarts_registered); + return -ENXIO; + } + p->port.mapbase = res->start; + p->port.membase = NULL; + + p->port.irq = platform_get_irq(pdev, 0); + if (p->port.irq < 0) { + dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n", + uarts_registered); + return p->port.irq; + } + + p->port.iotype = UPIO_MEM32; + p->port.uartclk = LPC32XX_MAIN_OSC_FREQ; + p->port.regshift = 2; + p->port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP; + p->port.dev = &pdev->dev; + p->port.ops = &serial_lpc32xx_pops; + p->port.line = uarts_registered++; + spin_lock_init(&p->port.lock); + + /* send port to loopback mode by default */ + lpc32xx_loopback_set(p->port.mapbase, 1); + + ret = uart_add_one_port(&lpc32xx_hs_reg, &p->port); + + platform_set_drvdata(pdev, p); + + return ret; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial_hs_lpc32xx_remove(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_remove_one_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} + + +#ifdef CONFIG_PM +static int serial_hs_lpc32xx_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_suspend_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} + +static int serial_hs_lpc32xx_resume(struct platform_device *pdev) +{ + struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); + + uart_resume_port(&lpc32xx_hs_reg, &p->port); + + return 0; +} +#else +#define serial_hs_lpc32xx_suspend NULL +#define serial_hs_lpc32xx_resume NULL +#endif + +static const struct of_device_id serial_hs_lpc32xx_dt_ids[] = { + { .compatible = "nxp,lpc3220-hsuart" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); + +static struct platform_driver serial_hs_lpc32xx_driver = { + .probe = serial_hs_lpc32xx_probe, + .remove = __devexit_p(serial_hs_lpc32xx_remove), + .suspend = serial_hs_lpc32xx_suspend, + .resume = serial_hs_lpc32xx_resume, + .driver = { + .name = MODNAME, + .owner = THIS_MODULE, + .of_match_table = serial_hs_lpc32xx_dt_ids, + }, +}; + +static int __init lpc32xx_hsuart_init(void) +{ + int ret; + + ret = uart_register_driver(&lpc32xx_hs_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_hs_lpc32xx_driver); + if (ret) + uart_unregister_driver(&lpc32xx_hs_reg); + + return ret; +} + +static void __exit lpc32xx_hsuart_exit(void) +{ + platform_driver_unregister(&serial_hs_lpc32xx_driver); + uart_unregister_driver(&lpc32xx_hs_reg); +} + +module_init(lpc32xx_hsuart_init); +module_exit(lpc32xx_hsuart_exit); + +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("NXP LPC32XX High Speed UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 5410c0637266..34e71874a892 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -208,6 +208,7 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .compatible = "ns16750", .data = (void *)PORT_16750, }, { .compatible = "ns16850", .data = (void *)PORT_16850, }, { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, + { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 4fdec6a6b758..d291518a58a4 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -253,6 +253,9 @@ struct eg20t_port { dma_addr_t rx_buf_dma; struct dentry *debugfs; + + /* protect the eg20t_port private structure and io access to membase */ + spinlock_t lock; }; /** @@ -1058,7 +1061,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) int next = 1; u8 msr; - spin_lock_irqsave(&priv->port.lock, flags); + spin_lock_irqsave(&priv->lock, flags); handled = 0; while (next) { iid = pch_uart_hal_get_iid(priv); @@ -1116,7 +1119,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) handled |= (unsigned int)ret; } - spin_unlock_irqrestore(&priv->port.lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); return IRQ_RETVAL(handled); } @@ -1226,9 +1229,9 @@ static void pch_uart_break_ctl(struct uart_port *port, int ctl) unsigned long flags; priv = container_of(port, struct eg20t_port, port); - spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&priv->lock, flags); pch_uart_hal_set_break(priv, ctl); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } /* Grab any interrupt resources and initialise any low level driver state. */ @@ -1376,7 +1379,8 @@ static void pch_uart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&priv->lock, flags); + spin_lock(&port->lock); uart_update_timeout(port, termios->c_cflag, baud); rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); @@ -1389,7 +1393,8 @@ static void pch_uart_set_termios(struct uart_port *port, tty_termios_encode_baud_rate(termios, baud, baud); out: - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock(&port->lock); + spin_unlock_irqrestore(&priv->lock, flags); } static const char *pch_uart_type(struct uart_port *port) @@ -1538,8 +1543,9 @@ pch_console_write(struct console *co, const char *s, unsigned int count) { struct eg20t_port *priv; unsigned long flags; + int priv_locked = 1; + int port_locked = 1; u8 ier; - int locked = 1; priv = pch_uart_ports[co->index]; @@ -1547,12 +1553,16 @@ pch_console_write(struct console *co, const char *s, unsigned int count) local_irq_save(flags); if (priv->port.sysrq) { - /* serial8250_handle_port() already took the lock */ - locked = 0; + spin_lock(&priv->lock); + /* serial8250_handle_port() already took the port lock */ + port_locked = 0; } else if (oops_in_progress) { - locked = spin_trylock(&priv->port.lock); - } else + priv_locked = spin_trylock(&priv->lock); + port_locked = spin_trylock(&priv->port.lock); + } else { + spin_lock(&priv->lock); spin_lock(&priv->port.lock); + } /* * First save the IER then disable the interrupts @@ -1570,8 +1580,10 @@ pch_console_write(struct console *co, const char *s, unsigned int count) wait_for_xmitr(priv, BOTH_EMPTY); iowrite8(ier, priv->membase + UART_IER); - if (locked) + if (port_locked) spin_unlock(&priv->port.lock); + if (priv_locked) + spin_unlock(&priv->lock); local_irq_restore(flags); } @@ -1669,6 +1681,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, pci_enable_msi(pdev); pci_set_master(pdev); + spin_lock_init(&priv->lock); + iobase = pci_resource_start(pdev, 0); mapbase = pci_resource_start(pdev, 1); priv->mapbase = mapbase; diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 5847a4b855f7..9033fc6e0e4e 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -670,9 +670,19 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) { struct uart_pxa_port *up = serial_pxa_ports[co->index]; unsigned int ier; + unsigned long flags; + int locked = 1; clk_prepare_enable(up->clk); + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + /* * First save the IER then disable the interrupts */ @@ -688,6 +698,10 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) wait_for_xmitr(up); serial_out(up, UART_IER, ier); + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); + clk_disable_unprepare(up->clk); } diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d8b0aee35632..d57f165d6be8 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -529,7 +529,7 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, switch (level) { case 3: - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + if (!IS_ERR(ourport->baudclk)) clk_disable(ourport->baudclk); clk_disable(ourport->clk); @@ -538,7 +538,7 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, case 0: clk_enable(ourport->clk); - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + if (!IS_ERR(ourport->baudclk)) clk_enable(ourport->baudclk); break; @@ -604,7 +604,6 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, char clkname[MAX_CLK_NAME_LENGTH]; int calc_deviation, deviation = (1 << 30) - 1; - *best_clk = NULL; clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel : ourport->info->def_clk_sel; for (cnt = 0; cnt < info->num_clks; cnt++) { @@ -613,7 +612,7 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, sprintf(clkname, "clk_uart_baud%d", cnt); clk = clk_get(ourport->port.dev, clkname); - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR(clk)) continue; rate = clk_get_rate(clk); @@ -684,7 +683,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, { struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); struct s3c24xx_uart_port *ourport = to_ourport(port); - struct clk *clk = NULL; + struct clk *clk = ERR_PTR(-EINVAL); unsigned long flags; unsigned int baud, quot, clk_sel = 0; unsigned int ulcon; @@ -705,7 +704,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel); if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; - if (!clk) + if (IS_ERR(clk)) return; /* check to see if we need to change clock source */ @@ -713,9 +712,9 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->baudclk != clk) { s3c24xx_serial_setsource(port, clk_sel); - if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { + if (!IS_ERR(ourport->baudclk)) { clk_disable(ourport->baudclk); - ourport->baudclk = NULL; + ourport->baudclk = ERR_PTR(-EINVAL); } clk_enable(clk); @@ -1014,10 +1013,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, * a disturbance in the clock-rate over the change. */ - if (IS_ERR(port->clk)) + if (IS_ERR(port->baudclk)) goto exit; - if (port->baudclk_rate == clk_get_rate(port->clk)) + if (port->baudclk_rate == clk_get_rate(port->baudclk)) goto exit; if (val == CPUFREQ_PRECHANGE) { @@ -1160,7 +1159,11 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); - return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name); + if (IS_ERR(ourport->baudclk)) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "* %s\n", + ourport->baudclk->name ?: "(null)"); } static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); @@ -1200,6 +1203,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) return -ENODEV; } + ourport->baudclk = ERR_PTR(-EINVAL); ourport->info = ourport->drv_data->info; ourport->cfg = (pdev->dev.platform_data) ? (struct s3c2410_uartcfg *)pdev->dev.platform_data : @@ -1387,7 +1391,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, sprintf(clk_name, "clk_uart_baud%d", clk_sel); clk = clk_get(port->dev, clk_name); - if (!IS_ERR(clk) && clk != NULL) + if (!IS_ERR(clk)) rate = clk_get_rate(clk); else rate = 1; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 246b823c1b27..a21dc8e3b7c0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2527,14 +2527,16 @@ void uart_insert_char(struct uart_port *port, unsigned int status, struct tty_struct *tty = port->state->port.tty; if ((status & port->ignore_status_mask & ~overrun) == 0) - tty_insert_flip_char(tty, ch, flag); + if (tty_insert_flip_char(tty, ch, flag) == 0) + ++port->icount.buf_overrun; /* * Overrun is special. Since it's reported immediately, * it doesn't affect the current character. */ if (status & ~port->ignore_status_mask & overrun) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (tty_insert_flip_char(tty, 0, TTY_OVERRUN) == 0) + ++port->icount.buf_overrun; } EXPORT_SYMBOL_GPL(uart_insert_char); diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 593d40ad0a6b..5ed0daae6564 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa1debf97cc7..45b43f11ca39 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index a3dddc12d2fe..4a1e4f07765b 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index b425c79675ad..ca7c25d9f6d5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -573,7 +574,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -666,7 +667,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -1103,12 +1104,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1403,10 +1404,14 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; + if (!tty->port) + tty->port = driver->ports[idx]; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1415,9 +1420,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1426,6 +1433,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1628,7 +1636,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1637,10 +1645,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1652,7 +1661,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1675,7 +1684,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1706,7 +1715,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1769,7 +1778,7 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); + tty_unlock_pair(tty, o_tty); return 0; } @@ -1782,14 +1791,16 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ release_tty(tty, idx); + tty_unlock_pair(tty, o_tty); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - tty_unlock(); return 0; } @@ -1893,6 +1904,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1916,8 +1930,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1938,17 +1951,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1977,7 +1992,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1989,17 +2004,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2007,11 +2020,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2094,10 +2106,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2934,6 +2949,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); @@ -3094,6 +3110,7 @@ static void destruct_tty_driver(struct kref *kref) kfree(p); cdev_del(&driver->cdev); } + kfree(driver->ports); kfree(driver); } @@ -3132,6 +3149,18 @@ int tty_register_driver(struct tty_driver *driver) if (!p) return -ENOMEM; } + /* + * There is too many lines in PTY and we won't need the array there + * since it has an ->install hook where it assigns ports properly. + */ + if (driver->type != TTY_DRIVER_TYPE_PTY) { + driver->ports = kcalloc(driver->num, sizeof(struct tty_port *), + GFP_KERNEL); + if (!driver->ports) { + error = -ENOMEM; + goto err_free_p; + } + } if (!driver->major) { error = alloc_chrdev_region(&dev, driver->minor_start, @@ -3144,10 +3173,8 @@ int tty_register_driver(struct tty_driver *driver) dev = MKDEV(driver->major, driver->minor_start); error = register_chrdev_region(dev, driver->num, driver->name); } - if (error < 0) { - kfree(p); - return error; - } + if (error < 0) + goto err_free_p; if (p) { driver->ttys = (struct tty_struct **)p; @@ -3160,13 +3187,8 @@ int tty_register_driver(struct tty_driver *driver) cdev_init(&driver->cdev, &tty_fops); driver->cdev.owner = driver->owner; error = cdev_add(&driver->cdev, dev, driver->num); - if (error) { - unregister_chrdev_region(dev, driver->num); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - return error; - } + if (error) + goto err_unreg_char; mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers); @@ -3193,13 +3215,14 @@ err: list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); +err_unreg_char: unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; +err_free_p: /* destruct_tty_driver will free driver->ports */ kfree(p); return error; } - EXPORT_SYMBOL(tty_register_driver); /* diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 9911eb6b34cd..847f7ed7a3ed 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -659,13 +659,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) goto enable; } - if (test_bit(TTY_HUPPED, &tty->flags)) { + if (test_bit(TTY_HUPPING, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -708,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -831,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_enable(tty); return 0; } + +static void tty_ldisc_kill(struct tty_struct *tty) +{ + mutex_lock(&tty->ldisc_mutex); + /* + * Now kill off the ldisc + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; + + /* Ensure the next open requests the N_TTY ldisc */ + tty_set_termios_ldisc(tty, N_TTY); + mutex_unlock(&tty->ldisc_mutex); +} + /** * tty_ldisc_release - release line discipline * @tty: tty being shut down @@ -912,27 +929,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock(); + tty_unlock_pair(tty, o_tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); - - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; + if (o_tty) { + tty_ldisc_halt(o_tty); + tty_ldisc_flush_works(o_tty); + } + tty_lock_pair(tty, o_tty); - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); - /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); if (o_tty) - tty_ldisc_release(o_tty, NULL); + tty_ldisc_kill(o_tty); /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c32a21..67feac9e6ebb 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,70 @@ #include <linux/semaphore.h> #include <linux/sched.h> -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ + +enum { + TTY_MUTEX_NORMAL, + TTY_MUTEX_NESTED, +}; /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +static void __lockfunc tty_lock_nested(struct tty_struct *tty, + unsigned int subclass) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock_nested(&tty->legacy_mutex, subclass); +} + +void __lockfunc tty_lock(struct tty_struct *tty) +{ + return tty_lock_nested(tty, TTY_MUTEX_NORMAL); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock_nested(tty2, TTY_MUTEX_NESTED); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock_nested(tty, TTY_MUTEX_NESTED); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index bf6e238146ae..a3ba776c574c 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -33,6 +33,15 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +struct device *tty_port_register_device(struct tty_port *port, + struct tty_driver *driver, unsigned index, + struct device *device) +{ + driver->ports[index] = port; + return tty_register_device(driver, index, device); +} +EXPORT_SYMBOL_GPL(tty_port_register_device); + int tty_port_alloc_xmit_buf(struct tty_port *port) { /* We may sleep in get_zeroed_page() */ @@ -230,7 +239,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -296,9 +305,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); @@ -413,6 +422,14 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, } EXPORT_SYMBOL(tty_port_close); +int tty_port_install(struct tty_port *port, struct tty_driver *driver, + struct tty_struct *tty) +{ + tty->port = port; + return tty_standard_install(driver, tty); +} +EXPORT_SYMBOL_GPL(tty_port_install); + int tty_port_open(struct tty_port *port, struct tty_struct *tty, struct file *filp) { diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 48cc6f25cfd3..9b4f60a6ab0e 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -310,7 +310,7 @@ static void put_queue(struct vc_data *vc, int ch) if (tty) { tty_insert_flip_char(tty, ch, 0); - con_schedule_flip(tty); + tty_schedule_flip(tty); } } @@ -325,7 +325,7 @@ static void puts_queue(struct vc_data *vc, char *cp) tty_insert_flip_char(tty, *cp, 0); cp++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void applkey(struct vc_data *vc, int key, char mode) @@ -586,7 +586,7 @@ static void fn_send_intr(struct vc_data *vc) if (!tty) return; tty_insert_flip_char(tty, 0, TTY_BREAK); - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void fn_scroll_forw(struct vc_data *vc) @@ -1049,13 +1049,10 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) */ int vt_get_leds(int console, int flag) { - unsigned long flags; struct kbd_struct * kbd = kbd_table + console; int ret; - spin_lock_irqsave(&kbd_event_lock, flags); ret = vc_kbd_led(kbd, flag); - spin_unlock_irqrestore(&kbd_event_lock, flags); return ret; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 84cbf298c094..7cb53c236339 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1380,7 +1380,7 @@ static void respond_string(const char *p, struct tty_struct *tty) tty_insert_flip_char(tty, *p, 0); p++; } - con_schedule_flip(tty); + tty_schedule_flip(tty); } static void cursor_report(struct vc_data *vc, struct tty_struct *tty) @@ -2792,41 +2792,52 @@ static void con_flush_chars(struct tty_struct *tty) /* * Allocate the console screen memory. */ -static int con_open(struct tty_struct *tty, struct file *filp) +static int con_install(struct tty_driver *driver, struct tty_struct *tty) { unsigned int currcons = tty->index; - int ret = 0; + struct vc_data *vc; + int ret; console_lock(); - if (tty->driver_data == NULL) { - ret = vc_allocate(currcons); - if (ret == 0) { - struct vc_data *vc = vc_cons[currcons].d; + ret = vc_allocate(currcons); + if (ret) + goto unlock; - /* Still being freed */ - if (vc->port.tty) { - console_unlock(); - return -ERESTARTSYS; - } - tty->driver_data = vc; - vc->port.tty = tty; + vc = vc_cons[currcons].d; - if (!tty->winsize.ws_row && !tty->winsize.ws_col) { - tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; - tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; - } - if (vc->vc_utf) - tty->termios->c_iflag |= IUTF8; - else - tty->termios->c_iflag &= ~IUTF8; - console_unlock(); - return ret; - } + /* Still being freed */ + if (vc->port.tty) { + ret = -ERESTARTSYS; + goto unlock; } + + ret = tty_port_install(&vc->port, driver, tty); + if (ret) + goto unlock; + + tty->driver_data = vc; + vc->port.tty = tty; + + if (!tty->winsize.ws_row && !tty->winsize.ws_col) { + tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; + tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; + } + if (vc->vc_utf) + tty->termios->c_iflag |= IUTF8; + else + tty->termios->c_iflag &= ~IUTF8; +unlock: console_unlock(); return ret; } +static int con_open(struct tty_struct *tty, struct file *filp) +{ + /* everything done in install */ + return 0; +} + + static void con_close(struct tty_struct *tty, struct file *filp) { /* Nothing to do - we defer to shutdown */ @@ -2947,6 +2958,7 @@ static int __init con_init(void) console_initcall(con_init); static const struct tty_operations con_ops = { + .install = con_install, .open = con_open, .close = con_close, .write = con_write, diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 64618547be11..b841f56d2e66 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new) wake_up_interruptible(&vt_event_waitqueue); } -/** - * vt_event_wait - wait for an event - * @vw: our event - * - * Waits for an event to occur which completes our vt_event_wait - * structure. On return the structure has wv->done set to 1 for success - * or 0 if some event such as a signal ended the wait. - */ - -static void vt_event_wait(struct vt_event_wait *vw) +static void __vt_event_queue(struct vt_event_wait *vw) { unsigned long flags; /* Prepare the event */ @@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw) spin_lock_irqsave(&vt_event_lock, flags); list_add(&vw->list, &vt_events); spin_unlock_irqrestore(&vt_event_lock, flags); +} + +static void __vt_event_wait(struct vt_event_wait *vw) +{ /* Wait for it to pass */ wait_event_interruptible(vt_event_waitqueue, vw->done); +} + +static void __vt_event_dequeue(struct vt_event_wait *vw) +{ + unsigned long flags; + /* Dequeue it */ spin_lock_irqsave(&vt_event_lock, flags); list_del(&vw->list); @@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw) } /** + * vt_event_wait - wait for an event + * @vw: our event + * + * Waits for an event to occur which completes our vt_event_wait + * structure. On return the structure has wv->done set to 1 for success + * or 0 if some event such as a signal ended the wait. + */ + +static void vt_event_wait(struct vt_event_wait *vw) +{ + __vt_event_queue(vw); + __vt_event_wait(vw); + __vt_event_dequeue(vw); +} + +/** * vt_event_wait_ioctl - event ioctl handler * @arg: argument to ioctl * @@ -177,10 +194,14 @@ int vt_waitactive(int n) { struct vt_event_wait vw; do { - if (n == fg_console + 1) - break; vw.event.event = VT_EVENT_SWITCH; - vt_event_wait(&vw); + __vt_event_queue(&vw); + if (n == fg_console + 1) { + __vt_event_dequeue(&vw); + break; + } + __vt_event_wait(&vw); + __vt_event_dequeue(&vw); if (vw.done == 0) return -EINTR; } while (vw.event.newev != n); diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index d47eb06fe463..7ae9af6b2a54 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -130,20 +130,14 @@ static void metrousb_read_int_callback(struct urb *urb) /* Set the data read from the usb port into the serial port buffer. */ tty = tty_port_tty_get(&port->port); - if (!tty) { - dev_err(&port->dev, "%s - bad tty pointer - exiting\n", - __func__); - return; - } - if (tty && urb->actual_length) { /* Loop through the data copying each byte to the tty layer. */ tty_insert_flip_string(tty, data, urb->actual_length); /* Force the data to the tty layer. */ tty_flip_buffer_push(tty); + tty_kref_put(tty); } - tty_kref_put(tty); /* Set any port variables. */ spin_lock_irqsave(&metro_priv->lock, flags); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 27483f91a4a3..da67abb1945e 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -207,7 +207,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_get_interface; - retval = tty_standard_install(driver, tty); + retval = tty_port_install(&port->port, driver, tty); if (retval) goto error_init_termios; |