Linux Serial subsystem development
 help / color / mirror / Atom feed
* Re: [PATCH v2 08/15] serial: earlycon: use uart_iotype_*() to simplify code
From: Andy Shevchenko @ 2026-05-04 16:01 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
	linux-serial, Hugo Villeneuve
In-Reply-To: <20260504114001.a20541e974060c6fe2f26ba1@hugovil.com>

On Mon, May 04, 2026 at 11:40:01AM -0400, Hugo Villeneuve wrote:
> On Thu, 30 Apr 2026 17:03:35 +0200
> Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > On Tue, Apr 28, 2026 at 01:53:54PM -0400, Hugo Villeneuve wrote:

...

> > > +	char address[64] = "";
> > 
> > TBH, I prefer two pr_info() calls as that approach
> > - doesn't require temporary buffer
> > - doesn't use heavy s*printf() on top of the existing printing
> > - doesn't limit the flexibility of each of the strings (64 might
> >   become not enough in some cases, however unlikely to happen)
> 
> Note that this approach is already used in uart_report_port().

Yeah, yeah... I noticed that after review.

> Also this patch may be reworked if patch "serial: uniformize serial
> port I/O infos display" is deemed ok...

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Andy Shevchenko @ 2026-05-04 15:59 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Hugo Villeneuve, Greg Kroah-Hartman, Jiri Slaby,
	Ilpo Järvinen, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <alpine.DEB.2.21.2605041617190.23161@angie.orcam.me.uk>

On Mon, May 04, 2026 at 04:20:13PM +0100, Maciej W. Rozycki wrote:
> On Mon, 4 May 2026, Andy Shevchenko wrote:
> 
> > I see no value in this discussion. And I see no point to add a word where it's
> > not needed at all.
> 
>  Agreed.  I just pointed out that the use of "legacy" in this context is 
> not incorrect.  And "regular users" won't see any of this stuff anyway.

Good, thanks for the point!

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v2 05/15] serial: core: replace snprintf with more robust scnprintf
From: Andy Shevchenko @ 2026-05-04 15:59 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
	linux-serial, Hugo Villeneuve
In-Reply-To: <20260504112712.7f3d74b47d76d0956b1911fc@hugovil.com>

On Mon, May 04, 2026 at 11:27:12AM -0400, Hugo Villeneuve wrote:
> On Thu, 30 Apr 2026 17:07:11 +0200
> Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > On Tue, Apr 28, 2026 at 01:53:51PM -0400, Hugo Villeneuve wrote:

...

> > >  	default:
> > >  		strscpy(address, "*unknown*", sizeof(address));
> > 
> > Side note: This may use 2-argument strscpy().
> 
> This section is now removed in patch:
>   serial: core: use uart_iotype_*() to simplify uart_report_port()
> 
> But interesting to know this 2-argument variant :)

Yep, we have a handful of functions (actually macros) that use some magic
to allow supply different number of arguments to the same macro and behave
accordingly.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v2 08/15] serial: earlycon: use uart_iotype_*() to simplify code
From: Hugo Villeneuve @ 2026-05-04 15:40 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
	linux-serial, Hugo Villeneuve
In-Reply-To: <afNvR59jmKu3-CoF@black.igk.intel.com>

On Thu, 30 Apr 2026 17:03:35 +0200
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> On Tue, Apr 28, 2026 at 01:53:54PM -0400, Hugo Villeneuve wrote:
> 
> > Make use of new functions uart_iotype_mmio() and uart_iotype_legacy_io()
> > to simplify and improve code readability.
> 
> ...
> 
> > +	char address[64] = "";
> 
> TBH, I prefer two pr_info() calls as that approach
> - doesn't require temporary buffer
> - doesn't use heavy s*printf() on top of the existing printing
> - doesn't limit the flexibility of each of the strings (64 might
>   become not enough in some cases, however unlikely to happen)

Note that this approach is already used in uart_report_port().

Also this patch may be reworked if patch "serial: uniformize serial
port I/O infos display" is deemed ok...

-- 
Hugo Villeneuve

^ permalink raw reply

* Re: [PATCH v2 05/15] serial: core: replace snprintf with more robust scnprintf
From: Hugo Villeneuve @ 2026-05-04 15:27 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Greg Kroah-Hartman, Jiri Slaby, ilpo.jarvinen, linux-kernel,
	linux-serial, Hugo Villeneuve
In-Reply-To: <afNwH01y_6Dk2hVb@black.igk.intel.com>

On Thu, 30 Apr 2026 17:07:11 +0200
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> On Tue, Apr 28, 2026 at 01:53:51PM -0400, Hugo Villeneuve wrote:
> > 
> > Use scnprintf() so we could perhaps one day get rid of snprintf() entirely.
> 
> Ah, now I understand the approach in earlycon. Hmm... Still not sure which one
> I prefer (it's not related to the contents of this patch anyway).
> 
> ...
> 
> >  	default:
> >  		strscpy(address, "*unknown*", sizeof(address));
> 
> Side note: This may use 2-argument strscpy().

This section is now removed in patch:
  serial: core: use uart_iotype_*() to simplify uart_report_port()

But interesting to know this 2-argument variant :)


-- 
Hugo Villeneuve

^ permalink raw reply

* Re: [PATCH 05/15] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware
From: Andy Shevchenko @ 2026-05-04 15:20 UTC (permalink / raw)
  To: Crescent Hsieh
  Cc: gregkh, jirislaby, ilpo.jarvinen, fangpingfp.cheng, linux-kernel,
	linux-serial
In-Reply-To: <20260504084900.22380-6-crescentcy.hsieh@moxa.com>

On Mon, May 4, 2026 at 11:50 AM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> The MUEx50 UART can handle in-band software flow control (XON/XOFF)
> directly in hardware.
>
> Program the on-chip XON/XOFF characters from termios settings and enable
> the corresponding MUEx50 flow control modes when IXON or IXOFF is
> requested. Provide throttle and unthrottle callbacks so RX can be
> stopped and resumed cleanly.

...

> +static void mxpcie8250_throttle(struct uart_port *port)
> +{
> +       unsigned long flags;

> +       uart_port_lock_irqsave(port, &flags);

You may use guard()() from cleanup.h.

> +       port->ops->stop_rx(port);
> +
> +       uart_port_unlock_irqrestore(port, flags);
> +}
> +
> +static void mxpcie8250_unthrottle(struct uart_port *port)
> +{
> +       struct uart_8250_port *up = up_to_u8250p(port);
> +       unsigned long flags;
> +
> +       uart_port_lock_irqsave(port, &flags);
> +
> +       up->ier |= UART_IER_RLSI | UART_IER_RDI;
> +       port->read_status_mask |= UART_LSR_DR;
> +       serial_out(up, UART_IER, up->ier);
> +
> +       uart_port_unlock_irqrestore(port, flags);

Ditto.

> +}

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Maciej W. Rozycki @ 2026-05-04 15:20 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Hugo Villeneuve, Greg Kroah-Hartman, Jiri Slaby,
	Ilpo Järvinen, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <afitf0I1izLup5TR@ashevche-desk.local>

