]> git.baikalelectronics.ru Git - kernel.git/commit
tty: sifive: Finish transmission before changing the clock
authorPalmer Dabbelt <palmerdabbelt@google.com>
Sat, 7 Mar 2020 04:26:38 +0000 (20:26 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Mar 2020 08:55:41 +0000 (09:55 +0100)
commitbc1364c07f05c159bd782128b94b1cecfd5d6245
tree5952ca577a1a72b55b5271239713c1b954cf0684
parente95f0791881c66d4b24908c9b9c9c24ba118d24f
tty: sifive: Finish transmission before changing the clock

SiFive's UART has a software controller clock divider that produces the
final baud rate clock.  Whenever the clock that drives the UART is
changed this divider must be updated accordingly, and given that these
two events are controlled by software they cannot be done atomically.
During the period between updating the UART's driving clock and internal
divider the UART will transmit a different baud rate than what the user
has configured, which will probably result in a corrupted transmission
stream.

The SiFive UART has a FIFO, but due to an issue with the programming
interface there is no way to directly determine when the UART has
finished transmitting.  We're essentially restricted to dead reckoning
in order to figure that out: we can use the FIFO's TX busy register to
figure out when the last frame has begun transmission and just delay for
a long enough that the last frame is guaranteed to get out.

As far as the actual implementation goes: I've modified the existing
existing clock notifier function to drain both the FIFO and the shift
register in on PRE_RATE_CHANGE.  As far as I know there is no hardware
flow control in this UART, so there's no good way to ask the other end
to stop transmission while we can't receive (inserting software flow
control messages seems like a bad idea here).

Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
Tested-by: Yash Shah <yash.shah@sifive.com>
Link: https://lore.kernel.org/r/20200307042637.83728-1-palmer@dabbelt.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/sifive.c