struct uart_sunsab_port {
struct uart_port port; /* Generic UART port */
- union sab82532_async_regs *regs; /* Chip registers */
+ union sab82532_async_regs __iomem *regs; /* Chip registers */
unsigned long irqflags; /* IRQ state flags */
int dsr; /* Current DSR state */
unsigned int cec_timeout; /* Chip poll timeout... */
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;
};
/*
writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);
}
+ /* Count may be zero for BRK, so we check for it here */
+ if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) &&
+ (up->port.line == up->port.cons->index))
+ saw_console_brk = 1;
+
for (i = 0; i < count; i++) {
- unsigned char ch = buf[i];
+ unsigned char ch = buf[i], flag;
if (tty == NULL) {
uart_handle_sysrq_char(&up->port, ch, regs);
continue;
}
- if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
- tty->flip.work.func((void *)tty);
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- return tty; // if TTY_DONT_FLIP is set
- }
-
- *tty->flip.char_buf_ptr = ch;
- *tty->flip.flag_buf_ptr = TTY_NORMAL;
+ flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |
stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR);
up->port.icount.brk++;
- if (up->port.line == up->port.cons->index)
- saw_console_brk = 1;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);
if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
- *tty->flip.flag_buf_ptr = TTY_BREAK;
+ flag = TTY_BREAK;
} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
- *tty->flip.flag_buf_ptr = TTY_PARITY;
+ flag = TTY_PARITY;
else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
- *tty->flip.flag_buf_ptr = TTY_FRAME;
+ flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch, regs))
continue;
if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
- (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0){
- tty->flip.flag_buf_ptr++;
- tty->flip.char_buf_ptr++;
- tty->flip.count++;
- }
- if ((stat->sreg.isr0 & SAB82532_ISR0_RFO) &&
- tty->flip.count < TTY_FLIPBUF_SIZE) {
- /*
- * Overrun is special, since it's reported
- * immediately, and doesn't affect the current
- * character.
- */
- *tty->flip.flag_buf_ptr = TTY_OVERRUN;
- tty->flip.flag_buf_ptr++;
- tty->flip.char_buf_ptr++;
- tty->flip.count++;
- }
+ (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+ if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
if (saw_console_brk)
return tty;
}
-static void sunsab_stop_tx(struct uart_port *, unsigned int);
+static void sunsab_stop_tx(struct uart_port *);
+static void sunsab_tx_idle(struct uart_sunsab_port *);
static void transmit_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
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;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
- uart_write_wakeup(&up->port);
return;
}
uart_write_wakeup(&up->port);
if (uart_circ_empty(xmit))
- sunsab_stop_tx(&up->port, 0);
+ sunsab_stop_tx(&up->port);
}
static void check_status(struct uart_sunsab_port *up,
tty = NULL;
if (status.stat) {
- if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
- SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
+ (status.sreg.isr1 & SAB82532_ISR1_BRK))
tty = receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & SAB82532_ISR1_CSC))
tty = NULL;
if (status.stat) {
- if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
- SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+ if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
+ (status.sreg.isr1 & SAB82532_ISR1_BRK))
+
tty = receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
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. */
+/* port->lock is held by caller and interrupts are disabled. */
static unsigned int sunsab_get_mctrl(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
- unsigned long flags;
unsigned char val;
unsigned int result;
result = 0;
- spin_lock_irqsave(&up->port.lock, flags);
-
val = readb(&up->regs->r.pvr);
result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR;
val = readb(&up->regs->r.star);
result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0;
- spin_unlock_irqrestore(&up->port.lock, flags);
-
return result;
}
/* port->lock held by caller. */
-static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static void sunsab_stop_tx(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
}
/* port->lock held by caller. */
-static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start)
+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)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
struct circ_buf *xmit = &up->port.info->xmit;
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);
}
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);
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
- unsigned char tmp;
spin_lock_irqsave(&up->port.lock, flags);
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
/* Internal routine, port->lock is held and local interrupts are disabled. */
static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag,
- unsigned int iflag, int baud)
+ unsigned int iflag, unsigned int baud,
+ unsigned int quot)
{
- unsigned int ebrg;
unsigned char dafo;
int bits, n, m;
} 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;
up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |
SAB82532_ISR0_TCD);
- /* 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);
-
- if (cflag & CRTSCTS) {
- writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_RTS,
- &up->regs->rw.mode);
- writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
- &up->regs->rw.mode);
- writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FCTS,
- &up->regs->rw.mode);
- up->interrupt_mask1 &= ~SAB82532_IMR1_CSC;
- writeb(up->interrupt_mask1, &up->regs->w.imr1);
- } else {
- writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
- &up->regs->rw.mode);
- writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
- &up->regs->rw.mode);
- writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FCTS,
- &up->regs->rw.mode);
- up->interrupt_mask1 |= SAB82532_IMR1_CSC;
- writeb(up->interrupt_mask1, &up->regs->w.imr1);
- }
- writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);
+ uart_update_timeout(&up->port, cflag,
+ (up->port.uartclk / (16 * quot)));
+ /* 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. */
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
- int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ unsigned int quot = uart_get_divisor(port, baud);
spin_lock_irqsave(&up->port.lock, flags);
- sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud);
+ sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot);
spin_unlock_irqrestore(&up->port.lock, flags);
}
{
struct uart_sunsab_port *up = &sunsab_ports[con->index];
unsigned long flags;
- int baud;
+ unsigned int baud, quot;
printk("Console: ttyS%d (SAB82532)\n",
(sunsab_reg.minor - 64) + con->index);
sunserial_console_termios(con);
- /* Firmware console speed is limited to 150-->38400 baud so
- * this hackish cflag thing is OK.
- */
switch (con->cflag & CBAUD) {
case B150: baud = 150; break;
case B300: baud = 300; break;
default: case B9600: baud = 9600; break;
case B19200: baud = 19200; break;
case B38400: baud = 38400; break;
+ case B57600: baud = 57600; break;
+ case B115200: baud = 115200; break;
+ case B230400: baud = 230400; break;
+ case B460800: baud = 460800; break;
};
/*
SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
- sunsab_convert_to_sab(up, con->cflag, 0, baud);
+ quot = uart_get_divisor(&up->port, baud);
+ sunsab_convert_to_sab(up, con->cflag, 0, baud, quot);
+ sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
spin_unlock_irqrestore(&up->port.lock, flags);
up->port.irq = edev->irqs[0];
up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;
up->port.mapbase = (unsigned long)up->regs;
- up->port.iotype = SERIAL_IO_MEM;
+ up->port.iotype = UPIO_MEM;
writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
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;
sunserial_current_minor += num_channels;
+ sunsab_console_init();
+
for (i = 0; i < num_channels; i++) {
struct uart_sunsab_port *up = &sunsab_ports[i];
uart_add_one_port(&sunsab_reg, &up->port);
}
- sunsab_console_init();
-
return 0;
}