On Mon, 4 May 2026, Andy Shevchenko wrote:

> I see no value in this discussion. And I see no point to add a word where it's
> not needed at all.

 Agreed.  I just pointed out that the use of "legacy" in this context is 
not incorrect.  And "regular users" won't see any of this stuff anyway.

  Maciej

^ permalink raw reply

* Re: [PATCH 03/15] serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger levels
From: Andy Shevchenko @ 2026-05-04 15:18 UTC (permalink / raw)
  To: Crescent Hsieh
  Cc: gregkh, jirislaby, ilpo.jarvinen, fangpingfp.cheng, linux-kernel,
	linux-serial
In-Reply-To: <20260504084900.22380-4-crescentcy.hsieh@moxa.com>

On Mon, May 4, 2026 at 11:49 AM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> The MUEx50 UART provides an enhanced register set and programmable FIFO
> trigger levels for RX, TX, and flow control.
>
> Enable enhanced mode during port startup and program the MUEx50 FIFO
> trigger registers according to the configured port settings. Clear the
> programmed state again during shutdown to restore the default UART
> configuration.

...

> +static int mxpcie8250_startup(struct uart_port *port)
> +{
> +       struct uart_8250_port *up = up_to_u8250p(port);
> +       unsigned int i;
> +       int ret;
> +
> +       ret = serial8250_do_startup(port);
> +       if (ret)
> +               return ret;

This needs a good comment explaining the retry logic. Why do we even need this?

> +       for (i = 0; i < 5; ++i)

for (unsigned int i ...)

> +               serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
> +
> +       serial_out(up, MOXA_PUART_EFR, MOXA_PUART_EFR_ENHANCED);
> +       serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950);
> +
> +       serial_out(up, MOXA_PUART_TTL, 0);
> +       serial_out(up, MOXA_PUART_RTL, 96);
> +       serial_out(up, MOXA_PUART_FCL, 16);
> +       serial_out(up, MOXA_PUART_FCH, 110);
> +
> +       return 0;
> +}

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* [PATCH v1 1/1] serial: 8250_fsl: Export fsl8250_handle_irq() conditionally
From: Andy Shevchenko @ 2026-05-04 15:12 UTC (permalink / raw)
  To: Andy Shevchenko, linux-serial, linux-kernel; +Cc: Greg Kroah-Hartman

Move fsl8250_handle_irq() prototype out of the rest of 8250 generic APIs
in the header and export it conditionally when CONFIG_SERIAL_8250_FSL
is provided.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 include/linux/serial_8250.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 1be007225b0e..43eb0d15012f 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -197,7 +197,6 @@ void serial8250_do_pm(struct uart_port *port, unsigned int state,
 void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
 void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 			       unsigned int quot);
-int fsl8250_handle_irq(struct uart_port *port);
 void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir);
 int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
 u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr);
@@ -220,6 +219,10 @@ extern int hp300_setup_serial_console(void) __init;
 static inline int hp300_setup_serial_console(void) { return 0; }
 #endif
 
+#if IS_REACHABLE(CONFIG_SERIAL_8250_FSL)
+int fsl8250_handle_irq(struct uart_port *port);
+#endif
+
 #ifdef CONFIG_SERIAL_8250_RT288X
 int rt288x_setup(struct uart_port *p);
 int au_platform_setup(struct plat_serial8250_port *p);
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Andy Shevchenko @ 2026-05-04 14:30 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Hugo Villeneuve, Greg Kroah-Hartman, Jiri Slaby,
	Ilpo Järvinen, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <alpine.DEB.2.21.2605041220540.23161@angie.orcam.me.uk>

On Mon, May 04, 2026 at 12:44:41PM +0100, Maciej W. Rozycki wrote:
> On Mon, 4 May 2026, Andy Shevchenko wrote:

...

> > > > Why do we use 'legacy'? Still in use in modern CPUs...
> > > 
> > >  Deprecated in PCIe and not available in numerous systems.  Also actually 
> > > called "legacy" in some serial port datasheets aged ~20 years now.  While 
> > > some contemporary CPUs indeed retain the port I/O address space, it's for 
> > > legacy use anyway, you don't want to rely on it in new designs.
> > 
> > For the holder of the new (modern) CPU which supports the IO ports, this is
> > definitely not a legacy interface despite on whatever PCIe or other datasheets
> > call it.
> 
>  I appreciate your point of view, however I disagree that the presence of an
>  interface in a contemporary chip makes the interface modern.

I appreciate a bike shedding.

>  I see the port I/O space so much legacy as say the PC/AT DMA controller 
> (8237 pair), which is similarly present in current x86 chipsets.  If this 
> stuff was not present, such as say the PC/AT interrupt controller (8259A 
> pair), which I believe has been removed from some x86 system designs, then 
> it would be obsolete/removed rather than legacy.
> 
>  It is analogous to PCI/e systems that lack a southbridge and are called 
> "legacy-free", as the whole southbridge stuff, the main consumer of the 
> port I/O space still remaining in use, is legacy nowadays (the other one I 
> know of being the 8255-based PC parallel port, which has been considered a 
> legacy interface as well, even though you can still buy and plug one into 
> a modern PCIe system).
> 
>  NB I have a couple of modern x86 CPUs around too that support the port 
> I/O space, but it doesn't change my view as to the nomenclature.

Is the regular user assumed to go to dig to the mailing list (at best) or drawn
into the search of the documentation for all this? In their perspective they
possess modern CPU with support of that technology. If the same user possesses
the HW uses that ("legacy") technology, it doesn't make so in their eyes.

On top of that the additional word occupies more space in the code, making it
harder to read. It's just a redundant word. I see no value at all of using it.

P.S.
I see no value in this discussion. And I see no point to add a word where it's
not needed at all.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH 01/15] serial: 8250: split Moxa PCIe serial board support out of 8250_pci
From: Andy Shevchenko @ 2026-05-04 13:29 UTC (permalink / raw)
  To: Crescent Hsieh
  Cc: gregkh, jirislaby, ilpo.jarvinen, fangpingfp.cheng, linux-kernel,
	linux-serial
In-Reply-To: <CAHp75VfFMLnLDP0V3U=4zG4Ayj71-ZgVkJsVtgNE=52tGQ963w@mail.gmail.com>

On Mon, May 4, 2026 at 4:27 PM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> On Mon, May 4, 2026 at 11:49 AM Crescent Hsieh
> <crescentcy.hsieh@moxa.com> wrote:

...

> > +static void mxpcie8250_remove(struct pci_dev *pdev)
> > +{
> > +       struct mxpcie8250 *priv = dev_get_drvdata(&pdev->dev);
>
> platform_get_drvdata() IIRC

Note, for the sake of consistency the probe may use platform_set_drvdata().

> > +       unsigned int i;
> > +
> > +       for (i = 0; i < priv->num_ports; i++)
>
> As per above.
>
> > +               serial8250_unregister_port(priv->line[i]);
> > +}

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH 01/15] serial: 8250: split Moxa PCIe serial board support out of 8250_pci
From: Andy Shevchenko @ 2026-05-04 13:27 UTC (permalink / raw)
  To: Crescent Hsieh
  Cc: gregkh, jirislaby, ilpo.jarvinen, fangpingfp.cheng, linux-kernel,
	linux-serial
