Kernel stuck at at91_change_speed

Sammy Chan csammy at gmail.com
Tue Feb 9 11:26:18 EST 2010


Hi,

This is my first post. I've an interesting finding to share with you.
I'm using an old kernel 2.4.21-rmk1 on AT91RM9200. Recently, I found
that the kernel would hang at times when calling open("/dev/ttySX") in
order to initialize an RS232 port, IRDA port or other serial device.
In fact, it got stuck at an infinite while loop here:

[drivers/at91/serial/at91_serial.c, at91_change_speed()]

......
    /* first, disable interrupts and drain transmitter */
    local_irq_save(flags);
    imr = UART_GET_IMR(uart);   /* get interrupt mask */
    UART_PUT_IDR(uart, -1);     /* disable all interrupts */
    local_irq_restore(flags);

    while (!(UART_GET_CSR(uart) & AT91C_US_TXEMPTY)) { barrier(); }
......
    UART_PUT_BRGR(uart, quot);

------------------------------------------
It didn't stand to reason that a byte sent to the shift register could
never trasmitted. After painstaking investigation, I found that
US_BRGR (clock divisor) was 0 when the loop was being executed, which
means the clock was disabled. The reason there was data to be
transmitted during the open call was that by default the ttyS device
has the ECHO c_lflag on and there was some initial noise generated
from the serial devices, causing the noise received to be transmitted
soon after the transmitted was enabled during. If the noise happened
to arrive just before the loop was executed, the kernel would be
trapped. That explains the randomness. If US_BRGR was set to some
non-zero value, the problem would disappear. It seems even in the
latest kernel, the while loop still precedes setting the US_BRGR. I
wonder if this is a potential kernel bug, or if it's just my
responsibility to ensure that there is no noise or ECHO turned off as
default for non-terminal devices?

Regards,
Sammy

----------------
Sammy Chan
csammy at gmail.com



More information about the linux-arm-kernel mailing list