----------------------------------------------------------------- ※ これは VAIO ML に出した質問のメールです。 VAIO 505Xに Vine Linux 1.0b と Kernel 2.2.9ac1 を入れて使用しています。 USBマウスを使いたくてはまってます。 Linux KernelのUSBドライバは、デバイスが存在していてるかどうかチェックして いるだけですが、FreeBSDではUSBデバイスに I/O,IRQが割り当てられていない場 合には、決め打ちで割り当ててると理解してます。 またVAIO 505Xでも、Windows98を普通にインストールしただけでUSBマウスが使えます。 そこでCMOSはいじらなくても、PCI廻りをいじるだけでUSBが使えないかと試してみ ました。 1. BIOSでPlug&PlayをOffにする。 lspci -vv を実行すると、 00:01.2 USB Controller: Intel Corporation 82371AB PIIX4 USB (rev 01) Interrupt: pin D routed to IRQ 0 と表示されるので、やっぱり割り込みが割り当てられていない。 2.setpciでPCIレジスタを書き換えて、IRQを割り当てる。 # /sbin/setpci -v -s 00:01.2 INTERRUPT_LINE 00:01.2:3c = ff ← IRQ = ff になってる? # /sbin/setpci -v -s 00:01.2 INTERRUPT_LINE=0b 00:01.2:3c = 0b ← IRQ = 11 に変えたつもり この状態で lspci を実行しても、 00:01.2 USB Controller: Intel Corporation 82371AB PIIX4 USB (rev 01) Interrupt: pin D routed to IRQ 0 と表示されており、割り込みが割り当てられていない。 PCIレジスタの設定手順が悪いのか、setpci の使い方が悪いのか? そもそも PCIレジスタをいじって設定を変えようというのが悪いのか。 どなたか同じようなことを試みられた方はいらっしゃいますでしょうか? ※このメールに対して、PCI-ISA bridgeコントローラで設定すべきことと、 ※コントローラのデータシートの在処を教えてもらった。 ----------------------------------------------------------------- ※Free BSDパッチの解析(PCI初期化ルーチンの一部と推測) +static void +piix_irq_routing(pcici_t tag) +{ + int elcr2; + /* pci_conf_write (tag, 0x60, 0x0980800aul); */ + pci_conf_write (tag, 0x60, 0x09800a09ul); + elcr2 = inb(0x4d1); + outb(0x4d1, elcr2|0x04); + printf("ELCR2 %02x -> %02x\n", elcr2, elcr2|0x04); +} +#endif /* FORCE_IRQ_ROUTING */ PCI to ISA/EIO Bridge の Route Control Register を設定している。 60H:PIRRCA# = 09H = 0000 1001 = IRQ9 へルーティング 61H:PIRRCB# = 0aH = 0000 1010 = IRQ10 へルーティング 62H:PIRRCC# = 80H = 1000 0000 = ルーティング・ディセーブル 63H:PIRRCD# = 09H = 0000 1001 = IRQ9 へルーティング Bit7 : Interrupt Routing Enable 0:Enable , 1:Disable Bit6-4 : Reserved. Read as 0. Bit0-3 : Interrupt Routing. When Bit7=0 0000=Reserved 1000=Reserved 0001=Reserved 1001=IRQ9 0010=Reserved 1010=IRQ10 0011=IRQ3 1011=IRQ11 0100=IRQ4 1100=IRQ12 0101=IRQ5 1101=Reserved 0110=IRQ6 1110=IRQ14 0111=IRQ7 1111=IRQ15 I/Oポート(0x4d1)アクセス I/Oポート 0x4d1は ECLR2 Edge/Level Control Register この例ではIRQ10をレベルトリガに変更している。 Power Managementの PIRQA# と USBの PIRQD# を IRQ9へ割り当てている。 また PIRQB# を IRQ10 へ割り当てている。 PIRQB#はどのFunction Block が使用しているのかまだわからない。 ----------------------------------------------------------------- ※82371PIIX4データシート P177より PCIデバイスの割り込みPIRQx#のIRQへの割り当ては、各々のRoute Control Register で行うと記述されている。 P59より Route Control Registerの詳細な説明がある。 この Register は、 PCI to ISA/EIO Bridge Configurationの一部である。 P120より PIRQA#はPower Management が使用している P49 の I/O Space Registersで、 USB Interrupt Enable という registerがあった。 I/O Space Registersってどうやってアクセスする? P107 の LEGSUP:Legacy Support Registerで、 USB PIRQ Enable というbitがあった。 ---------------------------------------------------------------------- ※BIOS Plug & Play OS のON/OFF による USB廻りの設定の違いを見る Plug & Play | yes | no -------------------------------+----------+----------- USB Command Register 04H | 0001 | 0001 USB I/O Space Base Address | 0000fce1 | 0000fce1 USB INTERRUPT LINE | ff | ff ISA Bridge PIRQx Route Control | 8080800b | 8080800b 何も変わらない。 ---------------------------------------------------------------------- ※Kernel Documentより 「メモリ,I/Oアドレスおよび割り込み番号は、config spaceから読み出さないほう がいい。カーネルにリマップされた pci_dev構造体 から読み出すべきである。」 とあった。 /sbin/setpciで設定しても反映されないわけがわかったような気がする。 ドキュメントは大事です。(--;; ---------------------------------------------------------------------- ※/usr/src/linux/drivers/usb/uhci.c をハッキング♪ uhci.c をほげる。 関数 start_uhci(struct pci_dev *dev) この関数で、PCIデバイスのI/Oアドレスをチェックしている。 チェック方法は /linux/ioport.h で定義されている check_region()を使っている。 戻り値は、dev->irq と見つけたI/Oアドレス。 関数 uhci_init(void) この関数が init_module()として初期化時にcallされているようである。 ここで start_uhci() をcallする前に、強引にirq=11を割り当てたらどうなる? ---------------------------------------------------------------------- ※初期化ルーチン内で、強引に設定してみる。 PCI to ISA/EIO BRIDGE のクラスコードがデータシートと異なることがわかった。 データシートでは、060100H(PCI_CLASS_BRIDGE_ISA)と記述されているが 実物は、068000H(PCI_CLASS_BRIDGE_OTHER)になっている。 /sbin/setpciで確認しても、068000Hになっていた。 まぁ現物あわせでいいかな。結果オーライということで。(--; ☆ PCI to ISA/EIO Bridge の Routing Registerを設定する。 ※本来はPCIバスの初期化ルーチンで行うべき処理だが、デバッグ中にkernelを何度 もコンパイルするのが嫌だったので、USBドライバ内部でまとめてやってしまった。 ELCR2(Edge/Level Control Register2)で使用するIRQの割り込みをレベル割り込みに 変更しなければならない。(PCIはレベル割り込み) int uhci_init(void)内で設定する。 u8 route; u8 elcr2; while ( (dev = pci_find_device( PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, dev))!=NULL ) { printk(" find PCI 82371AB-0 \n"); pci_read_config_byte(dev, 0x63, &route); /* Get Routing of PIRQD */ if ( route==0x80 ) { /* 80H=Routing disable */ route = 0x09; pci_write_config_byte(dev, 0x63, &route); pci_read_config_byte(dev, 0x63, &route); printk(" PIRQD route : %x \n", route); } } elcr2 = inb(0x4d1); /* Port 0x4d1 : Edge/Level Control Register */ printk(" ELCR2 : %0x ", elcr2); switch ( route ) { case 0x09 : elcr2 |= 0x02; break; case 0x0a : elcr2 |= 0x04; break; case 0x0b : elcr2 |= 0x08; break; case 0x0c : elcr2 |= 0x10; break; case 0x0e : elcr2 |= 0x40; break; case 0x0f : elcr2 |= 0x80; break; } outb(elcr2, 0x4d1); printk("-> %0x", elcr2); ☆ 割り当てたIRQをOSに教える start_uhci(dev)をコールする前に以下の記述を加えた。 if ( dev->irq != route ){ printk("USB irq set from %0x to %0x \n", dev->irq, route); dev->irq = route; } ※USBデバイスドライバがIRQのシェアリングに対応しているかどうかあてにならない ので、上の例ではUSBコントローラの割り込みPIRQDをIRQ=9にRoutingしようとしてい るが、結果としてIRQ=11にRoutingされている。 IRQ=11はVGA(Neomagic)が使用している。 ※FreeBSDでは、IRQ=10にRoutingしている。 そういえばFreeBSDのMLで、VAIO505は意図したIRQにRoutingされない問題が出てい たのをみかけた。 ※USBコントローラのI/Oポートについては割り当て済に見えるので、何もしていない。 ---------------------------------------------------------------------- ☆ PCI BIOSは賢い PCIデバイスへのIRQルーティングで間抜けなことをしてしまった。 以下のPCI関数の呼び出しで、値を渡すべきところで変数のポインタを渡してしまった。 pci_write_config_byte(dev, 0x63, &route); 正しくは、 pci_write_config_byte(dev, 0x63, route); である。 意図したIRQが割り当てられないのは、単なるコーディングミスのせいだった。 でたらめなIRQを指定すると、PCI BIOSが正しいIRQを割り当ててくれるようです。 もしくは Linux の PCIルーチンが面倒みてくれてるのかも。 何はともあれ OS なり PCI BIOS なりに管理してもらうほうが間違いが少ないので、 わざとあり得ないIRQの割り当てを要求しておいて、正しいIRQを割り当ててもらう ほうが問題がないかも。 それはともかく、おまぬけなコードをMLに流してしまった。(--;