In-Reply-To: <20260504084900.22380-2-crescentcy.hsieh@moxa.com>

On Mon, May 4, 2026 at 11:49 AM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> The Moxa PCIe multiport serial boards are currently handled as part of
> 8250_pci.c. In preparation for adding Moxa-specific UART features and
> optimizations, move the Moxa PCIe implementation into a dedicated
> driver.
>
> This introduces drivers/tty/serial/8250/8250_mxpcie.c and wires it up
> via Kconfig and Makefile, while preserving the existing probe flow and
> device IDs.

Thanks for doing this!

> This change was suggested during earlier review by Andy Shevchenko.

an earlier

Perhaps you wanted to add a link references here, something like this

  This change was suggested during earlier reviews by Andy Shevchenko [1][2].

> No functional change intended.

> Link: https://lore.kernel.org/all/ZmQovC6TbDpTb3c8@surfacebook.localdomain/
> Link: https://lore.kernel.org/all/CAHp75VeDsVt0GQYUFxLM+obfmqXBPa3hM3YMsFbc26uzWZG-SQ@mail.gmail.com/

...and hence

  Link: https://lore.kernel.org/all/ZmQovC6TbDpTb3c8@surfacebook.localdomain/
[1]
  Link: https://lore.kernel.org/all/CAHp75VeDsVt0GQYUFxLM+obfmqXBPa3hM3YMsFbc26uzWZG-SQ@mail.gmail.com/
[2]

(note [*] references)

> Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
> Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>

...

> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/device.h>
> +#include <linux/dev_printk.h>
> +#include <linux/types.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>

Keep above sorted alphabetically, it makes it easier to read and
follow and see if anything is missing or a leftover.

+ blank line (i.o.w. keep the below headers in a separate group as
this driver is 8250 driver)

> +#include <linux/serial_8250.h>
> +#include <linux/serial_core.h>
> +#include <linux/serial_reg.h>

Only the first one is needed.

> +#include <linux/8250_pci.h>

...

> +static unsigned int mxpcie8250_get_supp_rs(unsigned short device)
> +{
> +       switch (device & MOXA_DEV_ID_IFACE_MASK) {
> +       case 0x0000:
> +       case 0x0600:
> +               return MOXA_SUPP_RS232;
> +       case 0x0100:
> +               return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
> +       case 0x0300:
> +               return MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
> +       }

> +
> +       return 0;

Simply make it a default case.

> +}
> +
> +static unsigned short mxpcie8250_get_nports(unsigned short device)
> +{
> +       switch (device) {
> +       case PCI_DEVICE_ID_MOXA_CP116E_A_A:
> +       case PCI_DEVICE_ID_MOXA_CP116E_A_B:
> +               return 8;
> +       }
> +
> +       return FIELD_GET(MOXA_DEV_ID_NPORTS_MASK, device);

Ditto.

> +}
> +
> +static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
> +                                    unsigned int port_idx,
> +                                    u8 mode)
> +{
> +       void __iomem *uir_addr = priv->bar2_base + MOXA_UIR_OFFSET + port_idx / 2;
> +       u8 cval;
> +
> +       cval = ioread8(uir_addr);
> +
> +       if (port_idx & 1)

% 2

With them (/2, %2) going closer to each other some compilers gain a
few bytes, see this for example
9b3cd5c7099f ("regmap: place foo / 8 and foo % 8 closer to each other").

> +               cval = FIELD_MODIFY(MOXA_ODD_RS_MASK, &cval, mode);
> +       else
> +               cval = FIELD_MODIFY(MOXA_EVEN_RS_MASK, &cval, mode);
> +
> +       iowrite8(cval, uir_addr);
> +}

...

> +       priv = devm_kzalloc(dev, struct_size(priv, line, num_ports), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       priv->supp_rs = mxpcie8250_get_supp_rs(device);
> +       priv->num_ports = num_ports;

Swap these two lines (by the order) and add __counted_by() to the data
structure.

...

> +       for (i = 0; i < num_ports; i++) {

Seems i is not used outside the loop, hence just

       for (int i = 0; i < num_ports; i++) {

> +       }

...

> +static void mxpcie8250_remove(struct pci_dev *pdev)
> +{
> +       struct mxpcie8250 *priv = dev_get_drvdata(&pdev->dev);

platform_get_drvdata() IIRC

> +       unsigned int i;
> +
> +       for (i = 0; i < priv->num_ports; i++)

As per above.

> +               serial8250_unregister_port(priv->line[i]);
> +}

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH v2 02/15] serial: core: add uart_iotype_mmio/legacy_io helper functions
From: Maciej W. Rozycki @ 2026-05-04 11:44 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Hugo Villeneuve, Greg Kroah-Hartman, Jiri Slaby,
	Ilpo Järvinen, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <afhdTI_jAQrIsPSZ@ashevche-desk.local>

On Mon, 4 May 2026, Andy Shevchenko wrote:

> > > Why do we use 'legacy'? Still in use in modern CPUs...
> > 
> >  Deprecated in PCIe and not available in numerous systems.  Also actually 
> > called "legacy" in some serial port datasheets aged ~20 years now.  While 
> > some contemporary CPUs indeed retain the port I/O address space, it's for 
> > legacy use anyway, you don't want to rely on it in new designs.
> 
> For the holder of the new (modern) CPU which supports the IO ports, this is
> definitely not a legacy interface despite on whatever PCIe or other datasheets
> call it.

 I appreciate your point of view, however I disagree that the presence of 
an interface in a contemporary chip makes the interface modern.

 I see the port I/O space so much legacy as say the PC/AT DMA controller 
(8237 pair), which is similarly present in current x86 chipsets.  If this 
stuff was not present, such as say the PC/AT interrupt controller (8259A 
pair), which I believe has been removed from some x86 system designs, then 
it would be obsolete/removed rather than legacy.

 It is analogous to PCI/e systems that lack a southbridge and are called 
"legacy-free", as the whole southbridge stuff, the main consumer of the 
port I/O space still remaining in use, is legacy nowadays (the other one I 
know of being the 8255-based PC parallel port, which has been considered a 
legacy interface as well, even though you can still buy and plug one into 
a modern PCIe system).

 NB I have a couple of modern x86 CPUs around too that support the port 
I/O space, but it doesn't change my view as to the nomenclature.

 FWIW,

  Maciej

^ permalink raw reply

* [PATCH V2] serial: qcom-geni: Avoid probing debug console UART without console support
From: Aniket Randive @ 2026-05-04 10:10 UTC (permalink / raw)
  To: gregkh, jirislaby, linux-arm-msm, linux-kernel, linux-serial,
	praveen.talari, anup.kulkarni, dmitry.baryshkov, viken.dadhaniya
  Cc: Aniket Randive

When CONFIG_SERIAL_QCOM_GENI_CONSOLE is disabled, the driver still
advertises the debug UART compatible strings ("qcom,geni-debug-uart"
and "qcom,sa8255p-geni-debug-uart") in its of_match table. This lets the
driver match and probe console UART DT nodes even though console
support is not built. As a result, the console port is never registered
with the UART core and uart_add_one_port() fails with -EINVAL.

Fix this by only including the debug UART compatible entries in the
match table when CONFIG_SERIAL_QCOM_GENI_CONSOLE is enabled, preventing
the driver from probing console UART nodes when console support is
absent.

Reviewed-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
Signed-off-by: Aniket Randive <aniket.randive@oss.qualcomm.com>
---

Changes in v2: 
  - Fixed kernel test robot warning (no functional change).

 drivers/tty/serial/qcom_geni_serial.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 9854bb2406e3..d2287a8b1d54 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1992,6 +1992,7 @@ static int qcom_geni_serial_resume(struct device *dev)
 	return ret;
 }
 
