diff options
Diffstat (limited to 'drivers/tty/serial/xilinx_uartps.c')
| -rw-r--r-- | drivers/tty/serial/xilinx_uartps.c | 365 |
1 files changed, 282 insertions, 83 deletions
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 20a751663ef9..c793fc74c26b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -22,7 +22,10 @@ #include <linux/of.h> #include <linux/module.h> #include <linux/pm_runtime.h> -#include <linux/iopoll.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/delay.h> +#include <linux/reset.h> #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -30,7 +33,6 @@ #define CDNS_UART_MINOR 0 /* works best with devtmpfs */ #define CDNS_UART_NR_PORTS 16 #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ -#define CDNS_UART_REGISTER_SPACE 0x1000 #define TX_TIMEOUT 500000 /* Rx Trigger level */ @@ -188,25 +190,39 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @port: Pointer to the UART port * @uartclk: Reference clock * @pclk: APB clock - * @cdns_uart_driver: Pointer to UART driver * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes * @quirks: Flags for RXBS support. * @cts_override: Modem control state override + * @gpiod_rts: Pointer to the gpio descriptor + * @rs485_tx_started: RS485 tx state + * @tx_timer: Timer for tx + * @rstc: Pointer to the reset control */ struct cdns_uart { struct uart_port *port; struct clk *uartclk; struct clk *pclk; - struct uart_driver *cdns_uart_driver; unsigned int baud; struct notifier_block clk_rate_change_nb; u32 quirks; bool cts_override; + struct gpio_desc *gpiod_rts; + bool rs485_tx_started; + struct hrtimer tx_timer; + struct reset_control *rstc; }; struct cdns_platform_data { u32 quirks; }; + +static struct serial_rs485 cdns_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb) @@ -268,7 +284,7 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) continue; } - if (uart_handle_sysrq_char(port, data)) + if (uart_prepare_sysrq_char(port, data)) continue; if (is_rxbs_support) { @@ -306,32 +322,139 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) } /** - * cdns_uart_handle_tx - Handle the bytes to be Txed. + * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low + * @cdns_uart: Handle to the cdns_uart + * @enable: Value to be set to RTS/GPIO + */ +static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable) +{ + u32 val; + + if (cdns_uart->gpiod_rts) { + gpiod_set_value(cdns_uart->gpiod_rts, enable); + } else { + val = readl(cdns_uart->port->membase + CDNS_UART_MODEMCR); + if (enable) + val |= CDNS_UART_MODEMCR_RTS; + else + val &= ~CDNS_UART_MODEMCR_RTS; + writel(val, cdns_uart->port->membase + CDNS_UART_MODEMCR); + } +} + +/** + * cdns_rs485_tx_setup - Tx setup specific to rs485 + * @cdns_uart: Handle to the cdns_uart + */ +static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart) +{ + bool enable; + + enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_ON_SEND; + cdns_rts_gpio_enable(cdns_uart, enable); + + cdns_uart->rs485_tx_started = true; +} + +/** + * cdns_rs485_rx_setup - Rx setup specific to rs485 + * @cdns_uart: Handle to the cdns_uart + */ +static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart) +{ + bool enable; + + enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_AFTER_SEND; + cdns_rts_gpio_enable(cdns_uart, enable); + + cdns_uart->rs485_tx_started = false; +} + +/** + * cdns_uart_tx_empty - Check whether TX is empty + * @port: Handle to the uart port structure + * + * Return: TIOCSER_TEMT on success, 0 otherwise + */ +static unsigned int cdns_uart_tx_empty(struct uart_port *port) +{ + unsigned int status; + + status = readl(port->membase + CDNS_UART_SR); + status &= (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE); + return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 0; +} + +/** + * cdns_rs485_rx_callback - Timer rx callback handler for rs485. + * @t: Handle to the hrtimer structure + */ +static enum hrtimer_restart cdns_rs485_rx_callback(struct hrtimer *t) +{ + struct cdns_uart *cdns_uart = container_of(t, struct cdns_uart, tx_timer); + + /* + * Default Rx should be setup, because Rx signaling path + * need to enable to receive data. + */ + cdns_rs485_rx_setup(cdns_uart); + + return HRTIMER_NORESTART; +} + +/** + * cdns_calc_after_tx_delay - calculate delay required for after tx. + * @cdns_uart: Handle to the cdns_uart + */ +static u64 cdns_calc_after_tx_delay(struct cdns_uart *cdns_uart) +{ + /* + * Frame time + stop bit time + rs485.delay_rts_after_send + */ + return cdns_uart->port->frame_time + + DIV_ROUND_UP(cdns_uart->port->frame_time, 7) + + (u64)cdns_uart->port->rs485.delay_rts_after_send * NSEC_PER_MSEC; +} + +/** + * cdns_uart_handle_tx - Handle the bytes to be transmitted. * @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; - struct circ_buf *xmit = &port->state->xmit; + struct cdns_uart *cdns_uart = port->private_data; + struct tty_port *tport = &port->state->port; unsigned int numbytes; + unsigned char ch; - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { + /* Disable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); return; } numbytes = port->fifosize; - while (numbytes && !uart_circ_empty(xmit) && - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { - - writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO); - uart_xmit_advance(port, 1); + while (numbytes && + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) && + uart_fifo_get(port, &ch)) { + writel(ch, port->membase + CDNS_UART_FIFO); numbytes--; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); + + /* Enable the TX Empty interrupt */ + writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER); + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED && + (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) { + hrtimer_update_function(&cdns_uart->tx_timer, cdns_rs485_rx_callback); + hrtimer_start(&cdns_uart->tx_timer, + ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL); + } } /** @@ -346,7 +469,7 @@ 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); + uart_port_lock(port); /* Read the interrupt status register to determine which * interrupt(s) is/are active and clear them. @@ -369,7 +492,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS)) cdns_uart_handle_rx(dev_id, isrstatus); - spin_unlock(&port->lock); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -506,14 +629,14 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, return NOTIFY_BAD; } - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; writel(ctrl_reg, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + uart_port_unlock_irqrestore(cdns_uart->port, flags); return NOTIFY_OK; } @@ -523,7 +646,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, * frequency. */ - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); locked = 1; port->uartclk = ndata->new_rate; @@ -533,7 +656,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, fallthrough; case ABORT_RATE_CHANGE: if (!locked) - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); /* Set TX/RX Reset */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -555,7 +678,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; writel(ctrl_reg, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + uart_port_unlock_irqrestore(cdns_uart->port, flags); return NOTIFY_OK; default: @@ -565,12 +688,28 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, #endif /** + * cdns_rs485_tx_callback - Timer tx callback handler for rs485. + * @t: Handle to the hrtimer structure + */ +static enum hrtimer_restart cdns_rs485_tx_callback(struct hrtimer *t) +{ + struct cdns_uart *cdns_uart = container_of(t, struct cdns_uart, tx_timer); + + uart_port_lock(cdns_uart->port); + cdns_uart_handle_tx(cdns_uart->port); + uart_port_unlock(cdns_uart->port); + + return HRTIMER_NORESTART; +} + +/** * 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; + struct cdns_uart *cdns_uart = port->private_data; if (uart_tx_stopped(port)) return; @@ -584,15 +723,22 @@ static void cdns_uart_start_tx(struct uart_port *port) status |= CDNS_UART_CR_TX_EN; writel(status, port->membase + CDNS_UART_CR); - if (uart_circ_empty(&port->state->xmit)) + if (kfifo_is_empty(&port->state->port.xmit_fifo)) return; + /* Clear the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR); + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) { + if (!cdns_uart->rs485_tx_started) { + hrtimer_update_function(&cdns_uart->tx_timer, cdns_rs485_tx_callback); + cdns_rs485_tx_setup(cdns_uart); + return hrtimer_start(&cdns_uart->tx_timer, + ms_to_ktime(port->rs485.delay_rts_before_send), + HRTIMER_MODE_REL); + } + } cdns_uart_handle_tx(port); - - /* Enable the TX Empty interrupt */ - writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); } /** @@ -602,6 +748,10 @@ static void cdns_uart_start_tx(struct uart_port *port) static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; + struct cdns_uart *cdns_uart = port->private_data; + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart); regval = readl(port->membase + CDNS_UART_CR); regval |= CDNS_UART_CR_TX_DIS; @@ -627,21 +777,6 @@ static void cdns_uart_stop_rx(struct uart_port *port) } /** - * cdns_uart_tx_empty - Check whether TX is empty - * @port: Handle to the uart port structure - * - * Return: TIOCSER_TEMT on success, 0 otherwise - */ -static unsigned int cdns_uart_tx_empty(struct uart_port *port) -{ - unsigned int status; - - status = readl(port->membase + CDNS_UART_SR) & - (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE); - return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 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 @@ -652,19 +787,19 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) unsigned int status; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = readl(port->membase + CDNS_UART_CR); if (ctl == -1) - writel(CDNS_UART_CR_STARTBRK | status, + writel(CDNS_UART_CR_STARTBRK | (~CDNS_UART_CR_STOPBRK & status), port->membase + CDNS_UART_CR); else { if ((status & CDNS_UART_CR_STOPBRK) == 0) writel(CDNS_UART_CR_STOPBRK | status, port->membase + CDNS_UART_CR); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -683,7 +818,7 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned long flags; unsigned int ctrl_reg, mode_reg; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -794,7 +929,7 @@ static void cdns_uart_set_termios(struct uart_port *port, cval &= ~CDNS_UART_MODEMCR_FCM; writel(cval, port->membase + CDNS_UART_MODEMCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -813,7 +948,11 @@ static int cdns_uart_startup(struct uart_port *port) is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; - spin_lock_irqsave(&port->lock, flags); + ret = reset_control_deassert(cdns_uart->rstc); + if (ret) + return ret; + + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX */ writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, @@ -829,6 +968,9 @@ static int cdns_uart_startup(struct uart_port *port) (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart); + /* * Clear the RX disable bit and then set the RX enable bit to enable * the receiver. @@ -861,7 +1003,7 @@ static int cdns_uart_startup(struct uart_port *port) writel(readl(port->membase + CDNS_UART_ISR), port->membase + CDNS_UART_ISR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port); if (ret) { @@ -888,8 +1030,12 @@ static void cdns_uart_shutdown(struct uart_port *port) { int status; unsigned long flags; + struct cdns_uart *cdns_uart = port->private_data; + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + hrtimer_cancel(&cdns_uart->tx_timer); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable interrupts */ status = readl(port->membase + CDNS_UART_IMR); @@ -900,7 +1046,7 @@ static void cdns_uart_shutdown(struct uart_port *port) writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -949,15 +1095,15 @@ static int cdns_uart_verify_port(struct uart_port *port, */ static int cdns_uart_request_port(struct uart_port *port) { - if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE, + if (!request_mem_region(port->mapbase, port->mapsize, CDNS_UART_NAME)) { return -ENOMEM; } - port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE); + port->membase = ioremap(port->mapbase, port->mapsize); if (!port->membase) { dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); + release_mem_region(port->mapbase, port->mapsize); return -ENOMEM; } return 0; @@ -972,7 +1118,7 @@ static int cdns_uart_request_port(struct uart_port *port) */ static void cdns_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); + release_mem_region(port->mapbase, port->mapsize); iounmap(port->membase); port->membase = NULL; } @@ -1033,6 +1179,8 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) val |= CDNS_UART_MODEMCR_RTS; + if (cdns_uart_data->gpiod_rts) + gpiod_set_value(cdns_uart_data->gpiod_rts, !(mctrl & TIOCM_RTS)); if (mctrl & TIOCM_DTR) val |= CDNS_UART_MODEMCR_DTR; if (mctrl & TIOCM_LOOP) @@ -1050,7 +1198,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) int c; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Check if FIFO is empty */ if (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY) @@ -1058,7 +1206,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) else /* Read a character */ c = (unsigned char) readl(port->membase + CDNS_UART_FIFO); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return c; } @@ -1067,7 +1215,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Wait until FIFO is empty */ while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) @@ -1080,7 +1228,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #endif @@ -1229,12 +1377,10 @@ static void cdns_uart_console_write(struct console *co, const char *s, unsigned int imr, ctrl; int locked = 1; - if (port->sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* save and disable interrupt */ imr = readl(port->membase + CDNS_UART_IMR); @@ -1257,7 +1403,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, writel(imr, port->membase + CDNS_UART_IER); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -1317,7 +1463,6 @@ static struct console cdns_uart_console = { static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); - struct cdns_uart *cdns_uart = port->private_data; int may_wake; may_wake = device_may_wakeup(device); @@ -1325,7 +1470,7 @@ static int cdns_uart_suspend(struct device *device) if (console_suspend_enabled && uart_console(port) && may_wake) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Empty the receive FIFO 1st before making changes */ while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) @@ -1334,14 +1479,14 @@ static int cdns_uart_suspend(struct device *device) writel(1, port->membase + CDNS_UART_RXWM); /* disable RX timeout interrups */ writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IDR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* * Call the API provided in serial_core.c file which handles * the suspend. */ - return uart_suspend_port(cdns_uart->cdns_uart_driver, port); + return uart_suspend_port(&cdns_uart_uart_driver, port); } /** @@ -1372,7 +1517,7 @@ static int cdns_uart_resume(struct device *device) return ret; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Set TX/RX Reset */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -1392,17 +1537,17 @@ static int cdns_uart_resume(struct device *device) clk_disable(cdns_uart->uartclk); clk_disable(cdns_uart->pclk); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } else { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* restore original rx trigger level */ writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); /* enable RX timeout interrupt */ writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IER); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } - return uart_resume_port(cdns_uart->cdns_uart_driver, port); + return uart_resume_port(&cdns_uart_uart_driver, port); } #endif /* ! CONFIG_PM_SLEEP */ static int __maybe_unused cdns_runtime_suspend(struct device *dev) @@ -1456,6 +1601,39 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match); static int instances; /** + * cdns_rs485_config - Called when an application calls TIOCSRS485 ioctl. + * @port: Pointer to the uart_port structure + * @termios: Pointer to the ktermios structure + * @rs485: Pointer to the serial_rs485 structure + * + * Return: 0 + */ +static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 val; + struct cdns_uart *cdns_uart = port->private_data; + + if (rs485->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + /* Make sure auto RTS is disabled */ + val = readl(port->membase + CDNS_UART_MODEMCR); + val &= ~CDNS_UART_MODEMCR_FCM; + writel(val, port->membase + CDNS_UART_MODEMCR); + + /* Timer setup */ + hrtimer_setup(&cdns_uart->tx_timer, &cdns_rs485_tx_callback, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + /* Disable transmitter and make Rx setup*/ + cdns_uart_stop_tx(port); + } else { + hrtimer_cancel(&cdns_uart->tx_timer); + } + return 0; +} + +/** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * @@ -1505,8 +1683,6 @@ static int cdns_uart_probe(struct platform_device *pdev) } } - cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver; - match = of_match_node(cdns_uart_of_match, pdev->dev.of_node); if (match && match->data) { const struct cdns_platform_data *data = match->data; @@ -1544,6 +1720,13 @@ static int cdns_uart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); } + cdns_uart_data->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(cdns_uart_data->rstc)) { + rc = PTR_ERR(cdns_uart_data->rstc); + dev_err_probe(&pdev->dev, rc, "Cannot get UART reset\n"); + goto err_out_unregister_driver; + } + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); @@ -1562,8 +1745,8 @@ static int cdns_uart_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - rc = -ENXIO; + if (irq < 0) { + rc = irq; goto err_out_clk_disable; } @@ -1591,15 +1774,30 @@ static int cdns_uart_probe(struct platform_device *pdev) * and triggers invocation of the config_port() entry point. */ port->mapbase = res->start; + port->mapsize = resource_size(res); port->irq = irq; port->dev = &pdev->dev; port->uartclk = clk_get_rate(cdns_uart_data->uartclk); port->private_data = cdns_uart_data; port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; + port->rs485_config = cdns_rs485_config; + port->rs485_supported = cdns_rs485_supported; cdns_uart_data->port = port; platform_set_drvdata(pdev, port); + rc = uart_get_rs485_mode(port); + if (rc) + goto err_out_clk_notifier; + + cdns_uart_data->gpiod_rts = devm_gpiod_get_optional(&pdev->dev, "rts", + GPIOD_OUT_LOW); + if (IS_ERR(cdns_uart_data->gpiod_rts)) { + rc = PTR_ERR(cdns_uart_data->gpiod_rts); + dev_err(port->dev, "xuartps: devm_gpiod_get_optional failed\n"); + goto err_out_clk_notifier; + } + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); pm_runtime_set_active(&pdev->dev); @@ -1618,6 +1816,8 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = port; } #endif + if (cdns_uart_data->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart_data); rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { @@ -1646,6 +1846,7 @@ err_out_pm_disable: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); +err_out_clk_notifier: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); @@ -1656,17 +1857,15 @@ err_out_clk_dis_pclk: clk_disable_unprepare(cdns_uart_data->pclk); err_out_unregister_driver: if (!instances) - uart_unregister_driver(cdns_uart_data->cdns_uart_driver); + uart_unregister_driver(&cdns_uart_uart_driver); return rc; } /** * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure - * - * Return: 0 on success, negative errno otherwise */ -static int cdns_uart_remove(struct platform_device *pdev) +static void cdns_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct cdns_uart *cdns_uart_data = port->private_data; @@ -1676,7 +1875,7 @@ static int cdns_uart_remove(struct platform_device *pdev) clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); #endif - uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port); + uart_remove_one_port(&cdns_uart_uart_driver, port); port->mapbase = 0; clk_disable_unprepare(cdns_uart_data->uartclk); clk_disable_unprepare(cdns_uart_data->pclk); @@ -1689,10 +1888,10 @@ static int cdns_uart_remove(struct platform_device *pdev) if (console_port == port) console_port = NULL; #endif + reset_control_assert(cdns_uart_data->rstc); if (!--instances) - uart_unregister_driver(cdns_uart_data->cdns_uart_driver); - return 0; + uart_unregister_driver(&cdns_uart_uart_driver); } static struct platform_driver cdns_uart_platform_driver = { |
