diff mbox series

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

Message ID d929b43814e6e1a247b954c4be40a35d61b6a45a.1690208840.git.oleksii.kurochko@gmail.com (mailing list archive)
State Superseded
Headers show
Series [v3] ns16550: add support for polling mode when device tree is used | expand

Commit Message

Oleksii Kurochko July 24, 2023, 2:27 p.m. UTC
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(-)

Comments

Jan Beulich July 24, 2023, 2:43 p.m. UTC | #1
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
Oleksii Kurochko July 25, 2023, 8:15 a.m. UTC | #2
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
Jan Beulich July 25, 2023, 8:18 a.m. UTC | #3
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
Oleksii Kurochko July 25, 2023, 8:43 a.m. UTC | #4
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
diff mbox series

Patch

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");