+#if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE)
 static const struct qcom_geni_device_data qcom_geni_console_data = {
 	.console = true,
 	.mode = GENI_SE_FIFO,
@@ -2000,14 +2001,6 @@ static const struct qcom_geni_device_data qcom_geni_console_data = {
 	.power_state = geni_serial_resource_state,
 };
 
-static const struct qcom_geni_device_data qcom_geni_uart_data = {
-	.console = false,
-	.mode = GENI_SE_DMA,
-	.resources_init = geni_serial_resource_init,
-	.set_rate = geni_serial_set_rate,
-	.power_state = geni_serial_resource_state,
-};
-
 static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
 	.console = true,
 	.mode = GENI_SE_FIFO,
@@ -2019,6 +2012,15 @@ static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
 	.resources_init = geni_serial_pwr_init,
 	.set_rate = geni_serial_set_level,
 };
+#endif
+
+static const struct qcom_geni_device_data qcom_geni_uart_data = {
+	.console = false,
+	.mode = GENI_SE_DMA,
+	.resources_init = geni_serial_resource_init,
+	.set_rate = geni_serial_set_rate,
+	.power_state = geni_serial_resource_state,
+};
 
 static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
 	.console = false,
@@ -2039,6 +2041,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
 };
 
 static const struct of_device_id qcom_geni_serial_match_table[] = {
+#if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE)
 	{
 		.compatible = "qcom,geni-debug-uart",
 		.data = &qcom_geni_console_data,
@@ -2047,6 +2050,7 @@ static const struct of_device_id qcom_geni_serial_match_table[] = {
 		.compatible = "qcom,sa8255p-geni-debug-uart",
 		.data = &sa8255p_qcom_geni_console_data,
 	},
+#endif
 	{
 		.compatible = "qcom,geni-uart",
 		.data = &qcom_geni_uart_data,
-- 
2.34.1


^ permalink raw reply related

* [PATCH 15/15] serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL
From: Crescent Hsieh @ 2026-05-04  8:49 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

The MUEx50 UART exposes a programmable RX trigger level via the RTL
register.

Implement uart_port RX trigger set/get callbacks for the mxpcie driver
and wire them up to the generic rx_trig_bytes sysfs interface. Store the
configured trigger level in the per-port private data.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 28 ++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 5bf15ca78228..cc317202b658 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -111,6 +111,7 @@
 
 struct mxpcie8250_port {
 	int line;
+	u8 rx_trig_level;
 	unsigned long event_flags;
 	struct uart_port *port;
 	struct work_struct work;
@@ -262,6 +263,7 @@ static void mxpcie8250_set_termios(struct uart_port *port,
 
 static int mxpcie8250_startup(struct uart_port *port)
 {
+	struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned int i;
 	int ret;
@@ -277,7 +279,7 @@ static int mxpcie8250_startup(struct uart_port *port)
 	serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950);
 
 	serial_out(up, MOXA_PUART_TTL, 0);
-	serial_out(up, MOXA_PUART_RTL, 96);
+	serial_out(up, MOXA_PUART_RTL, priv->port[port->port_id].rx_trig_level);
 	serial_out(up, MOXA_PUART_FCL, 16);
 	serial_out(up, MOXA_PUART_FCH, 110);
 
@@ -477,6 +479,27 @@ static void mxpcie8250_break_ctl(struct uart_port *port, int break_state)
 		serial8250_do_break_ctl(port, break_state);
 }
 
+static int mxpcie8250_set_rxtrig(struct uart_port *port, unsigned char bytes)
+{
+	struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
+	struct uart_8250_port *up = up_to_u8250p(port);
+
+	if (bytes > 128)
+		return -EINVAL;
+
+	serial_out(up, MOXA_PUART_RTL, bytes);
+	priv->port[port->port_id].rx_trig_level = bytes;
+
+	return 0;
+}
+
+static int mxpcie8250_get_rxtrig(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+
+	return serial_in(up, MOXA_PUART_RTL);
+}
+
 static void mxpcie8250_work_handler(struct work_struct *work)
 {
 	struct mxpcie8250_port *priv_port = container_of(work, struct mxpcie8250_port, work);
@@ -591,6 +614,8 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.unthrottle = mxpcie8250_unthrottle;
 	up.port.handle_irq = mxpcie8250_handle_irq;
 	up.port.break_ctl = mxpcie8250_break_ctl;
+	up.port.set_rxtrig = mxpcie8250_set_rxtrig;
+	up.port.get_rxtrig = mxpcie8250_get_rxtrig;
 
 	for (i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
@@ -608,6 +633,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 		}
 		new_port = serial8250_get_port(priv->port[i].line);
 
+		priv->port[i].rx_trig_level = 96;
 		priv->port[i].port = &new_port->port;
 
 		INIT_WORK(&priv->port[i].work, mxpcie8250_work_handler);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 14/15] serial: 8250: allow UART drivers to override rx_trig_bytes handling
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

The rx_trig_bytes sysfs attribute currently relies on 8250-internal
helper functions and assumes a fixed mapping between trigger levels and
FIFO behavior.

Some UARTs provide hardware-specific RX trigger mechanisms that do not
fit this model. Add optional uart_port callbacks for setting and getting
the RX trigger level, and use them when provided, while preserving the
existing 8250 helpers as the default fallback.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_core.c |  4 ++++
 drivers/tty/serial/8250/8250_port.c | 14 ++++++++++++--
 include/linux/serial_core.h         |  2 ++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 0a3355eb4bc3..a0a53b642d6c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -802,6 +802,10 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		uart->port.pm = up->port.pm;
 	if (up->port.handle_break)
 		uart->port.handle_break = up->port.handle_break;
+	if (up->port.set_rxtrig)
+		uart->port.set_rxtrig = up->port.set_rxtrig;
+	if (up->port.get_rxtrig)
+		uart->port.get_rxtrig = up->port.get_rxtrig;
 	if (up->dl_read)
 		uart->dl_read = up->dl_read;
 	if (up->dl_write)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 72ecc0112b8a..2ece8af5d149 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2999,9 +2999,14 @@ static ssize_t rx_trig_bytes_show(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	struct tty_port *port = dev_get_drvdata(dev);
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
 	int rxtrig_bytes;
 
-	rxtrig_bytes = do_serial8250_get_rxtrig(port);
+	if (uport->get_rxtrig)
+		rxtrig_bytes = uport->get_rxtrig(uport);
+	else
+		rxtrig_bytes = do_serial8250_get_rxtrig(port);
 	if (rxtrig_bytes < 0)
 		return rxtrig_bytes;
 
@@ -3044,6 +3049,8 @@ static ssize_t rx_trig_bytes_store(struct device *dev,
 	struct device_attribute *attr, const char *buf, size_t count)
 {
 	struct tty_port *port = dev_get_drvdata(dev);
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
 	unsigned char bytes;
 	int ret;
 
@@ -3054,7 +3061,10 @@ static ssize_t rx_trig_bytes_store(struct device *dev,
 	if (ret < 0)
 		return ret;
 
-	ret = do_serial8250_set_rxtrig(port, bytes);
+	if (uport->set_rxtrig)
+		ret = uport->set_rxtrig(uport, bytes);
+	else
+		ret = do_serial8250_set_rxtrig(port, bytes);
 	if (ret < 0)
 		return ret;
 
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index d9e5e3d02003..bba6223d7b12 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -468,6 +468,8 @@ struct uart_port {
 	void			(*pm)(struct uart_port *, unsigned int state,
 				      unsigned int old);
 	void			(*handle_break)(struct uart_port *);
+	int			(*set_rxtrig)(struct uart_port *port, unsigned char bytes);
+	int			(*get_rxtrig)(struct uart_port *port);
 	int			(*rs485_config)(struct uart_port *,
 						struct ktermios *termios,
 						struct serial_rs485 *rs485);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 13/15] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

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>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 52 +++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 94c3552b9798..5bf15ca78228 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -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) */
@@ -426,6 +427,56 @@ 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 char tx_byte = 0x01;
+	unsigned int baud, quot;
+	unsigned long flags;
+	u8 sfr;
+
+	uart_port_lock_irqsave(port, &flags);
+
+	if (break_state == -1) {
+		serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
+		serial_out(up, UART_DLL, 0);
+		serial_out(up, UART_DLM, 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);
+	}
+	uart_port_unlock_irqrestore(port, flags);
+}
+
+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_work_handler(struct work_struct *work)
 {
 	struct mxpcie8250_port *priv_port = container_of(work, struct mxpcie8250_port, work);
@@ -539,6 +590,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 (i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 12/15] serial: 8250: allow low-level drivers to override break control
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

Some UARTs require driver-specific handling for break signaling, which
cannot be expressed by the generic 8250 break implementation alone.

Add an optional uart_port break_ctl callback and route
serial8250_break_ctl() through it when provided. Rename the existing
8250 implementation to serial8250_do_break_ctl() and export it under the
SERIAL_8250_PCI namespace so low-level drivers can reuse the default
8250 behavior when appropriate.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_core.c |  2 ++
 drivers/tty/serial/8250/8250_port.c | 11 ++++++++++-
 include/linux/serial_8250.h         |  1 +
 include/linux/serial_core.h         |  1 +
 4 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index bfa421ab3253..0a3355eb4bc3 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -796,6 +796,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		uart->port.startup = up->port.startup;
 	if (up->port.shutdown)
 		uart->port.shutdown = up->port.shutdown;
+	if (up->port.break_ctl)
+		uart->port.break_ctl = up->port.break_ctl;
 	if (up->port.pm)
 		uart->port.pm = up->port.pm;
 	if (up->port.handle_break)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index a17fdb5d68d2..72ecc0112b8a 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1937,7 +1937,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
 		serial8250_do_set_mctrl(port, mctrl);
 }
 
-static void serial8250_break_ctl(struct uart_port *port, int break_state)
+void serial8250_do_break_ctl(struct uart_port *port, int break_state)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
@@ -1950,6 +1950,15 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
 		up->lcr &= ~UART_LCR_SBC;
 	serial_port_out(port, UART_LCR, up->lcr);
 }
+EXPORT_SYMBOL_NS_GPL(serial8250_do_break_ctl, "SERIAL_8250_PCI");
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+	if (port->break_ctl)
+		port->break_ctl(port, break_state);
+	else
+		serial8250_do_break_ctl(port, break_state);
+}
 
 /* Returns true if @bits were set, false on timeout */
 static bool wait_for_lsr(struct uart_8250_port *up, int bits)
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 01efdce0fda0..5ae00dede026 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -192,6 +192,7 @@ void serial8250_do_shutdown(struct uart_port *port);
 void serial8250_do_pm(struct uart_port *port, unsigned int state,
 		      unsigned int oldstate);
 void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
+void serial8250_do_break_ctl(struct uart_port *port, int break_state);
 void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 			       unsigned int quot);
 int fsl8250_handle_irq(struct uart_port *port);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 666430b47899..d9e5e3d02003 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -463,6 +463,7 @@ struct uart_port {
 	void			(*shutdown)(struct uart_port *port);
 	void			(*throttle)(struct uart_port *port);
 	void			(*unthrottle)(struct uart_port *port);
+	void			(*break_ctl)(struct uart_port *port, int break_state);
 	int			(*handle_irq)(struct uart_port *);
 	void			(*pm)(struct uart_port *, unsigned int state,
 				      unsigned int old);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 11/15] serial: 8250_mxpcie: support serial interface mode switching
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

Moxa PCIe multiport serial boards support switching the serial interface
mode between RS232, RS422, RS485-2W, and RS485-4W via on-board control
registers.

Implement an rs485_config() callback and map TIOCSRS485 requests to the
corresponding hardware modes using serial_rs485 flags:

  - RS232                  = (no flags set)
  - RS422                  = SER_RS485_ENABLED | SER_RS485_MODE_RS422
  - RS485_2W (half-duplex) = SER_RS485_ENABLED
  - RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX

This allows users to reconfigure the serial mode at runtime via ioctl().

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 45 +++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 8dc1b7b0af04..94c3552b9798 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -129,6 +129,10 @@ enum {
 	MOXA_SUPP_RS485 = BIT(2),
 };
 
+static const struct serial_rs485 mxpcie8250_rs485_supported = {
+	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_MODE_RS422,
+};
+
 static bool mxpcie8250_is_mini_pcie(unsigned short device)
 {
 	if (device == PCI_DEVICE_ID_MOXA_CP102N ||
@@ -185,6 +189,38 @@ static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
 	iowrite8(cval, uir_addr);
 }
 
+/*
+ * Moxa PCIe multiport serial boards support switching serial interfaces
+ * via the ioctl() command "TIOCSRS485". Supported modes and corresponding
+ * flags in "serial_rs485":
+ *
+ *	RS232			= (no flags set)
+ *	RS422			= SER_RS485_ENABLED | SER_RS485_MODE_RS422
+ *	RS485_2W (half-duplex)	= SER_RS485_ENABLED
+ *	RS485_4W (full-duplex)	= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX
+ */
+static int mxpcie8250_rs485_config(struct uart_port *port,
+				   struct ktermios *termios,
+				   struct serial_rs485 *rs485)
+{
+	struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
+	u8 mode = MOXA_UIR_RS232;
+
+	if (rs485->flags & SER_RS485_ENABLED) {
+		if (rs485->flags & SER_RS485_MODE_RS422)
+			mode = MOXA_UIR_RS422;
+		else if (rs485->flags & SER_RS485_RX_DURING_TX)
+			mode = MOXA_UIR_RS485_4W;
+		else
+			mode = MOXA_UIR_RS485_2W;
+	} else if (!(priv->supp_rs & MOXA_SUPP_RS232)) {
+		return -ENODEV;
+	}
+	mxpcie8250_set_interface(priv, port->port_id, mode);
+
+	return 0;
+}
+
 static void mxpcie8250_set_termios(struct uart_port *port,
 				   struct ktermios *new,
 				   const struct ktermios *old)
@@ -435,9 +471,14 @@ static void mxpcie8250_setup_port(struct pci_dev *pdev,
 	int offset = idx * MOXA_PUART_OFFSET;
 	u8 init_mode = MOXA_UIR_RS232;
 
-	if (!(priv->supp_rs & MOXA_SUPP_RS232))
+	if (priv->supp_rs & MOXA_SUPP_RS485) {
+		up->port.rs485_config = mxpcie8250_rs485_config;
+		up->port.rs485_supported = mxpcie8250_rs485_supported;
+	}
+	if (!(priv->supp_rs & MOXA_SUPP_RS232)) {
 		init_mode = MOXA_UIR_RS422;
-
+		up->port.rs485.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422;
+	}
 	mxpcie8250_set_interface(priv, idx, init_mode);
 
 	if (idx == 3 &&
-- 
2.43.0


^ permalink raw reply related

* [PATCH 10/15] serial: 8250_mxpcie: defer uart_write_wakeup() to workqueue
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

Avoid calling uart_write_wakeup() directly from the interrupt-driven TX
path.

Defer the wakeup to a per-port work item and coalesce multiple TX events
using a pending flag, so only one wakeup is scheduled while a previous
one is still outstanding.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 34 +++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 19233c3c5f1f..8dc1b7b0af04 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -18,6 +18,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 #include <linux/tty_flip.h>
+#include <linux/workqueue.h>
 #include <linux/8250_pci.h>
 
 #include "8250.h"
@@ -105,8 +106,13 @@
 #define MOXA_EVEN_RS_MASK	GENMASK(3, 0)
 #define MOXA_ODD_RS_MASK	GENMASK(7, 4)
 
+#define MOXA_EVENT_TXLOW	BIT(0)
+
 struct mxpcie8250_port {
 	int line;
+	unsigned long event_flags;
+	struct uart_port *port;
+	struct work_struct work;
 };
 
 struct mxpcie8250 {
@@ -327,6 +333,8 @@ static void mxpcie8250_tx_chars(struct uart_8250_port *up)
 {
 	struct uart_port *port = &up->port;
 	struct tty_port *tport = &port->state->port;
+	struct device *dev = port->dev;
+	struct mxpcie8250 *priv = dev_get_drvdata(dev);
 	unsigned int i, count, txsize;
 	unsigned char c;
 
@@ -347,9 +355,10 @@ static void mxpcie8250_tx_chars(struct uart_8250_port *up)
 
 		serial_out(up, MOXA_PUART_TX_FIFO_MEM + i, c);
 	}
-	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
-		uart_write_wakeup(port);
-
+	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) {
+		if (!test_and_set_bit(MOXA_EVENT_TXLOW, &priv->port[port->port_id].event_flags))
+			schedule_work(&priv->port[port->port_id].work);
+	}
 	if (kfifo_is_empty(&tport->xmit_fifo) && !(up->capabilities & UART_CAP_RPM))
 		port->ops->stop_tx(port);
 }
@@ -381,6 +390,14 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
 	return 1;
 }
 
+static void mxpcie8250_work_handler(struct work_struct *work)
+{
+	struct mxpcie8250_port *priv_port = container_of(work, struct mxpcie8250_port, work);
+
+	if (test_and_clear_bit(MOXA_EVENT_TXLOW, &priv_port->event_flags))
+		uart_write_wakeup(priv_port->port);
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -436,7 +453,7 @@ static void mxpcie8250_setup_port(struct pci_dev *pdev,
 static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct device *dev = &pdev->dev;
-	struct uart_8250_port up = {};
+	struct uart_8250_port up = {}, *new_port;
 	struct mxpcie8250 *priv;
 	unsigned short device = pdev->device;
 	unsigned int i, num_ports;
@@ -496,6 +513,11 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 				up.port.iotype, priv->port[i].line);
 			break;
 		}
+		new_port = serial8250_get_port(priv->port[i].line);
+
+		priv->port[i].port = &new_port->port;
+
+		INIT_WORK(&priv->port[i].work, mxpcie8250_work_handler);
 	}
 	dev_set_drvdata(dev, priv);
 
@@ -507,8 +529,10 @@ static void mxpcie8250_remove(struct pci_dev *pdev)
 	struct mxpcie8250 *priv = dev_get_drvdata(&pdev->dev);
 	unsigned int i;
 
-	for (i = 0; i < priv->num_ports; i++)
+	for (i = 0; i < priv->num_ports; i++) {
+		cancel_work_sync(&priv->port[i].work);
 		serial8250_unregister_port(priv->port[i].line);
+	}
 }
 
 static const struct pci_device_id mxpcie8250_pci_ids[] = {
-- 
2.43.0


^ permalink raw reply related

* [PATCH 09/15] serial: 8250_mxpcie: introduce per-port private data structure
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

Introduce a private per-port data structure for the mxpcie driver and
replace the shared flexible array of registered lines with an array of
per-port objects.

This prepares the driver for storing per-port state needed by subsequent
features.

No functional change intended.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 5fe07f6947ef..19233c3c5f1f 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -105,12 +105,16 @@
 #define MOXA_EVEN_RS_MASK	GENMASK(3, 0)
 #define MOXA_ODD_RS_MASK	GENMASK(7, 4)
 
+struct mxpcie8250_port {
+	int line;
+};
+
 struct mxpcie8250 {
 	unsigned int supp_rs;
 	unsigned int num_ports;
 	void __iomem *bar1_base; /* UART registers (MMIO) */
 	void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */
-	int line[];
+	struct mxpcie8250_port port[];
 };
 
 enum {
@@ -444,7 +448,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 
 	num_ports = mxpcie8250_get_nports(device);
 
-	priv = devm_kzalloc(dev, struct_size(priv, line, num_ports), GFP_KERNEL);
+	priv = devm_kzalloc(dev, struct_size(priv, port, num_ports), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
@@ -484,12 +488,12 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 		dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n",
 			up.port.iobase, up.port.irq, up.port.iotype);
 
-		priv->line[i] = serial8250_register_8250_port(&up);
-		if (priv->line[i] < 0) {
+		priv->port[i].line = serial8250_register_8250_port(&up);
+		if (priv->port[i].line < 0) {
 			dev_err(dev,
 				"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
 				up.port.iobase, up.port.irq,
-				up.port.iotype, priv->line[i]);
+				up.port.iotype, priv->port[i].line);
 			break;
 		}
 	}
@@ -504,7 +508,7 @@ static void mxpcie8250_remove(struct pci_dev *pdev)
 	unsigned int i;
 
 	for (i = 0; i < priv->num_ports; i++)
-		serial8250_unregister_port(priv->line[i]);
+		serial8250_unregister_port(priv->port[i].line);
 }
 
 static const struct pci_device_id mxpcie8250_pci_ids[] = {
-- 
2.43.0


^ permalink raw reply related

* [PATCH 08/15] serial: 8250_mxpcie: speed up TX using memory-mapped FIFO window
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

The MUEx50 UART provides a memory-mapped TX FIFO data window along with
a TX FIFO level counter.

Fill the TX FIFO in bulk via the MMIO FIFO window based on available
FIFO space, and use this path from the mxpcie interrupt handler instead
of the generic serial8250_tx_chars() helper.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 35 ++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index f13fcb090df7..5fe07f6947ef 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -86,8 +86,10 @@
 #define MOXA_PUART_FCL		0x12	/* Flow Control Low Trigger Level */
 #define MOXA_PUART_FCH		0x13	/* Flow Control High Trigger Level */
 #define MOXA_PUART_RX_FIFO_CNT	0x15	/* Rx FIFO Data Counter */
+#define MOXA_PUART_TX_FIFO_CNT	0x16	/* Tx FIFO Data Counter */
 
 #define MOXA_PUART_RX_FIFO_MEM	0x100	/* Memory Space to Rx FIFO Data Register */
+#define MOXA_PUART_TX_FIFO_MEM	0x100	/* Memory Space to Tx FIFO Data Register */
 
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
@@ -317,6 +319,37 @@ static u16 mxpcie8250_rx_chars(struct uart_8250_port *up, u16 lsr)
 	return serial8250_rx_chars(up, lsr);
 }
 
+static void mxpcie8250_tx_chars(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	struct tty_port *tport = &port->state->port;
+	unsigned int i, count, txsize;
+	unsigned char c;
+
+	if (port->x_char) {
+		uart_xchar_out(port, UART_TX);
+		return;
+	}
+	if (uart_tx_stopped(port) || kfifo_is_empty(&tport->xmit_fifo)) {
+		port->ops->stop_tx(port);
+		return;
+	}
+	txsize = serial_in(up, MOXA_PUART_TX_FIFO_CNT);
+	count = min(kfifo_len(&tport->xmit_fifo), port->fifosize - txsize);
+
+	for (i = 0; i < count; ++i) {
+		if (!uart_fifo_get(port, &c))
+			break;
+
+		serial_out(up, MOXA_PUART_TX_FIFO_MEM + i, c);
+	}
+	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (kfifo_is_empty(&tport->xmit_fifo) && !(up->capabilities & UART_CAP_RPM))
+		port->ops->stop_tx(port);
+}
+
 static int mxpcie8250_handle_irq(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
@@ -337,7 +370,7 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
 	serial8250_modem_status(up);
 
 	if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
-		serial8250_tx_chars(up);
+		mxpcie8250_tx_chars(up);
 
 	uart_unlock_and_check_sysrq_irqrestore(port, flags);
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 07/15] serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

The MUEx50 UART provides a memory-mapped RX FIFO data window along with
an RX FIFO byte counter.

When no break or error conditions are present, read received data in
bulk via the MMIO FIFO window and push it to the tty layer in one
operation. Fall back to the generic 8250 RX path for break and error
handling.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 32 +++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 9860f2ac2572..f13fcb090df7 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -17,6 +17,7 @@
 #include <linux/serial_8250.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
+#include <linux/tty_flip.h>
 #include <linux/8250_pci.h>
 
 #include "8250.h"
@@ -48,6 +49,7 @@
 /* UART */
 #define MOXA_PUART_BASE_BAUD	921600
 #define MOXA_PUART_OFFSET	0x200
+#define MOXA_PUART_FIFO_SIZE	128
 
 /* Special Function Register (SFR) */
 #define MOXA_PUART_SFR		0x07
@@ -83,6 +85,9 @@
 #define MOXA_PUART_RTL		0x11	/* Rx Interrupt Trigger Level */
 #define MOXA_PUART_FCL		0x12	/* Flow Control Low Trigger Level */
 #define MOXA_PUART_FCH		0x13	/* Flow Control High Trigger Level */
+#define MOXA_PUART_RX_FIFO_CNT	0x15	/* Rx FIFO Data Counter */
+
+#define MOXA_PUART_RX_FIFO_MEM	0x100	/* Memory Space to Rx FIFO Data Register */
 
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
@@ -265,6 +270,29 @@ static void mxpcie8250_unthrottle(struct uart_port *port)
 	uart_port_unlock_irqrestore(port, flags);
 }
 
+static void mxpcie8250_do_rx_chars(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	struct tty_port *tport = &port->state->port;
+	u8 buf[MOXA_PUART_FIFO_SIZE];
+	int recv_room, gdl, i;
+
+	recv_room = tty_buffer_request_room(tport, port->fifosize);
+	if (!recv_room)
+		return;
+
+	gdl = serial_in(up, MOXA_PUART_RX_FIFO_CNT);
+	if (gdl > recv_room)
+		gdl = recv_room;
+
+	for (i = 0; i < gdl; ++i)
+		buf[i] = serial_in(up, MOXA_PUART_RX_FIFO_MEM + i);
+
+	port->icount.rx += gdl;
+	tty_insert_flip_string(tport, buf, gdl);
+	tty_flip_buffer_push(tport);
+}
+
 static u16 mxpcie8250_rx_chars(struct uart_8250_port *up, u16 lsr)
 {
 	struct uart_port *port = &up->port;
@@ -282,6 +310,10 @@ static u16 mxpcie8250_rx_chars(struct uart_8250_port *up, u16 lsr)
 	return lsr;
 
 do_rx:
+	if (!(lsr & UART_LSR_BRK_ERROR_BITS)) {
+		mxpcie8250_do_rx_chars(up);
+		return lsr;
+	}
 	return serial8250_rx_chars(up, lsr);
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 06/15] serial: 8250_mxpcie: add custom handle_irq callback
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

Add a mxpcie-specific handle_irq() implementation for Moxa PCIe serial
ports.

This keeps the interrupt handling self-contained in the driver and
provides a hook point for MUEx50-specific RX/TX paths added in subsequent
patches. The handler processes RX, updates modem status, and handles TX
when THRE is asserted.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 48 +++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 99fd789b7665..9860f2ac2572 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -265,6 +265,53 @@ static void mxpcie8250_unthrottle(struct uart_port *port)
 	uart_port_unlock_irqrestore(port, flags);
 }
 
+static u16 mxpcie8250_rx_chars(struct uart_8250_port *up, u16 lsr)
+{
+	struct uart_port *port = &up->port;
+
+	if (!(lsr & (UART_LSR_DR | UART_LSR_BI)))
+		return lsr;
+
+	if (!(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)))
+		goto do_rx;
+	if (lsr & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
+		goto do_rx;
+	if (port->read_status_mask & UART_LSR_DR)
+		goto do_rx;
+
+	return lsr;
+
+do_rx:
+	return serial8250_rx_chars(up, lsr);
+}
+
+static int mxpcie8250_handle_irq(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	unsigned long flags;
+	u16 lsr;
+	u8 iir;
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return 0;
+
+	uart_port_lock_irqsave(port, &flags);
+
+	lsr = serial_lsr_in(up);
+
+	lsr = mxpcie8250_rx_chars(up, lsr);
+
+	serial8250_modem_status(up);
+
+	if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
+		serial8250_tx_chars(up);
+
+	uart_unlock_and_check_sysrq_irqrestore(port, flags);
+
+	return 1;
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -364,6 +411,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.shutdown = mxpcie8250_shutdown;
 	up.port.throttle = mxpcie8250_throttle;
 	up.port.unthrottle = mxpcie8250_unthrottle;
+	up.port.handle_irq = mxpcie8250_handle_irq;
 
 	for (i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 05/15] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware
From: Crescent Hsieh @ 2026-05-04  8:48 UTC (permalink / raw)
  To: gregkh, jirislaby, ilpo.jarvinen, andy.shevchenko
  Cc: crescentcy.hsieh, fangpingfp.cheng, linux-kernel, linux-serial
In-Reply-To: <20260504084900.22380-1-crescentcy.hsieh@moxa.com>

The MUEx50 UART can handle in-band software flow control (XON/XOFF)
directly in hardware.

Program the on-chip XON/XOFF characters from termios settings and enable
the corresponding MUEx50 flow control modes when IXON or IXOFF is
requested. Provide throttle and unthrottle callbacks so RX can be
stopped and resumed cleanly.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 62 ++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 89086aa7b228..99fd789b7665 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -54,13 +54,31 @@
 #define MOXA_PUART_SFR_950	BIT(5)
 
 /* Enhanced Function Register (EFR) */
+/*
+ * EFR[1:0] - In-Band Receive Flow Control Mode (Compare XON/XOFF):
+ *	00b (0x00) = Disabled
+ *	01b (0x01) = Recognize XON2 & XOFF2 as XOFF character
+ *	10b (0x02) = Recognize XON1 & XOFF1 as XOFF character
+ *	11b (0x03) = Depends on EFR[3:2]
+ * EFR[3:2] - In-Band Transmit Flow Control Mode (Insert XON/XOFF):
+ *	00b (0x00) = Disabled
+ *	01b (0x04) = Use XON2 & XOFF2 as XOFF character
+ *	10b (0x08) = Use XON1 & XOFF1 as XOFF character
+ *	11b (0x0C) = Reserved
+ */
 #define MOXA_PUART_EFR			0x0A
+#define MOXA_PUART_EFR_RX_FLOW		0x02	/* Recognize XON1 & XOFF1 as XOFF character */
+#define MOXA_PUART_EFR_TX_FLOW		0x08	/* Use XON1 & XOFF1 as XOFF character */
 #define MOXA_PUART_EFR_ENHANCED		BIT(4)
 #define MOXA_PUART_EFR_AUTO_RTS		BIT(6)
 #define MOXA_PUART_EFR_AUTO_CTS		BIT(7)
 #define MOXA_PUART_EFR_RX_FLOW_MASK	GENMASK(1, 0)
 #define MOXA_PUART_EFR_TX_FLOW_MASK	GENMASK(3, 2)
 
+#define MOXA_PUART_XON1		0x0B
+#define MOXA_PUART_XON2		0x0C
+#define MOXA_PUART_XOFF1	0x0D
+#define MOXA_PUART_XOFF2	0x0E
 #define MOXA_PUART_TTL		0x10	/* Tx Interrupt Trigger Level */
 #define MOXA_PUART_RTL		0x11	/* Rx Interrupt Trigger Level */
 #define MOXA_PUART_FCL		0x12	/* Flow Control Low Trigger Level */
@@ -161,7 +179,7 @@ static void mxpcie8250_set_termios(struct uart_port *port,
 
 	serial8250_do_set_termios(port, new, old);
 
-	up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
+	up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF);
 
 	efr = serial_in(up, MOXA_PUART_EFR);
 	efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
@@ -170,6 +188,21 @@ static void mxpcie8250_set_termios(struct uart_port *port,
 		efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
 		up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
 	}
+	/* Set on-chip software flow control character */
+	serial_out(up, MOXA_PUART_XON1, START_CHAR(tty));
+	serial_out(up, MOXA_PUART_XON2, START_CHAR(tty));
+	serial_out(up, MOXA_PUART_XOFF1, STOP_CHAR(tty));
+	serial_out(up, MOXA_PUART_XOFF2, STOP_CHAR(tty));
+
+	efr &= ~(MOXA_PUART_EFR_RX_FLOW_MASK | MOXA_PUART_EFR_TX_FLOW_MASK);
+
+	if (I_IXON(tty))
+		efr |= MOXA_PUART_EFR_RX_FLOW;
+
+	if (I_IXOFF(tty)) {
+		efr |= MOXA_PUART_EFR_TX_FLOW;
+		up->port.status |= UPSTAT_AUTOXOFF;
+	}
 	serial_out(up, MOXA_PUART_EFR, efr);
 }
 
@@ -207,6 +240,31 @@ static void mxpcie8250_shutdown(struct uart_port *port)
 	serial8250_do_shutdown(port);
 }
 
+static void mxpcie8250_throttle(struct uart_port *port)
+{
+	unsigned long flags;
+
+	uart_port_lock_irqsave(port, &flags);
+
+	port->ops->stop_rx(port);
+
+	uart_port_unlock_irqrestore(port, flags);
+}
+
+static void mxpcie8250_unthrottle(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	unsigned long flags;
+
+	uart_port_lock_irqsave(port, &flags);
+
+	up->ier |= UART_IER_RLSI | UART_IER_RDI;
+	port->read_status_mask |= UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+
+	uart_port_unlock_irqrestore(port, flags);
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -304,6 +362,8 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.set_termios = mxpcie8250_set_termios;
 	up.port.startup = mxpcie8250_startup;
 	up.port.shutdown = mxpcie8250_shutdown;
+	up.port.throttle = mxpcie8250_throttle;
+	up.port.unthrottle = mxpcie8250_unthrottle;
 
 	for (i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox