Kernel Linux: udelay () retorna muito cedo?

Tenho um driver que requer atrasos de microssegundos. Para criar esse atraso, meu driver está usando a função udelay do kernel. Especificamente, há uma chamada para udelay (90):

iowrite32(data, addr + DATA_OFFSET);
iowrite32(trig, addr + CONTROL_OFFSET);

udelay(30);

trig |= 1;
iowrite32(trig, addr + CONTROL_OFFSET);

udelay(90); // This is the problematic call

Tivemos problemas de confiabilidade com o dispositivo. Depois de muita depuração, rastreamos o problema até o driver reiniciar antes dos 90us terem passado. (Veja "prova" abaixo.)

Estou executando o SMP da versão 2.6.38-11 genérica do kernel (Kubuntu 11.04, x86_64) em um Intel Pentium Dual Core (E5700

Até onde eu sei, a documentação afirma que o udelay atrasará a execução definalment o atraso especificado e é ininterrupto. Existe algum bug nesta versão do kernel, ou eu entendi algo errado sobre o uso do udela

Para nos convencermos de que o problema foi causado pelo retorno do udelay muito cedo, alimentamos um relógio de 100kHz em uma das portas de E / S e implementamos nosso próprio atraso da seguinte maneira:

// Wait until n number of falling edges
// are observed
void clk100_delay(void *addr, u32 n) {
    int i;

    for (i = 0; i < n; i++) {
        u32 prev_clk = ioread32(addr);
        while (1) {
            u32 clk = ioread32(addr);
            if (prev_clk && !clk) {
                break;
            } else {
                prev_clk = clk;
            }
        }
    }
}

... e o motorista agora funciona perfeitament

Como nota final, acheiuma discussã indicando que o escalonamento de frequência pode estar causando um comportamento inadequado à família de funções * delay (), mas isso estava em uma lista de discussão do ARM - presumindo que tais problemas não existissem em um PC com Linux x8

questionAnswers(2)

yourAnswerToTheQuestion