マイクロ秒の遅延が必要なドライバーがあります。この遅延を作成するために、ドライバーはカーネルの udelay 関数を使用しています。具体的には、udelay(90) への呼び出しが 1 つあります。
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
デバイスに信頼性の問題がありました。多くのデバッグを行った後、90us が経過する前にドライバーが再開する問題を突き止めました。(以下の「証明」を参照してください。)
Intel Pentium Dual Core (E5700) でカーネル バージョン 2.6.38-11-generic SMP (Kubuntu 11.04、x86_64) を実行しています。
私の知る限り、ドキュメントには、udelay は少なくとも指定された遅延の実行を遅らせ、中断できないと記載されています。このバージョンのカーネルにはバグがありますか、それとも udelay の使用について何か誤解していましたか?
問題の原因が udelay の戻りが早すぎることであると確信するために、I/O ポートの 1 つに 100kHz のクロックを供給し、次のように独自の遅延を実装しました。
// 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;
}
}
}
}
...そして、ドライバーは問題なく動作するようになりました。
最後に、周波数スケーリングが *delay() ファミリの関数の誤動作を引き起こしている可能性があることを示す議論を見つけましたが、これは ARM メーリング リストにありました - Linux x86 ベースではそのような問題は存在しないと思いますパソコン。