X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc%2F4xx_io%2Fserial_sicc.c;h=e35483961b9071af1f46643cd4b875bb2673d74e;hb=refs%2Fheads%2Fvserver;hp=ee4dbc047dd386133d0bf9ee368adfe9206b2dff;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/ppc/4xx_io/serial_sicc.c b/arch/ppc/4xx_io/serial_sicc.c index ee4dbc047..e35483961 100644 --- a/arch/ppc/4xx_io/serial_sicc.c +++ b/arch/ppc/4xx_io/serial_sicc.c @@ -1,6 +1,4 @@ /* - * arch/ppc/4xx_io/serial_sicc.c - * * Driver for IBM STB3xxx SICC serial port * * Based on drivers/char/serial_amba.c, by ARM Ltd. @@ -30,7 +28,6 @@ * is compatible with normal ttyS* devices. */ -#include #include #include #include @@ -47,16 +44,17 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include -#include #include @@ -214,7 +212,6 @@ static struct tty_driver *siccnormal_driver; * memory if large numbers of serial ports are open. */ static u_char *tmp_buf; -static DECLARE_MUTEX(tmp_buf_sem); #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) @@ -264,6 +261,7 @@ struct SICC_state { unsigned int flags; int count; struct SICC_info *info; + spinlock_t sicc_lock; }; #define SICC_XMIT_SIZE 1024 @@ -385,9 +383,10 @@ static void siccuart_stop(struct tty_struct *tty) struct SICC_info *info = tty->driver_data; unsigned long flags; - save_flags(flags); cli(); + /* disable interrupts while stopping serial port interrupts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); siccuart_disable_tx_interrupt(info); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } static void siccuart_start(struct tty_struct *tty) @@ -395,11 +394,12 @@ static void siccuart_start(struct tty_struct *tty) struct SICC_info *info = tty->driver_data; unsigned long flags; - save_flags(flags); cli(); + /* disable interrupts while starting serial port interrupts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (info->xmit.head != info->xmit.tail && info->xmit.buf) siccuart_enable_tx_interrupt(info); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } @@ -414,7 +414,7 @@ static void siccuart_event(struct SICC_info *info, int event) } static void -siccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs) +siccuart_rx_chars(struct SICC_info *info) { struct tty_struct *tty = info->tty; unsigned int status, ch, rsr, flg, ignored = 0; @@ -441,7 +441,7 @@ siccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs) #ifdef SUPPORT_SYSRQ if (info->sysrq) { if (ch && time_before(jiffies, info->sysrq)) { - handle_sysrq(ch, regs, NULL); + handle_sysrq(ch, NULL); info->sysrq = 0; goto ignore_char; } @@ -553,15 +553,15 @@ static void siccuart_tx_chars(struct SICC_info *info) } -static irqreturn_t siccuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t siccuart_int_rx(int irq, void *dev_id) { struct SICC_info *info = dev_id; - siccuart_rx_chars(info, regs); + siccuart_rx_chars(info) return IRQ_HANDLED; } -static irqreturn_t siccuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t siccuart_int_tx(int irq, void *dev_id) { struct SICC_info *info = dev_id; siccuart_tx_chars(info); @@ -604,7 +604,8 @@ static int siccuart_startup(struct SICC_info *info) return -ENOMEM; } - save_flags(flags); cli(); + /* lock access to info while doing setup */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (info->xmit.buf) free_page(page); @@ -688,12 +689,12 @@ static int siccuart_startup(struct SICC_info *info) siccuart_enable_rx_interrupt(info); info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); return 0; errout: - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); return retval; } @@ -708,7 +709,8 @@ static void siccuart_shutdown(struct SICC_info *info) if (!(info->flags & ASYNC_INITIALIZED)) return; - save_flags(flags); cli(); /* Disable interrupts */ + /* lock while shutting down port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq @@ -746,7 +748,7 @@ static void siccuart_shutdown(struct SICC_info *info) info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } @@ -868,8 +870,8 @@ static void siccuart_change_speed(struct SICC_info *info, struct termios *old_te info->ignore_status_mask |= _LSR_OE_MASK; } - /* first, disable everything */ - save_flags(flags); cli(); + /* disable interrupts while reading and clearing registers */ + spin_lock_irqsave(&info->state->sicc_lock,flags); old_rcr = readb(info->port->uart_base + BL_SICC_RCR); old_tcr = readb(info->port->uart_base + BL_SICC_TxCR); @@ -881,7 +883,7 @@ static void siccuart_change_speed(struct SICC_info *info, struct termios *old_te /*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/ - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); /* Set baud rate */ @@ -909,12 +911,13 @@ static void siccuart_put_char(struct tty_struct *tty, u_char ch) if (!tty || !info->xmit.buf) return; - save_flags(flags); cli(); + /* lock info->xmit while adding character to tx buffer */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) != 0) { info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (SICC_XMIT_SIZE - 1); } - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } static void siccuart_flush_chars(struct tty_struct *tty) @@ -928,12 +931,13 @@ static void siccuart_flush_chars(struct tty_struct *tty) || !info->xmit.buf) return; - save_flags(flags); cli(); + /* disable interrupts while transmitting characters */ + spin_lock_irqsave(&info->state->sicc_lock,flags); siccuart_enable_tx_interrupt(info); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } -static int siccuart_write(struct tty_struct *tty, int from_user, +static int siccuart_write(struct tty_struct *tty, const u_char * buf, int count) { struct SICC_info *info = tty->driver_data; @@ -943,63 +947,28 @@ static int siccuart_write(struct tty_struct *tty, int from_user, if (!tty || !info->xmit.buf || !tmp_buf) return 0; - save_flags(flags); - if (from_user) { - down(&tmp_buf_sem); - while (1) { - int c1; - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SICC_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - - c -= copy_from_user(tmp_buf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - cli(); - c1 = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SICC_XMIT_SIZE); - if (c1 < c) - c = c1; - memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); - info->xmit.head = (info->xmit.head + c) & - (SICC_XMIT_SIZE - 1); - restore_flags(flags); - buf += c; - count -= c; - ret += c; - } - up(&tmp_buf_sem); - } else { - cli(); - while (1) { - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SICC_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(info->xmit.buf + info->xmit.head, buf, c); - info->xmit.head = (info->xmit.head + c) & - (SICC_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } - restore_flags(flags); + /* lock info->xmit while removing characters from buffer */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = (info->xmit.head + c) & + (SICC_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped) siccuart_enable_tx_interrupt(info); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); return ret; } @@ -1023,9 +992,10 @@ static void siccuart_flush_buffer(struct tty_struct *tty) unsigned long flags; pr_debug("siccuart_flush_buffer(%d) called\n", tty->index); - save_flags(flags); cli(); + /* lock info->xmit while zeroing buffer counts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); info->xmit.head = info->xmit.tail = 0; - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1054,10 +1024,11 @@ static void siccuart_throttle(struct tty_struct *tty) siccuart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) { - save_flags(flags); cli(); + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl &= ~TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } } @@ -1074,10 +1045,11 @@ static void siccuart_unthrottle(struct tty_struct *tty) } if (tty->termios->c_cflag & CRTSCTS) { - save_flags(flags); cli(); + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl |= TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } } @@ -1170,8 +1142,8 @@ static int set_serial_info(struct SICC_info *info, info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS)); state->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ / 100; - state->closing_wait = new_serial.closing_wait * HZ / 100; + state->close_delay = msecs_to_jiffies(10 * new_serial.close_delay); + state->closing_wait = msecs_to_jiffies(10 * new_serial.closing_wait); info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; port->fifosize = new_serial.xmit_fifo_size; @@ -1216,9 +1188,10 @@ static int get_lsr_info(struct SICC_info *info, unsigned int *value) unsigned int result, status; unsigned long flags; - save_flags(flags); cli(); + /* disable interrupts while reading status from port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); status = readb(info->port->uart_base + BL_SICC_LSR); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); result = status & _LSR_TSR_EMPTY ? TIOCSER_TEMT : 0; /* @@ -1269,10 +1242,11 @@ static int set_modem_info(struct SICC_info *info, unsigned int cmd, default: return -EINVAL; } - save_flags(flags); cli(); + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (old != info->mctrl) info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); return 0; } @@ -1283,14 +1257,15 @@ static void siccuart_break_ctl(struct tty_struct *tty, int break_state) unsigned int lcr_h; - save_flags(flags); cli(); + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); lcr_h = readb(info->port + BL_SICC_LSR); if (break_state == -1) lcr_h |= _LSR_LB_MASK; else lcr_h &= ~_LSR_LB_MASK; writeb(lcr_h, info->port + BL_SICC_LSRS); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } static int siccuart_ioctl(struct tty_struct *tty, struct file *file, @@ -1338,9 +1313,10 @@ static int siccuart_ioctl(struct tty_struct *tty, struct file *file, * RI where only 0->1 is counted. */ case TIOCGICOUNT: - save_flags(flags); cli(); + /* disable interrupts while getting interrupt count */ + spin_lock_irqsave(&info->state->sicc_lock,flags); cnow = info->state->icount; - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; @@ -1377,22 +1353,24 @@ static void siccuart_set_termios(struct tty_struct *tty, struct termios *old_ter /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { - save_flags(flags); cli(); + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { - save_flags(flags); cli(); + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl |= TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) info->mctrl |= TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); } /* Handle turning off CRTSCTS */ @@ -1428,12 +1406,11 @@ static void siccuart_close(struct tty_struct *tty, struct file *filp) //pr_debug("siccuart_close() called\n"); - save_flags(flags); cli(); + /* lock tty->driver_data while closing port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + goto quick_close; } if ((tty->count == 1) && (state->count != 1)) { @@ -1452,12 +1429,10 @@ static void siccuart_close(struct tty_struct *tty, struct file *filp) state->count = 0; } if (state->count) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + goto quick_close; } info->flags |= ASYNC_CLOSING; - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -1487,15 +1462,17 @@ static void siccuart_close(struct tty_struct *tty, struct file *filp) info->event = 0; info->tty = NULL; if (info->blocked_open) { - if (info->state->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->state->close_delay); - } + if (info->state->close_delay) + schedule_timeout_interruptible(info->state->close_delay); wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); - MOD_DEC_USE_COUNT; + return; + +quick_close: + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return; } static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout) @@ -1514,7 +1491,7 @@ static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout) * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ - char_time = (info->timeout - HZ/50) / info->port->fifosize; + char_time = (info->timeout - msecs_to_jiffies(20)) / info->port->fifosize; char_time = char_time / 5; if (char_time == 0) char_time = 1; @@ -1539,8 +1516,7 @@ static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout) tty->index, jiffies, expire, char_time); while ((readb(info->port->uart_base + BL_SICC_LSR) & _LSR_TX_ALL) != _LSR_TX_ALL) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); + schedule_timeout_interruptible(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, expire)) @@ -1607,20 +1583,22 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, */ retval = 0; add_wait_queue(&info->open_wait, &wait); - save_flags(flags); cli(); + /* lock while decrementing state->count */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); info->blocked_open++; while (1) { - save_flags(flags); cli(); + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); if (tty->termios->c_cflag & CBAUD) { info->mctrl = TIOCM_DTR | TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); } - restore_flags(flags); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { @@ -1658,9 +1636,8 @@ static struct SICC_info *siccuart_get(int line) state->count++; if (state->info) return state->info; - info = kmalloc(sizeof(struct SICC_info), GFP_KERNEL); + info = kzalloc(sizeof(struct SICC_info), GFP_KERNEL); if (info) { - memset(info, 0, sizeof(struct SICC_info)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); @@ -1685,9 +1662,7 @@ static int siccuart_open(struct tty_struct *tty, struct file *filp) // is this a line that we've got? - MOD_INC_USE_COUNT; if (line >= SERIAL_SICC_NR) { - MOD_DEC_USE_COUNT; return -ENODEV; } @@ -1707,7 +1682,6 @@ static int siccuart_open(struct tty_struct *tty, struct file *filp) if (tmp_buf) free_page(page); else if (!page) { - MOD_DEC_USE_COUNT; return -ENOMEM; } tmp_buf = (u_char *)page; @@ -1720,7 +1694,6 @@ static int siccuart_open(struct tty_struct *tty, struct file *filp) (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); - MOD_DEC_USE_COUNT; return -EAGAIN; } @@ -1729,13 +1702,11 @@ static int siccuart_open(struct tty_struct *tty, struct file *filp) */ retval = siccuart_startup(info); if (retval) { - MOD_DEC_USE_COUNT; return retval; } retval = block_til_ready(tty, filp, info); if (retval) { - MOD_DEC_USE_COUNT; return retval; } @@ -1749,7 +1720,7 @@ static int siccuart_open(struct tty_struct *tty, struct file *filp) return 0; } -static struct tty_operations sicc_ops = { +static const struct tty_operations sicc_ops = { .open = siccuart_open, .close = siccuart_close, .write = siccuart_write, @@ -1778,6 +1749,7 @@ int __init siccuart_init(void) return -ENOMEM; printk("IBM Vesta SICC serial port driver V 0.1 by Yudong Yang and Yi Ge / IBM CRL .\n"); siccnormal_driver->driver_name = "serial_sicc"; + siccnormal_driver->owner = THIS_MODULE; siccnormal_driver->name = SERIAL_SICC_NAME; siccnormal_driver->major = SERIAL_SICC_MAJOR; siccnormal_driver->minor_start = SERIAL_SICC_MINOR; @@ -1785,7 +1757,7 @@ int __init siccuart_init(void) siccnormal_driver->subtype = SERIAL_TYPE_NORMAL; siccnormal_driver->init_termios = tty_std_termios; siccnormal_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - siccnormal_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + siccnormal_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(siccnormal_driver, &sicc_ops); if (tty_register_driver(siccnormal_driver)) @@ -1794,8 +1766,9 @@ int __init siccuart_init(void) for (i = 0; i < SERIAL_SICC_NR; i++) { struct SICC_state *state = sicc_state + i; state->line = i; - state->close_delay = 5 * HZ / 10; + state->close_delay = msecs_to_jiffies(500); state->closing_wait = 30 * HZ; + spin_lock_init(&state->sicc_lock); }