Message ID | 20250429104339.321962-1-quic_anupkulk@quicinc.com |
---|---|
State | New |
Headers | show |
Series | serial: qcom-geni: Enable support for half-duplex mode | expand |
On 4/29/25 12:43 PM, Anup Kulkarni wrote: > Currently the RTS pin is used as flow control in the UART controller by > default.For half-duplex modes, use the RTS pin for direction control to > prevent data collisions by allowing only one direction at a time. Utilize > the rs485 structure and callbacks in the serial core framework to support > half-duplex modes. Use the IOCTL value, 'TIOCSRS485', and the 'struct > serial_rs485' supported by the serial framework to implement support for > related callbacks. Enable RS485 mode with these callbacks. > > Signed-off-by: Anup Kulkarni <quic_anupkulk@quicinc.com> > --- [...] > +static void qcom_geni_set_rts_pin(struct uart_port *uport, bool pin_state) > +{ > + u32 rfr = UART_MANUAL_RFR_EN; > + > + /* Set the logical level of RTS GPIO pin based on the bool variable. */ This comment doesn't add much > + rfr |= pin_state ? UART_RFR_NOT_READY : UART_RFR_READY; > + writel(rfr, uport->membase + SE_UART_MANUAL_RFR); > +} > + > static int qcom_geni_serial_request_port(struct uart_port *uport) > { > struct platform_device *pdev = to_platform_device(uport->dev); > @@ -637,6 +650,7 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) > struct tty_port *tport = &uport->state->port; > unsigned int xmit_size; > u8 *tail; > + bool pin_state; > int ret; > > if (port->tx_dma_addr) > @@ -648,6 +662,12 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) > xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, > UART_XMIT_SIZE); > > + if (uport->rs485.flags & SER_RS485_ENABLED) { > + /* For RS485 mode, the RTS can be set/cleared before transmission */ This comment is incorrectly indented (need 1 more tab) [...] > if (dma) { > - if (dma_tx_status & TX_DMA_DONE) > + if (dma_tx_status & TX_DMA_DONE) { > qcom_geni_serial_handle_tx_dma(uport); > + // Check if RS485 mode is enabled > + if (uport->rs485.flags & SER_RS485_ENABLED) { > + // Determine the RTS pin state based on the > + // RS485 RTS_AFTER_SEND flag. > + pin_state = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND); > + // Set or clear the RTS pin according to the determined state > + qcom_geni_set_rts_pin(uport, pin_state); All of these comments repeat what the code says quite clearly [...] > + } > + } > > if (dma_rx_status) { > if (dma_rx_status & RX_RESET_DONE) > @@ -1594,6 +1624,29 @@ static void qcom_geni_serial_pm(struct uart_port *uport, > } > } > > +/** > + * qcom_geni_rs485_config - Configure RS485 settings for the UART port > + * @uport: Pointer to the UART port structure > + * @termios: Pointer to the termios structure > + * @rs485: Pointer to the RS485 configuration structure > + * > + * This function configures the RTS (Request to Send) pin behavior for RS485 mode. > + * When RS485 mode is enabled, the RTS pin is kept in the ACTIVE state. > + * When RS485 mode is disabled, the RTS pin is controlled by the QUP hardware for auto flow control. > + * > + * Return: Always returns 0. > + */ > + > +static int qcom_geni_rs485_config(struct uart_port *uport, > + struct ktermios *termios, struct serial_rs485 *rs485) > +{ > + if (rs485->flags & SER_RS485_ENABLED) Because SER_RS485_ENABLED is always checked before calling qcom_geni_set_rts_pin(), it may make more sense to combine that and this function, perhaps calling it from qcom_geni_serial_start_tx_dma() too Konrad > + qcom_geni_set_rts_pin(uport, true); // Set RTS pin to ACTIVE state > + else > + writel(0, uport->membase + SE_UART_MANUAL_RFR); // Revert to auto flow control > + return 0; > +}
On 29. 04. 25, 12:43, Anup Kulkarni wrote: > Currently the RTS pin is used as flow control in the UART controller by > default.For half-duplex modes, use the RTS pin for direction control to > prevent data collisions by allowing only one direction at a time. Utilize > the rs485 structure and callbacks in the serial core framework to support > half-duplex modes. Use the IOCTL value, 'TIOCSRS485', and the 'struct > serial_rs485' supported by the serial framework to implement support for > related callbacks. Enable RS485 mode with these callbacks. > > Signed-off-by: Anup Kulkarni <quic_anupkulk@quicinc.com> > --- > drivers/tty/serial/qcom_geni_serial.c | 61 ++++++++++++++++++++++++++- > 1 file changed, 60 insertions(+), 1 deletion(-) > > diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c > index a80ce7aaf309..ad3c63cecda0 100644 > --- a/drivers/tty/serial/qcom_geni_serial.c > +++ b/drivers/tty/serial/qcom_geni_serial.c ... > @@ -637,6 +650,7 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) > struct tty_port *tport = &uport->state->port; > unsigned int xmit_size; > u8 *tail; > + bool pin_state; > int ret; > > if (port->tx_dma_addr) > @@ -648,6 +662,12 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) > xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, > UART_XMIT_SIZE); > > + if (uport->rs485.flags & SER_RS485_ENABLED) { > + /* For RS485 mode, the RTS can be set/cleared before transmission */ > + pin_state = !!(uport->rs485.flags & SER_RS485_RTS_ON_SEND); That !! is unnecessary. > + qcom_geni_set_rts_pin(uport, pin_state); > + } > + > qcom_geni_serial_setup_tx(uport, xmit_size); > > ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, ... > @@ -1055,8 +1076,17 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) > } > > if (dma) { > - if (dma_tx_status & TX_DMA_DONE) > + if (dma_tx_status & TX_DMA_DONE) { > qcom_geni_serial_handle_tx_dma(uport); > + // Check if RS485 mode is enabled > + if (uport->rs485.flags & SER_RS485_ENABLED) { > + // Determine the RTS pin state based on the > + // RS485 RTS_AFTER_SEND flag. > + pin_state = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND); The same here. > + // Set or clear the RTS pin according to the determined state > + qcom_geni_set_rts_pin(uport, pin_state); > + } > + } > > if (dma_rx_status) { > if (dma_rx_status & RX_RESET_DONE) > @@ -1594,6 +1624,29 @@ static void qcom_geni_serial_pm(struct uart_port *uport, > } > } > > +/** > + * qcom_geni_rs485_config - Configure RS485 settings for the UART port > + * @uport: Pointer to the UART port structure > + * @termios: Pointer to the termios structure > + * @rs485: Pointer to the RS485 configuration structure > + * > + * This function configures the RTS (Request to Send) pin behavior for RS485 mode. > + * When RS485 mode is enabled, the RTS pin is kept in the ACTIVE state. > + * When RS485 mode is disabled, the RTS pin is controlled by the QUP hardware for auto flow control. > + * > + * Return: Always returns 0. > + */ > + This \n is superfluous. IIRC it is even problematic for kernel-doc. > +static int qcom_geni_rs485_config(struct uart_port *uport, > + struct ktermios *termios, struct serial_rs485 *rs485) > +{ > + if (rs485->flags & SER_RS485_ENABLED) > + qcom_geni_set_rts_pin(uport, true); // Set RTS pin to ACTIVE state C style comments ^^ and vv. > + else > + writel(0, uport->membase + SE_UART_MANUAL_RFR); // Revert to auto flow control > + return 0; > +} > + > static const struct uart_ops qcom_geni_console_pops = { > .tx_empty = qcom_geni_serial_tx_empty, > .stop_tx = qcom_geni_serial_stop_tx_fifo, thanks,
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index a80ce7aaf309..ad3c63cecda0 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -190,6 +190,19 @@ static struct qcom_geni_serial_port qcom_geni_console_port = { }, }; +static const struct serial_rs485 qcom_geni_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND | SER_RS485_RTS_ON_SEND, +}; + +static void qcom_geni_set_rts_pin(struct uart_port *uport, bool pin_state) +{ + u32 rfr = UART_MANUAL_RFR_EN; + + /* Set the logical level of RTS GPIO pin based on the bool variable. */ + rfr |= pin_state ? UART_RFR_NOT_READY : UART_RFR_READY; + writel(rfr, uport->membase + SE_UART_MANUAL_RFR); +} + static int qcom_geni_serial_request_port(struct uart_port *uport) { struct platform_device *pdev = to_platform_device(uport->dev); @@ -637,6 +650,7 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) struct tty_port *tport = &uport->state->port; unsigned int xmit_size; u8 *tail; + bool pin_state; int ret; if (port->tx_dma_addr) @@ -648,6 +662,12 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); + if (uport->rs485.flags & SER_RS485_ENABLED) { + /* For RS485 mode, the RTS can be set/cleared before transmission */ + pin_state = !!(uport->rs485.flags & SER_RS485_RTS_ON_SEND); + qcom_geni_set_rts_pin(uport, pin_state); + } + qcom_geni_serial_setup_tx(uport, xmit_size); ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, @@ -1017,6 +1037,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) u32 dma_rx_status; struct uart_port *uport = dev; bool drop_rx = false; + bool pin_state; struct tty_port *tport = &uport->state->port; struct qcom_geni_serial_port *port = to_dev_port(uport); @@ -1055,8 +1076,17 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) } if (dma) { - if (dma_tx_status & TX_DMA_DONE) + if (dma_tx_status & TX_DMA_DONE) { qcom_geni_serial_handle_tx_dma(uport); + // Check if RS485 mode is enabled + if (uport->rs485.flags & SER_RS485_ENABLED) { + // Determine the RTS pin state based on the + // RS485 RTS_AFTER_SEND flag. + pin_state = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND); + // Set or clear the RTS pin according to the determined state + qcom_geni_set_rts_pin(uport, pin_state); + } + } if (dma_rx_status) { if (dma_rx_status & RX_RESET_DONE) @@ -1594,6 +1624,29 @@ static void qcom_geni_serial_pm(struct uart_port *uport, } } +/** + * qcom_geni_rs485_config - Configure RS485 settings for the UART port + * @uport: Pointer to the UART port structure + * @termios: Pointer to the termios structure + * @rs485: Pointer to the RS485 configuration structure + * + * This function configures the RTS (Request to Send) pin behavior for RS485 mode. + * When RS485 mode is enabled, the RTS pin is kept in the ACTIVE state. + * When RS485 mode is disabled, the RTS pin is controlled by the QUP hardware for auto flow control. + * + * Return: Always returns 0. + */ + +static int qcom_geni_rs485_config(struct uart_port *uport, + struct ktermios *termios, struct serial_rs485 *rs485) +{ + if (rs485->flags & SER_RS485_ENABLED) + qcom_geni_set_rts_pin(uport, true); // Set RTS pin to ACTIVE state + else + writel(0, uport->membase + SE_UART_MANUAL_RFR); // Revert to auto flow control + return 0; +} + static const struct uart_ops qcom_geni_console_pops = { .tx_empty = qcom_geni_serial_tx_empty, .stop_tx = qcom_geni_serial_stop_tx_fifo, @@ -1686,6 +1739,8 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return -EINVAL; uport->mapbase = res->start; + uport->rs485_config = qcom_geni_rs485_config; + uport->rs485_supported = qcom_geni_rs485_supported; port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; @@ -1751,6 +1806,10 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return ret; } + ret = uart_get_rs485_mode(uport); + if (ret) + return ret; + ret = uart_add_one_port(drv, uport); if (ret) return ret;
Currently the RTS pin is used as flow control in the UART controller by default.For half-duplex modes, use the RTS pin for direction control to prevent data collisions by allowing only one direction at a time. Utilize the rs485 structure and callbacks in the serial core framework to support half-duplex modes. Use the IOCTL value, 'TIOCSRS485', and the 'struct serial_rs485' supported by the serial framework to implement support for related callbacks. Enable RS485 mode with these callbacks. Signed-off-by: Anup Kulkarni <quic_anupkulk@quicinc.com> --- drivers/tty/serial/qcom_geni_serial.c | 61 ++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-)