[PATCH v3] ns16550: add support for polling mode when device tree is used

Oleksii Kurochko posted 1 patch 9 months, 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://gitlab.com/xen-project/patchew/xen tags/patchew/d929b43814e6e1a247b954c4be40a35d61b6a45a.1690208840.git.oleksii.kurochko@gmail.com
xen/drivers/char/console.c |  2 ++
xen/drivers/char/ns16550.c | 38 ++++++++++++++++++++++++++++----------
2 files changed, 30 insertions(+), 10 deletions(-)
[PATCH v3] ns16550: add support for polling mode when device tree is used
Posted by Oleksii Kurochko 9 months, 1 week ago
RISC-V doesn't support interrupts for the time being, so it would be nice to
have polling mode.

The patch adds new option to console='...,polling' which forces driver to use
polling mode.

If there is no interrupt property in UART node, then polling will be used.    
According to 4.2.2 National Semiconductor 16450/16550 Compatible UART
Requirements from The Devicetree Specification v0.4
( https://www.devicetree.org/specifications/ ) the interrupt property is        
optional.

Also, it is possible that interrupt '0' can be used for some architectures as
an legal UART interrupt number ( according to dts files in Linux kernel ), so
the check of the return value of platform_get_irq() was updated in case of when
device tree is used for UART initialization.
For example:
https://github.com/torvalds/linux/blob/master/arch/powerpc/boot/dts/ebony.dts#L197

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
 xen/drivers/char/console.c |  2 ++
 xen/drivers/char/ns16550.c | 38 ++++++++++++++++++++++++++++----------
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 0e410fa086..07e9610d77 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -993,6 +993,8 @@ void __init console_init_preirq(void)
 #endif
         else if ( !strncmp(p, "none", 4) )
             continue;
+        else if ( !strncmp(p, "polling", 7 )
+            continue;
         else if ( (sh = serial_parse_handle(p)) >= 0 )
         {
             sercon_handle = sh;
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 2aed6ec707..a6eb3b3997 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -58,7 +58,11 @@ static struct ns16550 {
     struct timer timer;
     struct timer resume_timer;
     unsigned int timeout_ms;
-    bool_t intr_works;
+    enum {
+        intr_off,
+        intr_on,
+        polling,
+    } intr_works;
     bool_t dw_usr_bsy;
 #ifdef NS16550_PCI
     /* PCI card parameters. */
@@ -181,7 +185,7 @@ static void cf_check ns16550_interrupt(
     struct serial_port *port = dev_id;
     struct ns16550 *uart = port->uart;
 
-    uart->intr_works = 1;
+    uart->intr_works = intr_on;
 
     while ( !(ns_read_reg(uart, UART_IIR) & UART_IIR_NOINT) )
     {
@@ -212,7 +216,7 @@ static void cf_check __ns16550_poll(struct cpu_user_regs *regs)
     struct serial_port *port = this_cpu(poll_port);
     struct ns16550 *uart = port->uart;
 
-    if ( uart->intr_works )
+    if ( uart->intr_works == intr_on )
         return; /* Interrupts work - no more polling */
 
     while ( ns_read_reg(uart, UART_LSR) & UART_LSR_DR )
@@ -305,7 +309,8 @@ static void ns16550_setup_preirq(struct ns16550 *uart)
     unsigned char lcr;
     unsigned int  divisor;
 
-    uart->intr_works = 0;
+    if ( uart->intr_works != polling )
+        uart->intr_works = intr_off;
 
     pci_serial_early_init(uart);
 
@@ -394,7 +399,7 @@ static void __init cf_check ns16550_init_irq(struct serial_port *port)
 
 static void ns16550_setup_postirq(struct ns16550 *uart)
 {
-    if ( uart->irq > 0 )
+    if ( uart->intr_works != polling )
     {
         /* Master interrupt enable; also keep DTR/RTS asserted. */
         ns_write_reg(uart,
@@ -473,6 +478,7 @@ static void __init cf_check ns16550_init_postirq(struct serial_port *port)
                 if ( rc )
                 {
                     uart->irq = 0;
+                    uart->intr_works = polling;
                     if ( msi_desc )
                         msi_free_irq(msi_desc);
                     else
@@ -488,7 +494,7 @@ static void __init cf_check ns16550_init_postirq(struct serial_port *port)
     }
 #endif
 
-    if ( uart->irq > 0 )
+    if ( uart->intr_works != polling )
     {
         uart->irqaction.handler = ns16550_interrupt;
         uart->irqaction.name    = "ns16550";
@@ -595,7 +601,9 @@ static void __init cf_check ns16550_endboot(struct serial_port *port)
 static int __init cf_check ns16550_irq(struct serial_port *port)
 {
     struct ns16550 *uart = port->uart;
-    return ((uart->irq > 0) ? uart->irq : -1);
+
+    return ( uart->intr_works != polling ) ?
+            uart->irq : -1;
 }
 
 static void cf_check ns16550_start_tx(struct serial_port *port)
@@ -654,6 +662,9 @@ static void ns16550_init_common(struct ns16550 *uart)
 
     /* Default lsr_mask = UART_LSR_THRE */
     uart->lsr_mask  = UART_LSR_THRE;
+
+    if ( console_has("polling") )
+        uart->intr_works = polling;
 }
 
 #ifdef CONFIG_X86
@@ -1330,9 +1341,12 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
                  * as special only for X86.
                  */
                 if ( uart->irq == 0xff )
+                {
                     uart->irq = 0;
+                    uart->intr_works = polling;
+                }
 #endif
-                if ( !uart->irq )
+                if ( !uart->irq || (uart->intr_works == polling) )
                     printk(XENLOG_INFO
                            "ns16550: %pp: no legacy IRQ, using poll mode\n",
                            &PCI_SBDF(0, b, d, f));
@@ -1552,6 +1566,7 @@ static bool __init parse_positional(struct ns16550 *uart, char **str)
             conf += 3;
             uart->msi = true;
             uart->irq = 0;
+            uart->intr_works = polling;
         }
         else
 #endif
@@ -1791,8 +1806,11 @@ static int __init ns16550_uart_dt_init(struct dt_device_node *dev,
     }
 
     res = platform_get_irq(dev, 0);
-    if ( ! res )
-        return -EINVAL;
+    if ( res < 0 )
+    {
+        printk("there is no interrupt property, polling will be used\n");
+        uart->intr_works = polling;
+    }
     uart->irq = res;
 
     uart->dw_usr_bsy = dt_device_is_compatible(dev, "snps,dw-apb-uart");
-- 
2.41.0


Re: [PATCH v3] ns16550: add support for polling mode when device tree is used
Posted by Jan Beulich 9 months, 1 week ago
On 24.07.2023 16:27, Oleksii Kurochko wrote:
> --- a/xen/drivers/char/console.c
> +++ b/xen/drivers/char/console.c
> @@ -993,6 +993,8 @@ void __init console_init_preirq(void)
>  #endif
>          else if ( !strncmp(p, "none", 4) )
>              continue;
> +        else if ( !strncmp(p, "polling", 7 )
> +            continue;
>          else if ( (sh = serial_parse_handle(p)) >= 0 )
>          {
>              sercon_handle = sh;

Looks like you mean the new option to go under "console=". Besides
this being guesswork because of you not updating the command line
doc, this also is wrong, as the property then applies to all
consoles. What you mean is a control for a specific instance of a
16550 console, which can only possibly go under "com<N>=". I would
suggest to simply extend [<irq>|msi] there to [<irq>|msi|poll].

> @@ -595,7 +601,9 @@ static void __init cf_check ns16550_endboot(struct serial_port *port)
>  static int __init cf_check ns16550_irq(struct serial_port *port)
>  {
>      struct ns16550 *uart = port->uart;
> -    return ((uart->irq > 0) ? uart->irq : -1);
> +
> +    return ( uart->intr_works != polling ) ?
> +            uart->irq : -1;
>  }

Please don't corrupt previously correct style. You can keep things
almost like they were (albeit there's no strict need for any of the
parentheses):

    return ((uart->intr_works != polling) ? uart->irq : -1);

> @@ -1330,9 +1341,12 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
>                   * as special only for X86.
>                   */
>                  if ( uart->irq == 0xff )
> +                {
>                      uart->irq = 0;
> +                    uart->intr_works = polling;
> +                }
>  #endif
> -                if ( !uart->irq )
> +                if ( !uart->irq || (uart->intr_works == polling) )
>                      printk(XENLOG_INFO
>                             "ns16550: %pp: no legacy IRQ, using poll mode\n",
>                             &PCI_SBDF(0, b, d, f));

Message and code (after your addition) continue to be out of sync.
As said before, any condition that leads to polling mode wants to
find itself expressed by uart->intr_works set to "polling".

> @@ -1552,6 +1566,7 @@ static bool __init parse_positional(struct ns16550 *uart, char **str)
>              conf += 3;
>              uart->msi = true;
>              uart->irq = 0;
> +            uart->intr_works = polling;

How that? "msi" is specifically asking for interrupt driven mode,
just that the IRQ number isn't known yet. (Seeing this I notice that
parse_namevalue_pairs() offers no way of selecting MSI mode. But
that's not something you need to sort out.)

Jan
Re: [PATCH v3] ns16550: add support for polling mode when device tree is used
Posted by Oleksii 9 months, 1 week ago
On Mon, 2023-07-24 at 16:43 +0200, Jan Beulich wrote:
> On 24.07.2023 16:27, Oleksii Kurochko wrote:
> > --- a/xen/drivers/char/console.c
> > +++ b/xen/drivers/char/console.c
> > @@ -993,6 +993,8 @@ void __init console_init_preirq(void)
> >  #endif
> >          else if ( !strncmp(p, "none", 4) )
> >              continue;
> > +        else if ( !strncmp(p, "polling", 7 )
> > +            continue;
> >          else if ( (sh = serial_parse_handle(p)) >= 0 )
> >          {
> >              sercon_handle = sh;
> 
> Looks like you mean the new option to go under "console=". Besides
> this being guesswork because of you not updating the command line
> doc, this also is wrong, as the property then applies to all
> consoles. What you mean is a control for a specific instance of a
> 16550 console, which can only possibly go under "com<N>=". I would
> suggest to simply extend [<irq>|msi] there to [<irq>|msi|poll].
Thanks. It would be definitely better to go under "com<N>"
> 
> > @@ -595,7 +601,9 @@ static void __init cf_check
> > ns16550_endboot(struct serial_port *port)
> >  static int __init cf_check ns16550_irq(struct serial_port *port)
> >  {
> >      struct ns16550 *uart = port->uart;
> > -    return ((uart->irq > 0) ? uart->irq : -1);
> > +
> > +    return ( uart->intr_works != polling ) ?
> > +            uart->irq : -1;
> >  }
> 
> Please don't corrupt previously correct style. You can keep things
> almost like they were (albeit there's no strict need for any of the
> parentheses):
> 
>     return ((uart->intr_works != polling) ? uart->irq : -1);
Thanks.
> 
> > @@ -1330,9 +1341,12 @@ pci_uart_config(struct ns16550 *uart, bool_t
> > skip_amt, unsigned int idx)
> >                   * as special only for X86.
> >                   */
> >                  if ( uart->irq == 0xff )
> > +                {
> >                      uart->irq = 0;
> > +                    uart->intr_works = polling;
> > +                }
> >  #endif
> > -                if ( !uart->irq )
> > +                if ( !uart->irq || (uart->intr_works == polling) )
> >                      printk(XENLOG_INFO
> >                             "ns16550: %pp: no legacy IRQ, using
> > poll mode\n",
> >                             &PCI_SBDF(0, b, d, f));
> 
> Message and code (after your addition) continue to be out of sync.
> As said before, any condition that leads to polling mode wants to
> find itself expressed by uart->intr_works set to "polling".
Well. It looks like it would be better to set 'uart->intr_works =
polling' inside if ( !uart->irq ):
  if ( !uart->irq || (uart->intr_works == polling) )
  {
      uart->intr_works = polling;
      printk(XENLOG_INFO
             "ns16550: %pp: using poll mode\n",
             &PCI_SBDF(0, b, d, f));
  }
Then "uart->intr_works = polling;" can be removed from "if ( uart->irq
== 0xff )" as in that case 'uart->irq = 0' means polling mode for X86.
  
> 
> > @@ -1552,6 +1566,7 @@ static bool __init parse_positional(struct
> > ns16550 *uart, char **str)
> >              conf += 3;
> >              uart->msi = true;
> >              uart->irq = 0;
> > +            uart->intr_works = polling;
> 
> How that? "msi" is specifically asking for interrupt driven mode,
> just that the IRQ number isn't known yet. (Seeing this I notice that
> parse_namevalue_pairs() offers no way of selecting MSI mode. But
> that's not something you need to sort out.)
I was confused by the code from ns16550_init_postirq():
                if ( rc )
                {
                    uart->irq = 0;
                    if ( msi_desc )
                        msi_free_irq(msi_desc);
                    else
                        destroy_irq(msi.irq);
                }
where "uart->irq = 0;" means that polling mode should be used because
of the following code in the same function:
    if ( uart->irq > 0 )
    {
        uart->irqaction.handler = ns16550_interrupt;
        uart->irqaction.name    = "ns16550";
        uart->irqaction.dev_id  = port;
        if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
            printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart-
>irq);
    }

~ Oleksii
Re: [PATCH v3] ns16550: add support for polling mode when device tree is used
Posted by Jan Beulich 9 months, 1 week ago
On 25.07.2023 10:15, Oleksii wrote:
> On Mon, 2023-07-24 at 16:43 +0200, Jan Beulich wrote:
>> On 24.07.2023 16:27, Oleksii Kurochko wrote:
>>> @@ -1330,9 +1341,12 @@ pci_uart_config(struct ns16550 *uart, bool_t
>>> skip_amt, unsigned int idx)
>>>                   * as special only for X86.
>>>                   */
>>>                  if ( uart->irq == 0xff )
>>> +                {
>>>                      uart->irq = 0;
>>> +                    uart->intr_works = polling;
>>> +                }
>>>  #endif
>>> -                if ( !uart->irq )
>>> +                if ( !uart->irq || (uart->intr_works == polling) )
>>>                      printk(XENLOG_INFO
>>>                             "ns16550: %pp: no legacy IRQ, using
>>> poll mode\n",
>>>                             &PCI_SBDF(0, b, d, f));
>>
>> Message and code (after your addition) continue to be out of sync.
>> As said before, any condition that leads to polling mode wants to
>> find itself expressed by uart->intr_works set to "polling".
> Well. It looks like it would be better to set 'uart->intr_works =
> polling' inside if ( !uart->irq ):
>   if ( !uart->irq || (uart->intr_works == polling) )
>   {
>       uart->intr_works = polling;
>       printk(XENLOG_INFO
>              "ns16550: %pp: using poll mode\n",
>              &PCI_SBDF(0, b, d, f));
>   }
> Then "uart->intr_works = polling;" can be removed from "if ( uart->irq
> == 0xff )" as in that case 'uart->irq = 0' means polling mode for X86.

I'm not fully convinced. Care needs to be taken that both the x86 case
and the general case continue to function as they did (unless proven to
be buggy).

Jan

Re: [PATCH v3] ns16550: add support for polling mode when device tree is used
Posted by Oleksii 9 months, 1 week ago
On Tue, 2023-07-25 at 10:18 +0200, Jan Beulich wrote:
> On 25.07.2023 10:15, Oleksii wrote:
> > On Mon, 2023-07-24 at 16:43 +0200, Jan Beulich wrote:
> > > On 24.07.2023 16:27, Oleksii Kurochko wrote:
> > > > @@ -1330,9 +1341,12 @@ pci_uart_config(struct ns16550 *uart,
> > > > bool_t
> > > > skip_amt, unsigned int idx)
> > > >                   * as special only for X86.
> > > >                   */
> > > >                  if ( uart->irq == 0xff )
> > > > +                {
> > > >                      uart->irq = 0;
> > > > +                    uart->intr_works = polling;
> > > > +                }
> > > >  #endif
> > > > -                if ( !uart->irq )
> > > > +                if ( !uart->irq || (uart->intr_works ==
> > > > polling) )
> > > >                      printk(XENLOG_INFO
> > > >                             "ns16550: %pp: no legacy IRQ, using
> > > > poll mode\n",
> > > >                             &PCI_SBDF(0, b, d, f));
> > > 
> > > Message and code (after your addition) continue to be out of
> > > sync.
> > > As said before, any condition that leads to polling mode wants to
> > > find itself expressed by uart->intr_works set to "polling".
> > Well. It looks like it would be better to set 'uart->intr_works =
> > polling' inside if ( !uart->irq ):
> >   if ( !uart->irq || (uart->intr_works == polling) )
> >   {
> >       uart->intr_works = polling;
> >       printk(XENLOG_INFO
> >              "ns16550: %pp: using poll mode\n",
> >              &PCI_SBDF(0, b, d, f));
> >   }
> > Then "uart->intr_works = polling;" can be removed from "if ( uart-
> > >irq
> > == 0xff )" as in that case 'uart->irq = 0' means polling mode for
> > X86.
> 
> I'm not fully convinced. Care needs to be taken that both the x86
> case
> and the general case continue to function as they did (unless proven
> to
> be buggy).
But it continues to work as it worked before ( when uart->intr_works !=
polling ) otherwise, if uart->intr_works = polling, then polling mode
is forced.

The only thing that I would probably add it is to force polling mode in
case of X86 when polling mode is forced by command line:
    if ( ( uart->irq == 0xff ) || (uart->intr_works == polling) )
    {
        uart->irq = 0;
        uart->intr_works = polling;
    }
But this check looks a little bit unnecessary because if the polling
mode is forced or not will be checked later in the next if condition.

~ Oleksii