Re: High CPU load when using MAX14830 SPI UART controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Jan,

On Tue, 21 Jul 2020 16:20:02 +0200
Jan Kundrát <jan.kundrat@xxxxxxxxx> wrote:

> I have no code, but according to the datasheet, it's the "RTimeout" bit 
> (Line Status Register, bit 0). If properly configured (RxTimeOut set and 
> the interrupt routing enabled via LSRIntEn[0], the "RTimeoutIEn" bit), 
> we're supposed to get ISR[0] set upon this timeout.
> 
> I have not tried it, I just read the datasheet a few years ago. When you 
> have patches, I'll be happy to test them (likely only in September, though, 
> because of vacations).

Wow, this was really useful! I had somehow missed this when glancing
through datasheet, but now that you pointed out, I implemented the
following patch, which:

 - Uses the RX timeout interrupt to fire an interrupt when there's data
   in the RX FIFO *and* no characters have been received for a duration
   equivalent to the reception time of 4 characters.

 - Uses the RX FIFO trigger interrupt to trigger an interrupt when the
   RX FIFO is half full. This ensure that if we have a continuous flow
   of data, we do get interrupts.

Thanks to that, in my scenario of receiving 20 bytes of data every
16ms, instead of having multiple interrupts each picking max 3-4 bytes
of data from the RX FIFO, I get a single interrupt that picks up the
full 20 bytes of data in one go. Result: CPU consumption goes down from
25% to 5%.

Here is the patch that I have so far. I'm waiting on more testing to
happen, and if more extensive testing is successful, I'll submit the
patch properly. The question is what are the right thresholds. In a
separate e-mail, Andy Shevchenko suggested the "4 characters timeout",
which I used arbitrarily. Same question for the RX FIFO trigger at half
the FIFO size.

Best regards,

Thomas

commit e958c6087aa889f421323314cb33ad9756ee033e
Author: Thomas Petazzoni <thomas.petazzoni@xxxxxxxxxxx>
Date:   Wed Jul 22 16:18:14 2020 +0200

    serial: max310x: rework RX interrupt handling
    
    Currently, the RX interrupt logic uses the RXEMPTY interrupt, with the
    RXEMPTYINV bit set, which means we get an RX interrupt as soon as the
    RX FIFO is non-empty.
    
    However, with the MAX310X having a FIFO of 128 bytes, this makes very
    poor use of the FIFO: we trigger an interrupt as soon as the RX FIFO
    has one byte, which means a lot of interrupts, each only collecting a
    few bytes from the FIFO, causing a significant CPU load.
    
    Instead this commit relies on two other RX interrupt events:
    
     - MAX310X_IRQ_RXFIFO_BIT, which triggers when the RX FIFO has reached
       a certain threshold, which we define to be half of the FIFO
       size. This ensure we get an interrupt before the RX FIFO fills up.
    
     - MAX310X_LSR_RXTO_BIT, which triggers when the RX FIFO has received
       some bytes, and then no more bytes are received for a certain
       time. Arbitrarily, this time is defined to the time is takes to
       receive 4 characters.
    
    On a Microchip SAMA5D3 platform that is receiving 20 bytes every 16ms
    over one MAX310X UART, this patch has allowed to reduce the CPU
    consumption of the interrupt handler thread from ~25% to 6-7%.
    
    Signed-off-by: Thomas Petazzoni <thomas.petazzoni@xxxxxxxxxxx>

diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 8434bd5a8ec78..a1c80850d77ed 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1056,9 +1056,9 @@ static int max310x_startup(struct uart_port *port)
 	max310x_port_update(port, MAX310X_MODE1_REG,
 			    MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
 
-	/* Configure MODE2 register & Reset FIFOs*/
-	val = MAX310X_MODE2_RXEMPTINV_BIT | MAX310X_MODE2_FIFORST_BIT;
-	max310x_port_write(port, MAX310X_MODE2_REG, val);
+	/* Reset FIFOs*/
+	max310x_port_write(port, MAX310X_MODE2_REG,
+			   MAX310X_MODE2_FIFORST_BIT);
 	max310x_port_update(port, MAX310X_MODE2_REG,
 			    MAX310X_MODE2_FIFORST_BIT, 0);
 
@@ -1086,8 +1086,27 @@ static int max310x_startup(struct uart_port *port)
 	/* Clear IRQ status register */
 	max310x_port_read(port, MAX310X_IRQSTS_REG);
 
-	/* Enable RX, TX, CTS change interrupts */
-	val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT;
+	/*
+	 * Let's ask for an interrupt after a timeout equivalent to
+	 * the receiving time of 4 characters after the last character
+	 * has been received.
+	 */
+	max310x_port_write(port, MAX310X_RXTO_REG, 4);
+
+	/*
+	 * Make sure we also get RX interrupts when the RX FIFO is
+	 * filling up quickly, so get an interrupt when half of the RX
+	 * FIFO has been filled in.
+	 */
+	max310x_port_write(port, MAX310X_FIFOTRIGLVL_REG,
+			   MAX310X_FIFOTRIGLVL_RX(MAX310X_FIFO_SIZE / 2));
+
+	/* Enable RX timeout interrupt in LSR */
+	max310x_port_write(port, MAX310X_LSR_IRQEN_REG,
+			   MAX310X_LSR_RXTO_BIT);
+
+	/* Enable LSR, RX FIFO trigger, CTS change interrupts */
+	val = MAX310X_IRQ_LSR_BIT  | MAX310X_IRQ_RXFIFO_BIT | MAX310X_IRQ_TXEMPTY_BIT;
 	max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT);
 
 	return 0;




-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux