/** * cdns_uart_suspend - suspend event * @device: Pointer to the device structure * * Return: 0 */ static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); struct tty_struct *tty; struct device *tty_dev; int may_wake = 0; /* Get the tty which could be NULL so don't assume it's valid */ tty = tty_port_tty_get(&port->state->port); if (tty) { tty_dev = tty->dev; may_wake = device_may_wakeup(tty_dev); tty_kref_put(tty); } /* * Call the API provided in serial_core.c file which handles * the suspend. */ uart_suspend_port(&cdns_uart_uart_driver, port); if (console_suspend_enabled && !may_wake) { struct cdns_uart *cdns_uart = port->private_data; clk_disable(cdns_uart->uartclk); clk_disable(cdns_uart->pclk); } else { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY)) cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* set RX trigger level to 1 */ cdns_uart_writel(1, CDNS_UART_RXWM_OFFSET); /* disable RX timeout interrups */ cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IDR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } return 0; }
static int cdns_uart_poll_get_char(struct uart_port *port) { u32 imr; int c; /* Disable all interrupts */ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Check if FIFO is empty */ if (cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) c = NO_POLL_CHAR; else /* Read a character */ c = (unsigned char) cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Enable interrupts */ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return c; }
/** * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure */ static void cdns_uart_start_tx(struct uart_port *port) { unsigned int status, numbytes = port->fifosize; if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) return; status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Set the TX enable bit and clear the TX disable bit to enable the * transmitter. */ cdns_uart_writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, CDNS_UART_CR_OFFSET); while (numbytes-- && ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXFULL)) != CDNS_UART_SR_TXFULL) { /* Break if no more data available in the UART buffer */ if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer and * write it to the cdns_uart's TX_FIFO register. */ cdns_uart_writel( port->state->xmit.buf[port->state->xmit.tail], CDNS_UART_FIFO_OFFSET); port->icount.tx++; /* Adjust the tail of the UART buffer and wrap * the buffer if it reaches limit. */ port->state->xmit.tail = (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_ISR_OFFSET); /* Enable the TX Empty interrupt */ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IER_OFFSET); if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) uart_write_wakeup(port); }
/** * cdns_uart_shutdown - Called when an application closes a cdns_uart port * @port: Handle to the uart port structure */ static void cdns_uart_shutdown(struct uart_port *port) { int status; /* Disable interrupts */ status = cdns_uart_readl(CDNS_UART_IMR_OFFSET); cdns_uart_writel(status, CDNS_UART_IDR_OFFSET); /* Disable the TX and RX */ cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, CDNS_UART_CR_OFFSET); free_irq(port->irq, port); }
static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { u32 imr; /* Disable all interrupts */ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Wait until FIFO is empty */ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Write a character */ cdns_uart_writel(c, CDNS_UART_FIFO_OFFSET); /* Wait until FIFO is empty */ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Enable interrupts */ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return; }
/** * cdns_uart_console_write - perform write operation * @co: Console handle * @s: Pointer to character array * @count: No of characters */ static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &cdns_uart_port[co->index]; unsigned long flags; unsigned int imr, ctrl; int locked = 1; if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* * Make sure that the tx part is enabled. Set the TX enable bit and * clear the TX disable bit to enable the transmitter. */ ctrl = cdns_uart_readl(CDNS_UART_CR_OFFSET); cdns_uart_writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, CDNS_UART_CR_OFFSET); uart_console_write(port, s, count, cdns_uart_console_putchar); cdns_uart_console_wait_tx(port); cdns_uart_writel(ctrl, CDNS_UART_CR_OFFSET); /* restore interrupt state */ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); if (locked) spin_unlock_irqrestore(&port->lock, flags); }
static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { u32 val; val = cdns_uart_readl(CDNS_UART_MODEMCR_OFFSET); val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR); if (mctrl & TIOCM_RTS) val |= CDNS_UART_MODEMCR_RTS; if (mctrl & TIOCM_DTR) val |= CDNS_UART_MODEMCR_DTR; cdns_uart_writel(val, CDNS_UART_MODEMCR_OFFSET); }
/** * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure * @ctl: Value based on which start or stop decision is taken */ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) { unsigned int status; unsigned long flags; spin_lock_irqsave(&port->lock, flags); status = cdns_uart_readl(CDNS_UART_CR_OFFSET); if (ctl == -1) cdns_uart_writel(CDNS_UART_CR_STARTBRK | status, CDNS_UART_CR_OFFSET); else { if ((status & CDNS_UART_CR_STOPBRK) == 0) cdns_uart_writel(CDNS_UART_CR_STOPBRK | status, CDNS_UART_CR_OFFSET); } spin_unlock_irqrestore(&port->lock, flags); }
/** * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * * Return: IRQHANDLED */ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; unsigned int isrstatus; spin_lock(&port->lock); /* Read the interrupt status register to determine which * interrupt(s) is/are active and clear them. */ isrstatus = cdns_uart_readl(CDNS_UART_ISR_OFFSET); cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET); if (isrstatus & CDNS_UART_IXR_TXEMPTY) { cdns_uart_handle_tx(dev_id); isrstatus &= ~CDNS_UART_IXR_TXEMPTY; } if (isrstatus & CDNS_UART_IXR_MASK) cdns_uart_handle_rx(dev_id, isrstatus); spin_unlock(&port->lock); return IRQ_HANDLED; }
/** * cdns_uart_set_termios - termios operations, handling data length, parity, * stop bits, flow control, baud rate * @port: Handle to the uart port structure * @termios: Handle to the input termios structure * @old: Values of the previously saved termios structure */ static void cdns_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned int cval = 0; unsigned int baud, minbaud, maxbaud; unsigned long flags; unsigned int ctrl_reg, mode_reg; spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { cdns_uart_readl(CDNS_UART_FIFO_OFFSET); } /* Disable the TX and RX to set baud rate */ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); /* * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk * min and max baud should be calculated here based on port->uartclk. * this way we get a valid baud and can safely call set_baud() */ minbaud = port->uartclk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8); maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1); baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); baud = cdns_uart_set_baud_rate(port, baud); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* Set TX/RX Reset */ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); /* * Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; port->ignore_status_mask = 0; if (termios->c_iflag & INPCK) port->read_status_mask |= CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING; if (termios->c_iflag & IGNPAR) port->ignore_status_mask |= CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; mode_reg = cdns_uart_readl(CDNS_UART_MR_OFFSET); /* Handling Data Size */ switch (termios->c_cflag & CSIZE) { case CS6: cval |= CDNS_UART_MR_CHARLEN_6_BIT; break; case CS7: cval |= CDNS_UART_MR_CHARLEN_7_BIT; break; default: case CS8: cval |= CDNS_UART_MR_CHARLEN_8_BIT; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; } /* Handling Parity and Stop Bits length */ if (termios->c_cflag & CSTOPB) cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */ else cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */ if (termios->c_cflag & PARENB) { /* Mark or Space parity */ if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) cval |= CDNS_UART_MR_PARITY_MARK; else cval |= CDNS_UART_MR_PARITY_SPACE; } else { if (termios->c_cflag & PARODD) cval |= CDNS_UART_MR_PARITY_ODD; else cval |= CDNS_UART_MR_PARITY_EVEN; } } else { cval |= CDNS_UART_MR_PARITY_NONE; } cval |= mode_reg & 1; cdns_uart_writel(cval, CDNS_UART_MR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); }
/** * cdns_uart_clk_notitifer_cb - Clock notifier callback * @nb: Notifier block * @event: Notify event * @data: Notifier data * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. */ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { u32 ctrl_reg; struct uart_port *port; int locked = 0; struct clk_notifier_data *ndata = data; unsigned long flags = 0; struct cdns_uart *cdns_uart = to_cdns_uart(nb); port = cdns_uart->port; if (port->suspended) return NOTIFY_OK; switch (event) { case PRE_RATE_CHANGE: { u32 bdiv, cd; int div8; /* * Find out if current baud-rate can be achieved with new clock * frequency. */ if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud, &bdiv, &cd, &div8)) { dev_warn(port->dev, "clock rate change rejected\n"); return NOTIFY_BAD; } spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; } case POST_RATE_CHANGE: /* * Set clk dividers to generate correct baud with new clock * frequency. */ spin_lock_irqsave(&cdns_uart->port->lock, flags); locked = 1; port->uartclk = ndata->new_rate; cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port, cdns_uart->baud); /* fall through */ case ABORT_RATE_CHANGE: if (!locked) spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Set TX/RX Reset */ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* * Clear the RX disable and TX disable bits and then set the TX * enable bit and RX enable bit to enable the transmitter and * receiver. */ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; default: return NOTIFY_DONE; } }
/** * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * * Return: IRQHANDLED */ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; unsigned long flags; unsigned int isrstatus, numbytes; unsigned int data; char status = TTY_NORMAL; spin_lock_irqsave(&port->lock, flags); /* Read the interrupt status register to determine which * interrupt(s) is/are active. */ isrstatus = cdns_uart_readl(CDNS_UART_ISR_OFFSET); /* * There is no hardware break detection, so we interpret framing * error with all-zeros data as a break sequence. Most of the time, * there's another non-zero byte at the end of the sequence. */ if (isrstatus & CDNS_UART_IXR_FRAMING) { while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY)) { if (!cdns_uart_readl(CDNS_UART_FIFO_OFFSET)) { port->read_status_mask |= CDNS_UART_IXR_BRK; isrstatus &= ~CDNS_UART_IXR_FRAMING; } } cdns_uart_writel(CDNS_UART_IXR_FRAMING, CDNS_UART_ISR_OFFSET); } /* drop byte with parity error if IGNPAR specified */ if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; if ((isrstatus & CDNS_UART_IXR_TOUT) || (isrstatus & CDNS_UART_IXR_RXTRIG)) { /* Receive Timeout Interrupt */ while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Non-NULL byte after BREAK is garbage (99%) */ if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { port->read_status_mask &= ~CDNS_UART_IXR_BRK; port->icount.brk++; if (uart_handle_break(port)) continue; } #ifdef SUPPORT_SYSRQ /* * uart_handle_sysrq_char() doesn't work if * spinlocked, for some reason */ if (port->sysrq) { spin_unlock(&port->lock); if (uart_handle_sysrq_char(port, (unsigned char)data)) { spin_lock(&port->lock); continue; } spin_lock(&port->lock); } #endif port->icount.rx++; if (isrstatus & CDNS_UART_IXR_PARITY) { port->icount.parity++; status = TTY_PARITY; } else if (isrstatus & CDNS_UART_IXR_FRAMING) { port->icount.frame++; status = TTY_FRAME; } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { port->icount.overrun++; } uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, data, status); } spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); } /* Dispatch an appropriate handler */ if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) { if (uart_circ_empty(&port->state->xmit)) { cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IDR_OFFSET); } else { numbytes = port->fifosize; /* Break if no more data available in the UART buffer */ while (numbytes--) { if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer * and write it to the cdns_uart's TX_FIFO * register. */ cdns_uart_writel( port->state->xmit.buf[port->state->xmit. tail], CDNS_UART_FIFO_OFFSET); port->icount.tx++; /* Adjust the tail of the UART buffer and wrap * the buffer if it reaches limit. */ port->state->xmit.tail = (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } if (uart_circ_chars_pending( &port->state->xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } } cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET); /* be sure to release the lock and tty before leaving */ spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; }
/** * cdns_uart_console_wait_tx - Wait for the TX to be full * @port: Handle to the uart port structure */ static void cdns_uart_console_wait_tx(struct uart_port *port) { while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY) != CDNS_UART_SR_TXEMPTY) barrier(); }
/** * cdns_uart_startup - Called when an application opens a cdns_uart port * @port: Handle to the uart port structure * * Return: 0 on success, negative errno otherwise */ static int cdns_uart_startup(struct uart_port *port) { struct cdns_uart *cdns_uart = port->private_data; bool is_brk_support; unsigned int retval = 0, status = 0; is_brk_support = cdns_uart->quirks & CDNS_UART_BRK_DET; retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, (void *)port); if (retval) return retval; /* Disable the TX and RX */ cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, CDNS_UART_CR_OFFSET); /* Set the Control Register with TX/RX Enable, TX/RX Reset, * no break chars. */ cdns_uart_writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, CDNS_UART_CR_OFFSET); status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ cdns_uart_writel((status & ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS)) | (CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN | CDNS_UART_CR_STOPBRK), CDNS_UART_CR_OFFSET); /* Set the Mode Register with normal mode,8 data bits,1 stop bit, * no parity. */ cdns_uart_writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, CDNS_UART_MR_OFFSET); /* * Set the RX FIFO Trigger level to use most of the FIFO, but it * can be tuned with a module parameter */ cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); /* * Receive Timeout register is enabled but it * can be tuned with a module parameter */ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); /* Clear out any pending interrupts before enabling them */ cdns_uart_writel(cdns_uart_readl(CDNS_UART_ISR_OFFSET), CDNS_UART_ISR_OFFSET); /* * Set the Interrupt Registers with desired interrupts. Do not * enable parity error interrupt for the following reason: * When parity error interrupt is enabled, each Rx parity error always * results in 2 events. The first one being parity error interrupt * and the second one with a proper Rx interrupt with the incoming data. * Disabling parity error interrupt ensures better handling of parity * error events. With this change, for a parity error case, we get a * Rx interrupt with parity error set in ISR register and we still * handle parity errors in the desired way. */ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET); if (is_brk_support) { status = cdns_uart_readl(CDNS_UART_IMR_OFFSET); cdns_uart_writel((status | CDNS_UART_IXR_BRK), CDNS_UART_IER_OFFSET); } return retval; }
/** * cdns_uart_handle_rx - Handle the received bytes along with Rx errors. * @dev_id: Id of the UART port * @isrstatus: The interrupt status register value as read * Return: None */ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) { struct uart_port *port = (struct uart_port *)dev_id; struct cdns_uart *cdns_uart = port->private_data; unsigned int data; unsigned int framerrprocessed = 0; char status = TTY_NORMAL; bool is_brk_support; is_brk_support = cdns_uart->quirks & CDNS_UART_BRK_DET; while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET); port->icount.rx++; /* * There is no hardware break detection in Zynq, so we interpret * framing error with all-zeros data as a break sequence. * Most of the time, there's another non-zero byte at the * end of the sequence. */ if (!is_brk_support && (isrstatus & CDNS_UART_IXR_FRAMING)) { if (!data) { port->read_status_mask |= CDNS_UART_IXR_BRK; framerrprocessed = 1; continue; } } if (is_brk_support && (isrstatus & CDNS_UART_IXR_BRK)) { port->icount.brk++; status = TTY_BREAK; if (uart_handle_break(port)) continue; } isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; if ((isrstatus & CDNS_UART_IXR_TOUT) || (isrstatus & CDNS_UART_IXR_RXTRIG)) { if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { port->read_status_mask &= ~CDNS_UART_IXR_BRK; port->icount.brk++; if (uart_handle_break(port)) continue; } #ifdef SUPPORT_SYSRQ /* * uart_handle_sysrq_char() doesn't work if * spinlocked, for some reason */ if (port->sysrq) { spin_unlock(&port->lock); if (uart_handle_sysrq_char(port, (unsigned char)data)) { spin_lock(&port->lock); continue; } spin_lock(&port->lock); } #endif if (isrstatus & CDNS_UART_IXR_PARITY) { port->icount.parity++; status = TTY_PARITY; } if ((isrstatus & CDNS_UART_IXR_FRAMING) && !framerrprocessed) { port->icount.frame++; status = TTY_FRAME; } if (isrstatus & CDNS_UART_IXR_OVERRUN) { port->icount.overrun++; tty_insert_flip_char(&port->state->port, 0, TTY_OVERRUN); } tty_insert_flip_char(&port->state->port, data, status); } } spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); }