• Richard Genoud's avatar
    tty/serial: at91: fix hardware handshake on Atmel platforms · f5bb8416
    Richard Genoud authored
    commit 9bcffe75 upstream.
    
    After commit 1cf6e8fc ("tty/serial: at91: fix RTS line management
    when hardware handshake is enabled"), the hardware handshake wasn't
    functional anymore on Atmel platforms (beside SAMA5D2).
    
    To understand why, one has to understand the flag ATMEL_US_USMODE_HWHS
    first:
    Before commit 1cf6e8fc ("tty/serial: at91: fix RTS line management
    when hardware handshake is enabled"), this flag was never set.
    Thus, the CTS/RTS where only handled by serial_core (and everything
    worked just fine).
    
    This commit introduced the use of the ATMEL_US_USMODE_HWHS flag,
    enabling it for all boards when the user space enables flow control.
    
    When the ATMEL_US_USMODE_HWHS is set, the Atmel USART controller
    handles a part of the flow control job:
    - disable the transmitter when the CTS pin gets high.
    - drive the RTS pin high when the DMA buffer transfer is completed or
      PDC RX buffer full or RX FIFO is beyond threshold. (depending on the
      controller version).
    
    NB: This feature is *not* mandatory for the flow control to work.
    (Nevertheless, it's very useful if low latencies are needed.)
    
    Now, the specifics of the ATMEL_US_USMODE_HWHS flag:
    
    - For platforms with DMAC and no FIFOs (sam9x25, sam9x35, sama5D3,
    sama5D4, sam9g15, sam9g25, sam9g35)* this feature simply doesn't work.
    ( source: https://lkml.org/lkml/2016/9/7/598 )
    Tested it on sam9g35, the RTS pins always stays up, even when RXEN=1
    or a new DMA transfer descriptor is set.
    => ATMEL_US_USMODE_HWHS must not be used for those platforms
    
    - For platforms with a PDC (sam926{0,1,3}, sam9g10, sam9g20, sam9g45,
    sam9g46)*, there's another kind of problem. Once the flag
    ATMEL_US_USMODE_HWHS is set, the RTS pin can't be driven anymore via
    RTSEN/RTSDIS in USART Control Register. The RTS pin can only be driven
    by enabling/disabling the receiver or setting RCR=RNCR=0 in the PDC
    (Receive (Next) Counter Register).
    => Doing this is beyond the scope of this patch and could add other
    bugs, so the original (and working) behaviour should be set for those
    platforms (meaning ATMEL_US_USMODE_HWHS flag should be unset).
    
    - For platforms with a FIFO (sama5d2)*, the RTS pin is driven according
    to the RX FIFO thresholds, and can be also driven by RTSEN/RTSDIS in
    USART Control Register. No problem here.
    (This was the use case of commit 1cf6e8fc ("tty/serial: at91: fix
    RTS line management when hardware handshake is enabled"))
    NB: If the CTS pin declared as a GPIO in the DTS, (for instance
    cts-gpios = <&pioA PIN_PB31 GPIO_ACTIVE_LOW>), the transmitter will be
    disabled.
    => ATMEL_US_USMODE_HWHS flag can be set for this platform ONLY IF the
    CTS pin is not a GPIO.
    
    So, the only case when ATMEL_US_USMODE_HWHS can be enabled is when
    (atmel_use_fifo(port) &&
     !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
    
    Tested on all Atmel USART controller flavours:
    AT91SAM9G35-CM (DMAC flavour), AT91SAM9G20-EK (PDC flavour),
    SAMA5D2xplained (FIFO flavour).
    
    * the list may not be exhaustive
    
    Fixes: 1cf6e8fc ("tty/serial: at91: fix RTS line management when hardware handshake is enabled")
    Signed-off-by: default avatarRichard Genoud <richard.genoud@gmail.com>
    Acked-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
    Acked-by: default avatarCyrille Pitchen <cyrille.pitchen@atmel.com>
    Acked-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
    [nicolas.ferre@atmel.com: adapt to 4.4.x kernel for stable by adding
    the atmel_port variable declaration which was missing]
    Signed-off-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    f5bb8416
atmel_serial.c 74.9 KB