Skip to content

Commit

Permalink
[SUNSAB]: Defer register updates until transmitter is idle.
Browse files Browse the repository at this point in the history
The chip can emit garbage characters if we touch the
settings while characters are going out.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed May 11, 2005
1 parent 88d7bd8 commit e4fdee8
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 38 deletions.
109 changes: 71 additions & 38 deletions drivers/serial/sunsab.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ struct uart_sunsab_port {
unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */
unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */
int type; /* SAB82532 version */

/* Setting configuration bits while the transmitter is active
* can cause garbage characters to get emitted by the chip.
* Therefore, we cache such writes here and do the real register
* write the next time the transmitter becomes idle.
*/
unsigned int cached_ebrg;
unsigned char cached_mode;
unsigned char cached_pvr;
unsigned char cached_dafo;
};

/*
Expand Down Expand Up @@ -236,6 +246,7 @@ receive_chars(struct uart_sunsab_port *up,
}

static void sunsab_stop_tx(struct uart_port *, unsigned int);
static void sunsab_tx_idle(struct uart_sunsab_port *);

static void transmit_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
Expand All @@ -258,6 +269,7 @@ static void transmit_chars(struct uart_sunsab_port *up,
return;

set_bit(SAB82532_XPR, &up->irqflags);
sunsab_tx_idle(up);

if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
up->interrupt_mask1 |= SAB82532_IMR1_XPR;
Expand Down Expand Up @@ -397,21 +409,21 @@ static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl)
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;

if (mctrl & TIOCM_RTS) {
writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
up->cached_mode &= ~SAB82532_MODE_FRTS;
up->cached_mode |= SAB82532_MODE_RTS;
} else {
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
up->cached_mode |= (SAB82532_MODE_FRTS |
SAB82532_MODE_RTS);
}
if (mctrl & TIOCM_DTR) {
writeb(readb(&up->regs->rw.pvr) & ~(up->pvr_dtr_bit), &up->regs->rw.pvr);
up->cached_pvr &= ~(up->pvr_dtr_bit);
} else {
writeb(readb(&up->regs->rw.pvr) | up->pvr_dtr_bit, &up->regs->rw.pvr);
up->cached_pvr |= up->pvr_dtr_bit;
}

set_bit(SAB82532_REGS_PENDING, &up->irqflags);
if (test_bit(SAB82532_XPR, &up->irqflags))
sunsab_tx_idle(up);
}

/* port->lock is not held. */
Expand Down Expand Up @@ -449,6 +461,25 @@ static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop)
writeb(up->interrupt_mask1, &up->regs->w.imr1);
}

/* port->lock held by caller. */
static void sunsab_tx_idle(struct uart_sunsab_port *up)
{
if (test_bit(SAB82532_REGS_PENDING, &up->irqflags)) {
u8 tmp;

clear_bit(SAB82532_REGS_PENDING, &up->irqflags);
writeb(up->cached_mode, &up->regs->rw.mode);
writeb(up->cached_pvr, &up->regs->rw.pvr);
writeb(up->cached_dafo, &up->regs->w.dafo);

writeb(up->cached_ebrg & 0xff, &up->regs->w.bgr);
tmp = readb(&up->regs->rw.ccr2);
tmp &= ~0xc0;
tmp |= (up->cached_ebrg >> 2) & 0xc0;
writeb(tmp, &up->regs->rw.ccr2);
}
}

/* port->lock held by caller. */
static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start)
{
Expand Down Expand Up @@ -517,12 +548,16 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state)

spin_lock_irqsave(&up->port.lock, flags);

val = readb(&up->regs->rw.dafo);
val = up->cached_dafo;
if (break_state)
val |= SAB82532_DAFO_XBRK;
else
val &= ~SAB82532_DAFO_XBRK;
writeb(val, &up->regs->rw.dafo);
up->cached_dafo = val;

set_bit(SAB82532_REGS_PENDING, &up->irqflags);
if (test_bit(SAB82532_XPR, &up->irqflags))
sunsab_tx_idle(up);

