All of lore.kernel.org
 help / color / mirror / Atom feed
* xen/arm: uart interrupts handling
@ 2014-12-04  3:50 Vijay Kilari
  2014-12-04 13:51 ` Julien Grall
  0 siblings, 1 reply; 5+ messages in thread
From: Vijay Kilari @ 2014-12-04  3:50 UTC (permalink / raw)
  To: Ian Campbell, Stefano Stabellini, Tim Deegan; +Cc: xen-devel@lists.xen.org

Hi Tim,

I see that on uart interrupt, ICR is written to clear the all
interrupts except TX, RX and RX timeout. With this, cpu always finds
TX/RX is active and never
comes out of the loop.

With the below changes, TX, RX & RTI are cleared before handling this
interrupts.

Is my observation is correct?. If so I wonder how it is working on
platforms that
are using pl011. Without this for my cpu just keeps looping here.

  index fba0a55..d21bce3 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -63,7 +63,7 @@ static void pl011_interrupt(int irq, void *data,
struct cpu_user_regs *regs)
     {
         do
         {
-            pl011_write(uart, ICR, status & ~(TXI|RTI|RXI));
+            pl011_write(uart, ICR, status & (TXI|RTI|RXI));

             if ( status & (RTI|RXI) )
                 serial_rx_interrupt(port, regs);
@@ -157,7 +157,7 @@ static void pl011_resume(struct serial_port *port)
 {
     BUG(); // XXX
 }


Regards
Vijay

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: xen/arm: uart interrupts handling
  2014-12-04  3:50 xen/arm: uart interrupts handling Vijay Kilari
@ 2014-12-04 13:51 ` Julien Grall
  2014-12-04 15:40   ` Tim Deegan
  0 siblings, 1 reply; 5+ messages in thread
From: Julien Grall @ 2014-12-04 13:51 UTC (permalink / raw)
  To: Vijay Kilari, Ian Campbell, Stefano Stabellini, Tim Deegan
  Cc: xen-devel@lists.xen.org

On 04/12/14 03:50, Vijay Kilari wrote:
> Hi Tim,

Hi Vijay,

> I see that on uart interrupt, ICR is written to clear the all
> interrupts except TX, RX and RX timeout. With this, cpu always finds
> TX/RX is active and never
> comes out of the loop.

FWIW, the PL011 serial code has been copied from the Linux drivers.

Linux interrupt handler also clear all interrupts except TX, RX, RX
timeout. So do you see the issue on Linux?

> 
> With the below changes, TX, RX & RTI are cleared before handling this
> interrupts.
> 
> Is my observation is correct?. If so I wonder how it is working on
> platforms that
> are using pl011. Without this for my cpu just keeps looping here.
> 
>   index fba0a55..d21bce3 100644
> --- a/xen/drivers/char/pl011.c
> +++ b/xen/drivers/char/pl011.c
> @@ -63,7 +63,7 @@ static void pl011_interrupt(int irq, void *data,
> struct cpu_user_regs *regs)
>      {
>          do
>          {
> -            pl011_write(uart, ICR, status & ~(TXI|RTI|RXI));
> +            pl011_write(uart, ICR, status & (TXI|RTI|RXI));


This changes looks wrong to me. We want to clear the bit in status we
don't handle. Otherwise the interrupt will be fired in loop.

If I'm not mistaken, TXI/RTI/RXI will be cleared when data is read or
write into the fifo. So we should not clear automatically.

Regards,

-- 
Julien Grall

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: xen/arm: uart interrupts handling
  2014-12-04 13:51 ` Julien Grall
@ 2014-12-04 15:40   ` Tim Deegan
  2014-12-05  0:46     ` Vijay Kilari
  0 siblings, 1 reply; 5+ messages in thread
From: Tim Deegan @ 2014-12-04 15:40 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel@lists.xen.org, Ian Campbell, Vijay Kilari,
	Stefano Stabellini

