Example #1
0
/**
 * cdns_uart_set_baud_rate - Calculate and set the baud rate
 * @port: Handle to the uart port structure
 * @baud: Baud rate to set
 * Return: baud rate, requested baud when possible, or actual baud when there
 *	   was too much error, zero if no valid divisors are found.
 */
static unsigned int cdns_uart_set_baud_rate(struct uart_port *port,
        unsigned int baud)
{
    unsigned int calc_baud;
    u32 cd = 0, bdiv = 0;
    u32 mreg;
    int div8;
    struct cdns_uart *cdns_uart = port->private_data;

    calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd,
                                         &div8);

    /* Write new divisors to hardware */
    mreg = cdns_uart_readl(CDNS_UART_MR_OFFSET);
    if (div8)
        mreg |= CDNS_UART_MR_CLKSEL;
    else
        mreg &= ~CDNS_UART_MR_CLKSEL;
    cdns_uart_writel(mreg, CDNS_UART_MR_OFFSET);
    cdns_uart_writel(cd, CDNS_UART_BAUDGEN_OFFSET);
    cdns_uart_writel(bdiv, CDNS_UART_BAUDDIV_OFFSET);
    cdns_uart->baud = baud;

    return calc_baud;
}
Example #2
0
/**
 * cdns_uart_resume - Resume after a previous suspend
 * @device: Pointer to the device structure
 *
 * Return: 0
 */
static int cdns_uart_resume(struct device *device)
{
    struct uart_port *port = dev_get_drvdata(device);
    unsigned long flags = 0;
    u32 ctrl_reg;
    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);
    }

    if (console_suspend_enabled && !may_wake) {
        struct cdns_uart *cdns_uart = port->private_data;

        clk_enable(cdns_uart->pclk);
        clk_enable(cdns_uart->uartclk);

        spin_lock_irqsave(&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();

        /* restore rx timeout value */
        cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET);
        /* Enable Tx/Rx */
        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(&port->lock, flags);
    } else {
        spin_lock_irqsave(&port->lock, flags);
        /* restore original rx trigger level */
        cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET);
        /* enable RX timeout interrupt */
        cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET);
        spin_unlock_irqrestore(&port->lock, flags);
    }

    return uart_resume_port(&cdns_uart_uart_driver, port);
}
Example #3
0
/**
 * 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);
}
Example #4
0
/**
 * 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)
{
    unsigned int retval = 0, status = 0;

    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 */
    cdns_uart_writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_PARITY |
                     CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN |
                     CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT,
                     CDNS_UART_IER_OFFSET);

    return retval;
}
Example #5
0
/**
 * cdns_uart_stop_rx - Stop RX
 * @port: Handle to the uart port structure
 */
static void cdns_uart_stop_rx(struct uart_port *port)
{
    unsigned int regval;

    regval = cdns_uart_readl(CDNS_UART_CR_OFFSET);
    regval |= CDNS_UART_CR_RX_DIS;
    /* Disable the receiver */
    cdns_uart_writel(regval, CDNS_UART_CR_OFFSET);
}
Example #6
0
/**
 * 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);
}
Example #7
0
/**
 * 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;
}
Example #8
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;
}
Example #9
0
/**
 * 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);
}
Example #10
0
/**
 * cdns_uart_handle_tx - Handle the bytes to be Txed.
 * @dev_id: Id of the UART port
 * Return: None
 */
static void cdns_uart_handle_tx(void *dev_id)
{
	struct uart_port *port = (struct uart_port *)dev_id;
	unsigned int numbytes;

	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);
	}
}
Example #11
0
/**
 * 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);
}
Example #12
0
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;
}
Example #13
0
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);
}
Example #14
0
/**
 * 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;
}
Example #15
0
/**
 * 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;
}
Example #16
0
/**
 * cdns_uart_console_putchar - write the character to the FIFO buffer
 * @port: Handle to the uart port structure
 * @ch: Character to be written
 */
static void cdns_uart_console_putchar(struct uart_port *port, int ch)
{
    cdns_uart_console_wait_tx(port);
    cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET);
}
Example #17
0
/**
 * 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);
}
Example #18
0
/**
 * 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;
}
Example #19
0
/**
 * 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;
    }
}