static void bcm_uart_do_rx(struct uart_port *port) { struct tty_struct *tty; unsigned int max_count; max_count = 32; tty = port->state->port.tty; do { unsigned int iestat, c, cstat; char flag; iestat = bcm_uart_readl(port, UART_IR_REG); if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) break; cstat = c = bcm_uart_readl(port, UART_FIFO_REG); port->icount.rx++; flag = TTY_NORMAL; c &= 0xff; if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { if (cstat & UART_FIFO_BRKDET_MASK) { port->icount.brk++; if (uart_handle_break(port)) continue; } if (cstat & UART_FIFO_PARERR_MASK) port->icount.parity++; if (cstat & UART_FIFO_FRAMEERR_MASK) port->icount.frame++; cstat &= port->read_status_mask; if (cstat & UART_FIFO_BRKDET_MASK) flag = TTY_BREAK; if (cstat & UART_FIFO_FRAMEERR_MASK) flag = TTY_FRAME; if (cstat & UART_FIFO_PARERR_MASK) flag = TTY_PARITY; } if (uart_handle_sysrq_char(port, c)) continue; if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { port->icount.overrun++; tty_insert_flip_char(tty, 0, TTY_OVERRUN); } if ((cstat & port->ignore_status_mask) == 0) tty_insert_flip_char(tty, c, flag); } while (--max_count); tty_flip_buffer_push(tty); }
/* * process uart interrupt */ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) { struct uart_port *port; unsigned int irqstat; port = dev_id; spin_lock(&port->lock); irqstat = bcm_uart_readl(port, UART_IR_REG); if (irqstat & UART_RX_INT_STAT) bcm_uart_do_rx(port); if (irqstat & UART_TX_INT_STAT) bcm_uart_do_tx(port); if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { unsigned int estat; estat = bcm_uart_readl(port, UART_EXTINP_REG); if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) uart_handle_cts_change(port, estat & UART_EXTINP_CTS_MASK); if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) uart_handle_dcd_change(port, estat & UART_EXTINP_DCD_MASK); } spin_unlock(&port->lock); return IRQ_HANDLED; }
static void bcm_uart_flush(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_CTL_REG); val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; bcm_uart_writel(port, val, UART_CTL_REG); (void)bcm_uart_readl(port, UART_FIFO_REG); }
/* * serial core request to (re)enable tx */ static void bcm_uart_start_tx(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_IR_REG); val |= UART_TX_INT_MASK; bcm_uart_writel(port, val, UART_IR_REG); val = bcm_uart_readl(port, UART_CTL_REG); val |= UART_CTL_TXEN_MASK; bcm_uart_writel(port, val, UART_CTL_REG); }
/* * clear all unread data in rx fifo and unsent data in tx fifo */ static void bcm_uart_flush(struct uart_port *port) { unsigned int val; /* empty rx and tx fifo */ val = bcm_uart_readl(port, UART_CTL_REG); val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; bcm_uart_writel(port, val, UART_CTL_REG); /* read any pending char to make sure all irq status are * cleared */ (void)bcm_uart_readl(port, UART_FIFO_REG); }
/* * fill tx fifo with chars to send, stop when fifo is about to be full * or when all chars have been sent. */ static void bcm_uart_do_tx(struct uart_port *port) { struct circ_buf *xmit; unsigned int val, max_count; if (port->x_char) { bcm_uart_writel(port, port->x_char, UART_FIFO_REG); port->icount.tx++; port->x_char = 0; return; } if (uart_tx_stopped(port)) { bcm_uart_stop_tx(port); return; } xmit = &port->state->xmit; if (uart_circ_empty(xmit)) goto txq_empty; val = bcm_uart_readl(port, UART_MCTL_REG); val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; max_count = port->fifosize - val; while (max_count--) { unsigned int c; c = xmit->buf[xmit->tail]; bcm_uart_writel(port, c, UART_FIFO_REG); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) goto txq_empty; return; txq_empty: /* nothing to send, disable transmit interrupt */ val = bcm_uart_readl(port, UART_IR_REG); val &= ~UART_TX_INT_MASK; bcm_uart_writel(port, val, UART_IR_REG); return; }
/* * serial core request to check if uart tx fifo is empty */ static unsigned int bcm_uart_tx_empty(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_IR_REG); return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; }
/* * serial core request to enable modem status interrupt reporting */ static void bcm_uart_enable_ms(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_IR_REG); val |= UART_IR_MASK(UART_IR_EXTIP); bcm_uart_writel(port, val, UART_IR_REG); }
/* * serial core request to stop rx, called before port shutdown */ static void bcm_uart_stop_rx(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_IR_REG); val &= ~UART_RX_INT_MASK; bcm_uart_writel(port, val, UART_IR_REG); }
/* * enable rx & tx operation on uart */ static void bcm_uart_enable(struct uart_port *port) { unsigned int val; val = bcm_uart_readl(port, UART_CTL_REG); val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); bcm_uart_writel(port, val, UART_CTL_REG); }
/* * serial core request to set RTS and DTR pin state and loopback mode */ static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int val; val = bcm_uart_readl(port, UART_MCTL_REG); val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); /* invert of written value is reflected on the pin */ if (!(mctrl & TIOCM_DTR)) val |= UART_MCTL_DTR_MASK; if (!(mctrl & TIOCM_RTS)) val |= UART_MCTL_RTS_MASK; bcm_uart_writel(port, val, UART_MCTL_REG); val = bcm_uart_readl(port, UART_CTL_REG); if (mctrl & TIOCM_LOOP) val |= UART_CTL_LOOPBACK_MASK; else val &= ~UART_CTL_LOOPBACK_MASK; bcm_uart_writel(port, val, UART_CTL_REG); }
/* * serial core request to initialize uart and start rx operation */ static int bcm_uart_startup(struct uart_port *port) { unsigned int val; int ret; /* mask all irq and flush port */ bcm_uart_disable(port); bcm_uart_writel(port, 0, UART_IR_REG); bcm_uart_flush(port); /* clear any pending external input interrupt */ (void)bcm_uart_readl(port, UART_EXTINP_REG); /* set rx/tx fifo thresh to fifo half size */ val = bcm_uart_readl(port, UART_MCTL_REG); val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; bcm_uart_writel(port, val, UART_MCTL_REG); /* set rx fifo timeout to 1 char time */ val = bcm_uart_readl(port, UART_CTL_REG); val &= ~UART_CTL_RXTMOUTCNT_MASK; val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; bcm_uart_writel(port, val, UART_CTL_REG); /* report any edge on dcd and cts */ val = UART_EXTINP_INT_MASK; val |= UART_EXTINP_DCD_NOSENSE_MASK; val |= UART_EXTINP_CTS_NOSENSE_MASK; bcm_uart_writel(port, val, UART_EXTINP_REG); /* register irq and enable rx interrupts */ ret = request_irq(port->irq, bcm_uart_interrupt, 0, bcm_uart_type(port), port); if (ret) return ret; bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); bcm_uart_enable(port); return 0; }
static int bcm_uart_startup(struct uart_port *port) { unsigned int val; int ret; bcm_uart_disable(port); bcm_uart_writel(port, 0, UART_IR_REG); bcm_uart_flush(port); (void)bcm_uart_readl(port, UART_EXTINP_REG); val = bcm_uart_readl(port, UART_MCTL_REG); val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; bcm_uart_writel(port, val, UART_MCTL_REG); val = bcm_uart_readl(port, UART_CTL_REG); val &= ~UART_CTL_RXTMOUTCNT_MASK; val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; bcm_uart_writel(port, val, UART_CTL_REG); val = UART_EXTINP_INT_MASK; val |= UART_EXTINP_DCD_NOSENSE_MASK; val |= UART_EXTINP_CTS_NOSENSE_MASK; bcm_uart_writel(port, val, UART_EXTINP_REG); ret = request_irq(port->irq, bcm_uart_interrupt, 0, bcm_uart_type(port), port); if (ret) return ret; bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); bcm_uart_enable(port); return 0; }
/* * serial core request to start/stop emitting break char */ static void bcm_uart_break_ctl(struct uart_port *port, int ctl) { unsigned long flags; unsigned int val; spin_lock_irqsave(&port->lock, flags); val = bcm_uart_readl(port, UART_CTL_REG); if (ctl) val |= UART_CTL_XMITBRK_MASK; else val &= ~UART_CTL_XMITBRK_MASK; bcm_uart_writel(port, val, UART_CTL_REG); spin_unlock_irqrestore(&port->lock, flags); }
/* * serial core request to return RI, CTS, DCD and DSR pin state */ static unsigned int bcm_uart_get_mctrl(struct uart_port *port) { unsigned int val, mctrl; mctrl = 0; val = bcm_uart_readl(port, UART_EXTINP_REG); if (val & UART_EXTINP_RI_MASK) mctrl |= TIOCM_RI; if (val & UART_EXTINP_CTS_MASK) mctrl |= TIOCM_CTS; if (val & UART_EXTINP_DCD_MASK) mctrl |= TIOCM_CD; if (val & UART_EXTINP_DSR_MASK) mctrl |= TIOCM_DSR; return mctrl; }
*/ static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) { unsigned int ctl, baud, quot, ier; unsigned long flags; spin_lock_irqsave(&port->lock, flags); /* disable uart while changing speed */ bcm_uart_disable(port); bcm_uart_flush(port); /* update Control register */ ctl = bcm_uart_readl(port, UART_CTL_REG); ctl &= ~UART_CTL_BITSPERSYM_MASK; switch (new->c_cflag & CSIZE) { case CS5: ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); break; case CS6: ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); break; case CS7: ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); break; default: ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); break;
/* * read all chars in rx fifo and send them to core */ static void bcm_uart_do_rx(struct uart_port *port) { struct tty_struct *tty; unsigned int max_count; /* limit number of char read in interrupt, should not be * higher than fifo size anyway since we're much faster than * serial port */ max_count = 32; tty = port->state->port.tty; do { unsigned int iestat, c, cstat; char flag; /* get overrun/fifo empty information from ier * register */ iestat = bcm_uart_readl(port, UART_IR_REG); if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { unsigned int val; /* fifo reset is required to clear * interrupt */ val = bcm_uart_readl(port, UART_CTL_REG); val |= UART_CTL_RSTRXFIFO_MASK; bcm_uart_writel(port, val, UART_CTL_REG); port->icount.overrun++; tty_insert_flip_char(tty, 0, TTY_OVERRUN); } if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) break; cstat = c = bcm_uart_readl(port, UART_FIFO_REG); port->icount.rx++; flag = TTY_NORMAL; c &= 0xff; if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { /* do stats first */ if (cstat & UART_FIFO_BRKDET_MASK) { port->icount.brk++; if (uart_handle_break(port)) continue; } if (cstat & UART_FIFO_PARERR_MASK) port->icount.parity++; if (cstat & UART_FIFO_FRAMEERR_MASK) port->icount.frame++; /* update flag wrt read_status_mask */ cstat &= port->read_status_mask; if (cstat & UART_FIFO_BRKDET_MASK) flag = TTY_BREAK; if (cstat & UART_FIFO_FRAMEERR_MASK) flag = TTY_FRAME; if (cstat & UART_FIFO_PARERR_MASK) flag = TTY_PARITY; } if (uart_handle_sysrq_char(port, c)) continue; if ((cstat & port->ignore_status_mask) == 0) tty_insert_flip_char(tty, c, flag); } while (--max_count); tty_flip_buffer_push(tty); }