At 13:51 +0000 on 04 Dec (1417697518), Julien Grall wrote:
> On 04/12/14 03:50, Vijay Kilari wrote:
> > Hi Tim,
> 
> Hi Vijay,
> 
> > I see that on uart interrupt, ICR is written to clear the all
> > interrupts except TX, RX and RX timeout. With this, cpu always finds
> > TX/RX is active and never
> > comes out of the loop.
> 
> FWIW, the PL011 serial code has been copied from the Linux drivers.
> 
> Linux interrupt handler also clear all interrupts except TX, RX, RX
> timeout. So do you see the issue on Linux?
> 
> > 
> > With the below changes, TX, RX & RTI are cleared before handling this
> > interrupts.
> > 
> > Is my observation is correct?. If so I wonder how it is working on
> > platforms that
> > are using pl011. Without this for my cpu just keeps looping here.
> > 
> >   index fba0a55..d21bce3 100644
> > --- a/xen/drivers/char/pl011.c
> > +++ b/xen/drivers/char/pl011.c
> > @@ -63,7 +63,7 @@ static void pl011_interrupt(int irq, void *data,
> > struct cpu_user_regs *regs)
> >      {
> >          do
> >          {
> > -            pl011_write(uart, ICR, status & ~(TXI|RTI|RXI));
> > +            pl011_write(uart, ICR, status & (TXI|RTI|RXI));
> 
> 
> This changes looks wrong to me. We want to clear the bit in status we
> don't handle. Otherwise the interrupt will be fired in loop.

Yes - even if we do want to explicitly clear the TXI|RTI|RXI bits, we
must clear the others too!

> If I'm not mistaken, TXI/RTI/RXI will be cleared when data is read or
> write into the fifo. So we should not clear automatically.

I've been looking at that and I think it's OK for the RX path -- we
ought to keep calling serial_rx_interrupt() until we've drained the
FIFO, at which point both RTI and RXI will be cleared.  Certainly we
shouldn't unconditionally clear RXI/RTI without somehow making sure
that we're not leaving bytes in the FIFO.

The TX path looks more suspect -- if the uart raises TXI and we have
nothing buffered to send, then TXI won't get cleared.
Still, I wonder why you're seeing this and other people haven't.

And again, we ought not to just clear the interrupt:
serial_tx_interrupt might not send any bytes (and indeed by my reading
it won't do anything unless the FIFO is actually empty), in which case
we need to keep something around to tell us to try again.

Actually, looking at the current behaviour, I wonder whether we ought
to push the interrupt trigger back from firing at half-empty.  Like so:

diff --git a/xen/drivers/char/pl011.c b/xen/drivers/char/pl011.c
index dd19ce8..2e97234 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -109,6 +109,8 @@ static void __init pl011_init_preirq(struct
serial_port *port)
             panic("pl011: No Baud rate configured\n");
         uart->baud = (uart->clock_hz << 2) / divisor;
     }
+    /* Trigger RX interrupt at 1/2 full, TX interrupt at 7/8 empty */
+    pl011_write(uart, IFLS, (2<<3 | 0));
     /* This write must follow FBRD and IBRD writes. */
     pl011_write(uart, LCR_H, (uart->data_bits - 5) << 5
                             | FEN


Tim.

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: xen/arm: uart interrupts handling
  2014-12-04 15:40   ` Tim Deegan
@ 2014-12-05  0:46     ` Vijay Kilari
  2014-12-05 10:52       ` Julien Grall
  0 siblings, 1 reply; 5+ messages in thread
From: Vijay Kilari @ 2014-12-05  0:46 UTC (permalink / raw)
  To: Tim Deegan
  Cc: Julien Grall, xen-devel@lists.xen.org, Ian Campbell,
	Stefano Stabellini

Hi Tim,

On Thu, Dec 4, 2014 at 9:10 PM, Tim Deegan <tim@xen.org> wrote:
> At 13:51 +0000 on 04 Dec (1417697518), Julien Grall wrote:
>> On 04/12/14 03:50, Vijay Kilari wrote:
>> > Hi Tim,
>>
>> Hi Vijay,
>>
>> > I see that on uart interrupt, ICR is written to clear the all
>> > interrupts except TX, RX and RX timeout. With this, cpu always finds
>> > TX/RX is active and never
>> > comes out of the loop.
>>
>> FWIW, the PL011 serial code has been copied from the Linux drivers.
>>
>> Linux interrupt handler also clear all interrupts except TX, RX, RX
>> timeout. So do you see the issue on Linux?
>>
>> >
>> > With the below changes, TX, RX & RTI are cleared before handling this
>> > interrupts.
>> >
>> > Is my observation is correct?. If so I wonder how it is working on
>> > platforms that
>> > are using pl011. Without this for my cpu just keeps looping here.
>> >
>> >   index fba0a55..d21bce3 100644
>> > --- a/xen/drivers/char/pl011.c
>> > +++ b/xen/drivers/char/pl011.c
>> > @@ -63,7 +63,7 @@ static void pl011_interrupt(int irq, void *data,
>> > struct cpu_user_regs *regs)
>> >      {
>> >          do
>> >          {
>> > -            pl011_write(uart, ICR, status & ~(TXI|RTI|RXI));
>> > +            pl011_write(uart, ICR, status & (TXI|RTI|RXI));
>>
>>
>> This changes looks wrong to me. We want to clear the bit in status we
>> don't handle. Otherwise the interrupt will be fired in loop.
>
> Yes - even if we do want to explicitly clear the TXI|RTI|RXI bits, we
> must clear the others too!
>
>> If I'm not mistaken, TXI/RTI/RXI will be cleared when data is read or
>> write into the fifo. So we should not clear automatically.
>
> I've been looking at that and I think it's OK for the RX path -- we
> ought to keep calling serial_rx_interrupt() until we've drained the
> FIFO, at which point both RTI and RXI will be cleared.  Certainly we
> shouldn't unconditionally clear RXI/RTI without somehow making sure
> that we're not leaving bytes in the FIFO.
>
> The TX path looks more suspect -- if the uart raises TXI and we have
> nothing buffered to send, then TXI won't get cleared.
> Still, I wonder why you're seeing this and other people haven't.

Yes, this is the behaviour that Iam seeing. In Linux, uart driver
masks TXI interrupt
in IMSC if buffer is empty. However in xen, this scenario is not
handled. This is the reason why cpu does not come out of uart irq
routine if TX interrupt is raised but buffer is empty.

I have added below changes to fix this on top of your suggested change

diff --git a/xen/drivers/char/pl011.c b/xen/drivers/char/pl011.c
index dd19ce8..ad48df3 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -109,6 +109,8 @@ static void __init pl011_init_preirq(struct
serial_port *port)
             panic("pl011: No Baud rate configured\n");
         uart->baud = (uart->clock_hz << 2) / divisor;
     }
+    /* Trigger RX interrupt at 1/2 full, TX interrupt at 7/8 empty */
+    pl011_write(uart, IFLS, (2<<3 | 0));
     /* This write must follow FBRD and IBRD writes. */
     pl011_write(uart, LCR_H, (uart->data_bits - 5) << 5
                             | FEN
@@ -197,6 +199,20 @@ static const struct vuart_info
*pl011_vuart(struct serial_port *port)
     return &uart->vuart;
 }

+static void pl011_tx_stop(struct serial_port *port)
+{
+    struct pl011 *uart = port->uart;
+
+    pl011_write(uart, IMSC, pl011_read(uart, IMSC) & ~(TXI));
+}
+
+static void pl011_tx_start(struct serial_port *port)
+{
+    struct pl011 *uart = port->uart;
+
+    pl011_write(uart, IMSC, pl011_read(uart, IMSC) | (TXI));
+}
+
 static struct uart_driver __read_mostly pl011_driver = {
     .init_preirq  = pl011_init_preirq,
     .init_postirq = pl011_init_postirq,
@@ -207,6 +223,8 @@ static struct uart_driver __read_mostly pl011_driver = {
     .putc         = pl011_putc,
     .getc         = pl011_getc,
     .irq          = pl011_irq,
+    .start_tx     = pl011_tx_start,
+    .stop_tx      = pl011_tx_stop,
     .vuart_info   = pl011_vuart,
 };

diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c
index 44026b1..0f26d40 100644
--- a/xen/drivers/char/serial.c
+++ b/xen/drivers/char/serial.c
@@ -76,6 +76,18 @@ void serial_tx_interrupt(struct serial_port *port,
struct cpu_user_regs *regs)
         cpu_relax();
     }

+    if ( port->txbufc == port->txbufp )
+    {
+        /* Disable TX. nothing to send */
+        port->driver->stop_tx(port);
+        spin_unlock(&port->tx_lock);
+        goto out;
+    }
+    else
+    {
+        if ( port->driver->tx_ready(port) )
+            port->driver->start_tx(port);
+    }
     for ( i = 0, n = port->driver->tx_ready(port); i < n; i++ )
     {
         if ( port->txbufc == port->txbufp )
@@ -117,6 +129,8 @@ static void __serial_putc(struct serial_port *port, char c)
                     cpu_relax();
                 if ( n > 0 )
                 {
+                    /* Enable TX before sending chars */
+                    port->driver->start_tx(port);
                     while ( n-- )
                         port->driver->putc(
                             port,
@@ -135,6 +149,8 @@ static void __serial_putc(struct serial_port *port, char c)
         if ( ((port->txbufp - port->txbufc) == 0) &&
              port->driver->tx_ready(port) > 0 )
         {
+            /* Enable TX before sending chars */
+            port->driver->start_tx(port);
             /* Buffer and UART FIFO are both empty, and port is available. */
             port->driver->putc(port, c);
         }
@@ -152,11 +168,16 @@ static void __serial_putc(struct serial_port
*port, char c)
         while ( !(n = port->driver->tx_ready(port)) )
             cpu_relax();
         if ( n > 0 )
+        {
+            /* Enable TX before sending chars */
+            port->driver->start_tx(port);
             port->driver->putc(port, c);
+        }
     }
     else
     {
         /* Simple synchronous transmitter. */
+        port->driver->start_tx(port);
         port->driver->putc(port, c);
     }
 }
@@ -404,6 +425,7 @@ void serial_start_sync(int handle)
                 /* port is unavailable and might not come up until reenabled by
                    dom0, we can't really do proper sync */
                 break;
+            port->driver->start_tx(port);
             port->driver->putc(
                 port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
         }
diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
index 9f4451b..71e6ade 100644
--- a/xen/include/xen/serial.h
+++ b/xen/include/xen/serial.h
@@ -81,6 +81,10 @@ struct uart_driver {
     int  (*getc)(struct serial_port *, char *);
     /* Get IRQ number for this port's serial line: returns -1 if none. */
     int  (*irq)(struct serial_port *);
+    /* Unmask TX interrupt */
+    void  (*start_tx)(struct serial_port *);
+    /* Mask TX interrupt */
+    void  (*stop_tx)(struct serial_port *);
     /* Get serial information */
     const struct vuart_info *(*vuart_info)(struct serial_port *);
 };

-Vijay

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: xen/arm: uart interrupts handling
  2014-12-05  0:46     ` Vijay Kilari
@ 2014-12-05 10:52       ` Julien Grall
  0 siblings, 0 replies; 5+ messages in thread
From: Julien Grall @ 2014-12-05 10:52 UTC (permalink / raw)
  To: Vijay Kilari, Tim Deegan
  Cc: xen-devel@lists.xen.org, Ian Campbell, Stefano Stabellini

Hi Vijay,

On 05/12/2014 00:46, Vijay Kilari wrote:
  > Yes, this is the behaviour that Iam seeing. In Linux, uart driver
> masks TXI interrupt
> in IMSC if buffer is empty. However in xen, this scenario is not
> handled. This is the reason why cpu does not come out of uart irq
> routine if TX interrupt is raised but buffer is empty.
>
> I have added below changes to fix this on top of your suggested change

Can you send a formal patch (commit message + signed-off-by)?

Also, you will have to make sure you don't break the other serial drivers.

Regards,

-- 
Julien Grall

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-12-05 10:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-04  3:50 xen/arm: uart interrupts handling Vijay Kilari
2014-12-04 13:51 ` Julien Grall
2014-12-04 15:40   ` Tim Deegan
2014-12-05  0:46     ` Vijay Kilari
2014-12-05 10:52       ` Julien Grall

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.