/*
- * arch/ppc/4xx_io/serial_sicc.c
- *
* Driver for IBM STB3xxx SICC serial port
*
* Based on drivers/char/serial_amba.c, by ARM Ltd.
* is compatible with normal ttyS* devices.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/capability.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
+#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
-#include <asm/bitops.h>
#include <asm/serial.h>
* 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)
unsigned int flags;
int count;
struct SICC_info *info;
+ spinlock_t sicc_lock;
};
#define SICC_XMIT_SIZE 1024
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)
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);
}
}
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;
#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;
}
}
-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);
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);
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;
}
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
info->flags &= ~ASYNC_INITIALIZED;
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
}
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);
/*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
/* Set baud rate */
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)
|| !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;
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;
}
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)
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);
}
}
}
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);
}
}
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;
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;
/*
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;
}
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,
* 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;
/* 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 */
//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)) {
- restore_flags(flags);
- return;
+ goto quick_close;
}
if ((tty->count == 1) && (state->count != 1)) {
state->count = 0;
}
if (state->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.
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);
+ return;
+
+quick_close:
+ spin_unlock_irqrestore(&info->state->sicc_lock,flags);
+ return;
}
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;
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))
*/
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)) {
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);
return 0;
}
-static struct tty_operations sicc_ops = {
+static const struct tty_operations sicc_ops = {
.open = siccuart_open,
.close = siccuart_close,
.write = siccuart_write,
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))
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);
}