spin_unlock_irqrestore(&up->port.lock, flags);
}
Expand Down Expand Up @@ -566,8 +601,9 @@ static int sunsab_startup(struct uart_port *port)
SAB82532_CCR2_TOE, &up->regs->w.ccr2);
writeb(0, &up->regs->w.ccr3);
writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);
writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
SAB82532_MODE_RAC, &up->regs->w.mode);
up->cached_mode = (SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
SAB82532_MODE_RAC);
writeb(up->cached_mode, &up->regs->w.mode);
writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc);

tmp = readb(&up->regs->rw.ccr0);
Expand Down Expand Up @@ -598,7 +634,6 @@ static void sunsab_shutdown(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
unsigned char tmp;

spin_lock_irqsave(&up->port.lock, flags);

Expand All @@ -609,14 +644,13 @@ static void sunsab_shutdown(struct uart_port *port)
writeb(up->interrupt_mask1, &up->regs->w.imr1);

/* Disable break condition */
tmp = readb(&up->regs->rw.dafo);
tmp &= ~SAB82532_DAFO_XBRK;
writeb(tmp, &up->regs->rw.dafo);
up->cached_dafo = readb(&up->regs->rw.dafo);
up->cached_dafo &= ~SAB82532_DAFO_XBRK;
writeb(up->cached_dafo, &up->regs->rw.dafo);

/* Disable Receiver */
tmp = readb(&up->regs->rw.mode);
tmp &= ~SAB82532_MODE_RAC;
writeb(tmp, &up->regs->rw.mode);
up->cached_mode &= ~SAB82532_MODE_RAC;
writeb(up->cached_mode, &up->regs->rw.mode);

/*
* XXX FIXME
Expand Down Expand Up @@ -685,7 +719,6 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla
unsigned int iflag, unsigned int baud,
unsigned int quot)
{
unsigned int ebrg;
unsigned char dafo;
int bits, n, m;

Expand Down Expand Up @@ -714,10 +747,11 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla
} else {
dafo |= SAB82532_DAFO_PAR_EVEN;
}
up->cached_dafo = dafo;

calc_ebrg(baud, &n, &m);

ebrg = n | (m << 6);
up->cached_ebrg = n | (m << 6);

up->tec_timeout = (10 * 1000000) / baud;
up->cec_timeout = up->tec_timeout >> 2;
Expand Down Expand Up @@ -770,16 +804,13 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla
uart_update_timeout(&up->port, cflag,
(up->port.uartclk / (16 * quot)));

/* Now bang the new settings into the chip. */
sunsab_cec_wait(up);
sunsab_tec_wait(up);
writeb(dafo, &up->regs->w.dafo);
writeb(ebrg & 0xff, &up->regs->w.bgr);
writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0),
&up->regs->rw.ccr2);

writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);

/* Now schedule a register update when the chip's
* transmitter is idle.
*/
up->cached_mode |= SAB82532_MODE_RAC;
set_bit(SAB82532_REGS_PENDING, &up->irqflags);
if (test_bit(SAB82532_XPR, &up->irqflags))
sunsab_tx_idle(up);
}

/* port->lock is not held. */
Expand Down Expand Up @@ -1084,11 +1115,13 @@ static void __init sunsab_init_hw(void)
up->pvr_dsr_bit = (1 << 3);
up->pvr_dtr_bit = (1 << 2);
}
writeb((1 << 1) | (1 << 2) | (1 << 4), &up->regs->w.pvr);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);
writeb(up->cached_pvr, &up->regs->w.pvr);
up->cached_mode = readb(&up->regs->rw.mode);
up->cached_mode |= SAB82532_MODE_FRTS;
writeb(up->cached_mode, &up->regs->rw.mode);
up->cached_mode |= SAB82532_MODE_RTS;
writeb(up->cached_mode, &up->regs->rw.mode);

up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
Expand Down
1 change: 1 addition & 0 deletions drivers/serial/sunsab.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ union sab82532_irq_status {
/* irqflags bits */
#define SAB82532_ALLS 0x00000001
#define SAB82532_XPR 0x00000002
#define SAB82532_REGS_PENDING 0x00000004

/* RFIFO Status Byte */
#define SAB82532_RSTAT_PE 0x80
Expand Down

0 comments on commit e4fdee8

Please sign in to comment.