Commit b1e16506 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] SMP-safe macserial driver

The patch below removes the uses of save_flags/restore_flags/cli
etc. from the macserial driver and replaces them with a spinlock.
parent c2970ed3
......@@ -441,12 +441,8 @@ static _INLINE_ void receive_chars(struct mac_serial *info,
static void transmit_chars(struct mac_serial *info)
{
unsigned long flags;
save_flags(flags);
cli();
if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)
goto out;
return;
info->tx_active = 0;
if (info->x_char && !info->power_wait) {
......@@ -454,13 +450,13 @@ static void transmit_chars(struct mac_serial *info)
write_zsdata(info->zs_channel, info->x_char);
info->x_char = 0;
info->tx_active = 1;
goto out;
return;
}
if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped
|| info->power_wait) {
write_zsreg(info->zs_channel, 0, RES_Tx_P);
goto out;
return;
}
/* Send char */
......@@ -471,17 +467,17 @@ static void transmit_chars(struct mac_serial *info)
if (info->xmit_cnt < WAKEUP_CHARS)
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
out:
restore_flags(flags);
}
static void powerup_done(unsigned long data)
{
struct mac_serial *info = (struct mac_serial *) data;
unsigned long flags;
spin_lock_irqsave(&info->lock, flags);
info->power_wait = 0;
transmit_chars(info);
spin_unlock_irqrestore(&info->lock, flags);
}
static _INLINE_ void status_handle(struct mac_serial *info)
......@@ -567,6 +563,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
struct mac_serial *info = (struct mac_serial *) dev_id;
unsigned char zs_intreg;
int shift;
unsigned long flags;
if (!(info->flags & ZILOG_INITIALIZED)) {
printk(KERN_WARNING "rs_interrupt: irq %d, port not "
......@@ -588,6 +585,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
else
shift = 0; /* Channel B */
spin_lock_irqsave(&info->lock, flags);
for (;;) {
zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
#ifdef SERIAL_DEBUG_INTR
......@@ -611,6 +609,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
if (zs_intreg & CHBEXT)
status_handle(info);
}
spin_unlock_irqrestore(&info->lock, flags);
}
/* Transmit DMA interrupt - not used at present */
......@@ -672,13 +671,13 @@ static void rs_stop(struct tty_struct *tty)
return;
#if 0
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
if (info->curregs[5] & TxENAB) {
info->curregs[5] &= ~TxENAB;
info->pendregs[5] &= ~TxENAB;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
}
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
#endif
}
......@@ -695,7 +694,7 @@ static void rs_start(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->device, "rs_start"))
return;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
#if 0
if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
info->curregs[5] |= TxENAB;
......@@ -707,7 +706,7 @@ static void rs_start(struct tty_struct *tty)
transmit_chars(info);
}
#endif
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
static void do_softint(void *private_)
......@@ -754,12 +753,11 @@ static int startup(struct mac_serial * info)
unsigned long flags;
/* delay is in ms */
save_flags(flags);
cli();
spin_lock_irqsave(&info->lock, flags);
info->power_wait = 1;
mod_timer(&info->powerup_timer,
jiffies + (delay * HZ + 999) / 1000);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq);
......@@ -1019,7 +1017,7 @@ static int setup_scc(struct mac_serial * info)
OPNDBG("setting up ttyS%d SCC...\n", info->line);
save_flags(flags); cli(); /* Disable interrupts */
spin_lock_irqsave(&info->lock, flags);
/* Nice buggy HW ... */
fix_zero_bug_scc(info);
......@@ -1091,6 +1089,8 @@ static int setup_scc(struct mac_serial * info)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
spin_unlock_irqrestore(&info->lock, flags);
/*
* Set the speed of the serial port
*/
......@@ -1099,8 +1099,6 @@ static int setup_scc(struct mac_serial * info)
/* Save the current value of RR0 */
info->read_reg_zero = read_zsreg(info->zs_channel, 0);
restore_flags(flags);
if (info->dma_initted) {
spin_lock_irqsave(&info->rx_dma_lock, flags);
rxdma_start(info, 0);
......@@ -1214,10 +1212,7 @@ static int set_scc_power(struct mac_serial * info, int state)
static void irda_rts_pulses(struct mac_serial *info, int w)
{
unsigned long flags;
udelay(w);
save_flags(flags); cli();
write_zsreg(info->zs_channel, 5, Tx8 | TxENAB);
udelay(2);
write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
......@@ -1225,7 +1220,6 @@ static void irda_rts_pulses(struct mac_serial *info, int w)
write_zsreg(info->zs_channel, 5, Tx8 | TxENAB);
udelay(4);
write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
restore_flags(flags);
}
/*
......@@ -1234,7 +1228,6 @@ static void irda_rts_pulses(struct mac_serial *info, int w)
static void irda_setup(struct mac_serial *info)
{
int code, speed, t;
unsigned long flags;
speed = info->tty->termios->c_cflag & CBAUD;
if (speed < B2400 || speed > B115200)
......@@ -1268,10 +1261,8 @@ static void irda_setup(struct mac_serial *info)
/* set TxD low for ~104us and pulse RTS */
udelay(1000);
save_flags(flags); cli();
write_zsdata(info->zs_channel, 0xfe);
irda_rts_pulses(info, 150);
restore_flags(flags);
irda_rts_pulses(info, 180);
irda_rts_pulses(info, 50);
udelay(100);
......@@ -1351,7 +1342,7 @@ static void change_speed(struct mac_serial *info, struct termios *old_termios)
else if (baud == 0)
baud = 38400;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->zs_baud = baud;
info->clk_divisor = 16;
......@@ -1460,22 +1451,23 @@ static void change_speed(struct mac_serial *info, struct termios *old_termios)
/* Load up the new values */
load_zsregs(info->zs_channel, info->curregs);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_flush_chars(struct tty_struct *tty)
{
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
return;
if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
!info->xmit_buf)
return;
/* Enable transmitter */
transmit_chars(info);
spin_lock_irqsave(&info->lock, flags);
if (!(info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
!info->xmit_buf))
/* Enable transmitter */
transmit_chars(info);
spin_unlock_irqrestore(&info->lock, flags);
}
static int rs_write(struct tty_struct * tty, int from_user,
......@@ -1506,15 +1498,14 @@ static int rs_write(struct tty_struct * tty, int from_user,
ret = -EFAULT;
break;
}
save_flags(flags);
cli();
spin_lock_irqsave(&info->lock, flags);
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
info->xmit_head = ((info->xmit_head + c) &
(SERIAL_XMIT_SIZE-1));
info->xmit_cnt += c;
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
buf += c;
count -= c;
ret += c;
......@@ -1522,28 +1513,29 @@ static int rs_write(struct tty_struct * tty, int from_user,
up(&tmp_buf_sem);
} else {
while (1) {
save_flags(flags);
cli();
spin_lock_irqsave(&info->lock, flags);
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0) {
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
break;
}
memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head = ((info->xmit_head + c) &
(SERIAL_XMIT_SIZE-1));
info->xmit_cnt += c;
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
buf += c;
count -= c;
ret += c;
}
}
spin_lock_irqsave(&info->lock, flags);
if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
&& !info->tx_active)
transmit_chars(info);
spin_unlock_irqrestore(&info->lock, flags);
return ret;
}
......@@ -1576,9 +1568,9 @@ static void rs_flush_buffer(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
return;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
......@@ -1605,11 +1597,11 @@ static void rs_throttle(struct tty_struct * tty)
return;
if (I_IXOFF(tty)) {
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->x_char = STOP_CHAR(tty);
if (!info->tx_active)
transmit_chars(info);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
if (C_CRTSCTS(tty)) {
......@@ -1627,21 +1619,21 @@ static void rs_throttle(struct tty_struct * tty)
* drop RTS.
*/
if (info->is_internal_modem) {
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->curregs[5] &= ~RTS;
info->pendregs[5] &= ~RTS;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
}
#ifdef CDTRCTS
if (tty->termios->c_cflag & CDTRCTS) {
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->curregs[5] &= ~DTR;
info->pendregs[5] &= ~DTR;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
#endif /* CDTRCTS */
}
......@@ -1659,7 +1651,7 @@ static void rs_unthrottle(struct tty_struct * tty)
return;
if (I_IXOFF(tty)) {
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
if (info->x_char)
info->x_char = 0;
else {
......@@ -1667,26 +1659,26 @@ static void rs_unthrottle(struct tty_struct * tty)
if (!info->tx_active)
transmit_chars(info);
}
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
if (C_CRTSCTS(tty) && info->is_internal_modem) {
/* Assert RTS line */
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->curregs[5] |= RTS;
info->pendregs[5] |= RTS;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
#ifdef CDTRCTS
if (tty->termios->c_cflag & CDTRCTS) {
/* Assert DTR line */
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
info->curregs[5] |= DTR;
info->pendregs[5] |= DTR;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
#endif
}
......@@ -1779,9 +1771,9 @@ static int get_lsr_info(struct mac_serial * info, unsigned int *value)
unsigned char status;
unsigned long flags;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
status = read_zsreg(info->zs_channel, 0);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
status = (status & Tx_BUF_EMP)? TIOCSER_TEMT: 0;
return put_user(status,value);
}
......@@ -1792,10 +1784,10 @@ static int get_modem_info(struct mac_serial *info, unsigned int *value)
unsigned int result;
unsigned long flags;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
control = info->curregs[5];
status = read_zsreg(info->zs_channel, 0);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
result = ((control & RTS) ? TIOCM_RTS: 0)
| ((control & DTR) ? TIOCM_DTR: 0)
| ((status & DCD) ? TIOCM_CAR: 0)
......@@ -1812,7 +1804,7 @@ static int set_modem_info(struct mac_serial *info, unsigned int cmd,
if (get_user(arg, value))
return -EFAULT;
bits = (arg & TIOCM_RTS? RTS: 0) + (arg & TIOCM_DTR? DTR: 0);
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
switch (cmd) {
case TIOCMBIS:
info->curregs[5] |= bits;
......@@ -1824,12 +1816,12 @@ static int set_modem_info(struct mac_serial *info, unsigned int cmd,
info->curregs[5] = (info->curregs[5] & ~(DTR | RTS)) | bits;
break;
default:
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
return -EINVAL;
}
info->pendregs[5] = info->curregs[5];
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
......@@ -1844,13 +1836,13 @@ static void rs_break(struct tty_struct *tty, int break_state)
if (serial_paranoia_check(info, tty->device, "rs_break"))
return;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
if (break_state == -1)
info->curregs[5] |= SND_BRK;
else
info->curregs[5] &= ~SND_BRK;
write_zsreg(info->zs_channel, 5, info->curregs[5]);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
......@@ -1932,11 +1924,11 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
return;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
if (tty_hung_up_p(filp)) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
return;
}
......@@ -1960,7 +1952,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
}
if (info->count) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
return;
}
info->flags |= ZILOG_CLOSING;
......@@ -1979,9 +1971,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
OPNDBG("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
tty->closing = 1;
if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
tty_wait_until_sent(tty, info->closing_wait);
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
}
/*
......@@ -2001,15 +1993,15 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
* has completely drained.
*/
OPNDBG("waiting end of Rx...\n");
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
rs_wait_until_sent(tty, info->timeout);
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
}
shutdown(info);
/* restore flags now since shutdown() will have disabled this port's
specific irqs */
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
......@@ -2174,18 +2166,18 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
add_wait_queue(&info->open_wait, &wait);
OPNDBG("block_til_ready before block: ttyS%d, count = %d\n",
info->line, info->count);
cli();
spin_lock_irq(&info->lock);
if (!tty_hung_up_p(filp))
info->count--;
sti();
spin_unlock_irq(&info->lock);
info->blocked_open++;
while (1) {
cli();
spin_lock_irq(&info->lock);
if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
(tty->termios->c_cflag & CBAUD) &&
!info->is_irda)
zs_rtsdtr(info, 1);
sti();
spin_unlock_irq(&info->lock);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ZILOG_INITIALIZED)) {
......@@ -2553,7 +2545,6 @@ probe_sccs()
int macserial_init(void)
{
int channel, i;
unsigned long flags;
struct mac_serial *info;
/* Find out how many Z8530 SCCs we have */
......@@ -2570,7 +2561,6 @@ int macserial_init(void)
* We also request the OF resources here as probe_sccs()
* might be called too early for that
*/
save_flags(flags); cli();
for (i = 0; i < zs_channels_found; ++i) {
struct device_node* ch = zs_soft[i].dev_node;
if (!request_OF_resource(ch, 0, NULL)) {
......@@ -2607,7 +2597,6 @@ int macserial_init(void)
zs_soft[i].irq);
disable_irq(zs_soft[i].irq);
}
restore_flags(flags);
show_serial_version();
......@@ -2723,6 +2712,7 @@ int macserial_init(void)
info->count = 0;
info->blocked_open = 0;
INIT_WORK(&info->tqueue, do_softint, info);
spin_lock_init(&info->lock);
info->callout_termios = callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
init_waitqueue_head(&info->open_wait);
......@@ -2753,7 +2743,7 @@ void macserial_cleanup(void)
for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
set_scc_power(info, 0);
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
for (i = 0; i < zs_channels_found; ++i) {
free_irq(zs_soft[i].irq, &zs_soft[i]);
if (zs_soft[i].has_dma) {
......@@ -2767,7 +2757,7 @@ void macserial_cleanup(void)
release_OF_resource(ch, ch->n_addrs - 1);
}
}
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
tty_unregister_driver(&callout_driver);
tty_unregister_driver(&serial_driver);
......@@ -2944,7 +2934,7 @@ static int __init serial_console_setup(struct console *co, char *options)
}
co->cflag = cflag;
save_flags(flags); cli();
spin_lock_irqsave(&info->lock, flags);
memset(info->curregs, 0, sizeof(info->curregs));
info->zs_baud = baud;
......@@ -3026,7 +3016,7 @@ static int __init serial_console_setup(struct console *co, char *options)
/* Load up the new values */
load_zsregs(info->zs_channel, info->curregs);
restore_flags(flags);
spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
......
......@@ -9,6 +9,8 @@
#ifndef _MACSERIAL_H
#define _MACSERIAL_H
#include <linux/spinlock.h>
#define NUM_ZSREGS 16
struct serial_struct {
......@@ -105,6 +107,7 @@ struct mac_serial {
struct mac_zschannel *zs_chan_a; /* A side registers */
unsigned char read_reg_zero;
struct device_node* dev_node;
spinlock_t lock;
char soft_carrier; /* Use soft carrier on this channel */
char break_abort; /* Is serial console in, so process brk/abrt */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment