serial: 8250_mxpcie: add break support for RS485 using MUEx50 features

On MUEx50, break signaling under RS485 requires a driver-specific
sequence and cannot be handled correctly by the generic 8250 break
implementation alone.

Implement a mxpcie break_ctl callback that performs MUEx50-specific
break handling when RS485 is enabled and fall back to the default 8250
break handling for other modes.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
Link: https://patch.msgid.link/20260731074820.735619-12-crescentcy.hsieh@moxa.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Crescent Hsieh 2026-07-31 15:48:17 +08:00 committed by Greg Kroah-Hartman
parent af1ba61b62
commit ade78d3d25

View File

@ -54,6 +54,7 @@
/* Special Function Register (SFR) */
#define MOXA_PUART_SFR 0x07
#define MOXA_PUART_SFR_FORCE_TX BIT(0)
#define MOXA_PUART_SFR_950 BIT(5)
/* Enhanced Function Register (EFR) */
@ -382,6 +383,52 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
return 1;
}
static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct tty_struct *tty = port->state->port.tty;
unsigned int baud, quot;
u8 sfr, tx_byte = 0x01;
guard(uart_port_lock_irqsave)(port);
if (break_state == -1) {
serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
serial_dl_write(up, 0);
serial_out(up, UART_LCR, up->lcr);
serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte);
sfr = serial_in(up, MOXA_PUART_SFR);
serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX);
up->lcr |= UART_LCR_SBC;
serial_out(up, UART_LCR, up->lcr);
} else {
up->lcr &= ~UART_LCR_SBC;
serial_out(up, UART_LCR, up->lcr);
sfr = serial_in(up, MOXA_PUART_SFR);
serial_out(up, MOXA_PUART_SFR, sfr & ~MOXA_PUART_SFR_FORCE_TX);
serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT);
baud = tty_get_baud_rate(tty);
quot = uart_get_divisor(port, baud);
serial8250_do_set_divisor(port, baud, quot);
serial_out(up, UART_LCR, up->lcr);
}
}
static void mxpcie8250_break_ctl(struct uart_port *port, int break_state)
{
if (port->rs485.flags & SER_RS485_ENABLED &&
!(port->rs485.flags & SER_RS485_MODE_RS422))
mxpcie8250_software_break_ctl(port, break_state);
else
serial8250_do_break_ctl(port, break_state);
}
static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
{
void __iomem *bar2_base = priv->bar2_base;
@ -487,6 +534,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
up.port.throttle = mxpcie8250_throttle;
up.port.unthrottle = mxpcie8250_unthrottle;
up.port.handle_irq = mxpcie8250_handle_irq;
up.port.break_ctl = mxpcie8250_break_ctl;
for (unsigned int i = 0; i < num_ports; i++) {
mxpcie8250_setup_port(pdev, priv, &up, i);