* 16bit-PCICの制御方法
-16bit-PCICは、16bitPCカードを制御するためのコントローラです。
-ここではAT互換機の世界で非常にメジャーなi82365を説明します。CardBusがサポートする16bitPCIC機能も、たいていはこれとコンパチブルです。
-ソケットごとに、最大で2つのI/Oウィンドウと5つのメモリウィンドウを利用できます。
-基本的にこのチップはISAバスに接続されています(そのためメモリウィンドウが24bitアドレスまでしか考えられていなかったりする)。CardBusがPCIに接続されているのと対照的といえます。
* レジスタへのアクセス方法
-i82365には64個のレジスタがあります(そのうちのいくつかはリザーブでアクセスできないかもしれませんが)。このレジスタにアクセスする方法は、次の2つのI/Oポートを使います。
--0x03e0 : indexレジスタ (バイトアクセス、R/W)
---bit0-5 : レジスタ番号 (0~63)
---bit6-7 : ソケット番号 (0~3)
--0x03e1 : dataレジスタ (バイトアクセス、R/W -- indexレジスタの内容によってはリードオンリーだったりライトオンリーだったりする)
---bit0-7 : データ
--(註)
---滅多にないとは思いますが、もしかしたら0x03e0以外のアドレスに割り振られていることもあるかもしれません。
---ソケット番号は0~3となっていますが、今のところ2以上を設定できるコントローラは無いようです(ソケットを4つサポートしているチップがあったらおもしろいのになあ。CardBusなら機能番号が3bitあるから8つまでできるかな?)。
---CardBusでの16bitPCIC互換機能の場合は、こんな面倒な方法ではなくて、メモリ空間に64個の全レジスタを直接割り振れるので、とてもドライバが書きやすいです。詳しくはCardBusのページを参照してください。もちろん、ここの方法でもアクセスはできます(アドレスは0x03e0に限定されず、CardBusブリッジでの設定で決まります)。
-具体的には次のようにアクセスします。
--レジスタをリードする場合:indexレジスタに対象となるレジスタ番号とソケット番号を書き込む。そしてdataレジスタを読み込む。
--レジスタをライトする場合:indexレジスタに対象となるレジスタ番号とソケット番号を書き込む。そしてdataレジスタに書き込む。
-このようにソケットごと完全に独立したレジスタが64個あると考えていいです。
* レジスタ表
|index|名前|
|0x00|Identification and Revision|
|0x01|Interface Status|
|0x02|Power and RESETDEV Control|
|0x03|Interrupt and General Control|
|0x04|Card Status Change|
|0x05|Card Status Change Interrupt Configuration|
|0x06|Address Window Enable|
|0x07|I/O Control|
|0x08-0x0b|I/O Window 0|
|0x0c-0x0f|I/O Window 1|
|0x10-0x15|Memory Window 0|
|0x16|Card Detect and General Control|
|0x17|- reserve -|
|0x18-0x1d|Memory Window 1|
|0x1e|Global Control|
|0x1f|Mode Control 1|
|0x20-0x25|Memory Window 2|
|0x26-0x27|- reserve -|
|0x28-0x2d|Memory Window 3|
|0x2e|- reserve -|
|0x2f|Mode Control 2|
|0x30-0x35|Memory Window 4|
|0x36-0x3f|- reserve -|
* レジスタの詳細
-Identification and Revision (R, 0x00):
--bit0-7:ID
--チップのリビジョンが格納されています。i82365の存在確認として非常によく使われます。
--たいていのものでは0x83が書かれています(i82365sl B step)。
--少し古い版だと0x82が書かれています(i82365sl A step)。
--そのほかの互換品だと、0x84、0x88~0x8cが書いてある事もあります。
--これ以外の値が読めるようなら、i82365がそのアドレスには存在していないと考えるのがよいでしょう。
-Interface Status (R, 0x01):
--bit0-1:BVD1, BVD2ピンの状態(メモリカードモードのときのみ有効)
--bit2-3:CD1#, CD2#ピンの状態
--bit4:WPピンの状態(メモリカードモードのときのみ有効)
--bit5:READY(メモリカードモードのときのみ有効)
--bit6:Power Active
--bit7:etc
--BVDピンはボタン電池でバックアップするタイプのSRAMカード用のバッテリ残量ピンで、今では使われないと思うので説明はパス。
--CDピンはCard Detecetを意味していて、スロットにカードが刺さっているかどうかを検出するためのものです。11ならカードあり、00ならカードなしです。他の値の場合は、カードが中途半端にささっているとか、抜き差し中であったりするでしょう。
--WPはWrite Protectピンですが、これも今のデバイスでは使われない情報なのでパスです(ストレージデバイスであるPCカードATAでもこのピンは使いません)。
--READYも今のデバイスでは使わない情報なので説明はパス。
--Power Activeが1になっていると、カードに電源が供給されていることを意味します。0だと供給されていません。CardBusではこの互換レジスタを使わずに、プレゼントステータスレジスタでチェックするほうがいいと思います。
--etcはデバイスによって意味がまちまちです。とりあえず気にしなくていいでしょう。
-Power and RESETDEV Control (R/W, 0x02):
--bit0-3:Vpp Control
--bit4:PC Card Power Enable
--bit5:Auto Power Switch Enable
--bit6:Disable RESETDRV
--bit7:Output Enable
--Vpp Controlはカードの補助電源制御なのですが、とりあえずこれを使うカードを使ったことがないのでよく分かりません。とりあえず0000にしておけば補助電源をOFFにできます。[[K]]はいつもOFFで使っていますが問題ありません。[[(PCMCIA)CIS]]に補助電源に関する記述が見付かったら、それにあわせて設定すればいいんじゃないでしょうか。
--PC Card Power Enableを1にするとカードに電源(Vcc)が供給され始めます。ちゃんと供給されたかどうかはInterface Status(0x01)でチェックできます。なおこのビットを0にすると補助電源の設定に関わらず、補助電源もOFFになるようです。
--Auto Power Switch Enableを1にしておくとカードの抜きさしに応じて自動的に電源がON/OFFされます。一見便利に思えるかもしれませんが、電源はソフトウェアで地道に制御するほうがいいと思うので、このbitを常に0にしておくことを[[K]]はおすすめします。
--Disable RESETDRVを0にしておくと、RESETDRVが有効になって、マシンがサスペンドから復帰するたびにコントローラのレジスタ内容をリセットするようになります。こんなことをしてもらいたいケースがあるのか?と思うほどなので、[[K]]はいつもこのビットは1にしています。
--Output Enableを0にすると、設定してあるI/Oウィンドウやメモリウィンドウ内にアクセスしても、またInterrupt and General Control(0x03)をいじっても、カードには伝わらなくなります。1にしておけばちゃんと伝わります。[[K]]はいつも1にしています。
-Interrupt and General Control (R/W, 0x03):
--bit0-3:Card IRQ Number
--bit4:Socket -INTR Enable
--bit5:PC Card Type
--bit6:PC Card -Reset
--bit7:Ring Indicate Enable
--Card IRQ Numberは、カードが発生させた割り込みをIRQの何番に割り振るかを決めるものです。カードによっては設定できない番号もあるので、[[(PCMCIA)CIS]]をちゃんと読んで設定可能な値を設定するようにします。カードからの割込み信号を使わないとき、ここは0000でいいでしょう。なお、CardBusを使っている場合にここが0000だと、カードからの割り込みが発生した場合はPCIバスのINTA#やINTB#で割り込みが通知されます(むしろPCIデバイスとしてはこれこそ正常)。この場合、カード割り込みとスロット状態割り込みを一つのIRQで共有していることになるので、IRQはかならずレベルトリガに設定してください。
--Socket -INTR EnableはPCカードスロットの状態変化(主にカードの抜き差し)があった場合に割り込みを発生させるかどうかです。1にすると割り込みが発生します。このとき発生する割り込みのIRQ番号はCard Status Change Interrupt Configuration(0x05)で設定します。CardBusではカード状態はCardBusのステータスからより詳しく読めるので、16bitPCICで状態変化を割り込み報告してもらう必要はないでしょう。CardBus側の設定で、状態変化割り込みを受けられます。
--PC Card TypeはPCカードのモードを設定します。0だとメモリカードモードで、1だとI/Oカードモードです。この設定は主にホスト側のもので、この設定を変えるとカード側がそのモードに切り替わるというわけではありません。カード側では、CCORにコンフィグレーションインデックスを設定して、メモリカードモード、I/Oカードモードを切り替えます。カード側と常に設定を一致させておくべきでしょう。なお、起動時のカードは必ずメモリモードになっています。
--PC Card -ResetはPCカードへのリセット信号の制御です。実際のリセット信号の反転です。通常はこのbitを1にして使いますが、リセットするときはこのビットを0にして、そして1にします。0を持続しなければいけない時間は一説によると100msなのですが、なんかこれは1に戻してからの時間のミスなんじゃないかという気もします。[[K]]は20msほど0を維持して、その後1に戻してまた20msほど待つ、というのがいいんじゃないかと勝手に思っています。
---先日、10ms-10ms程度でやったところ、うまくリセットされないPCカードがありましたので、一応報告しておきます。20ms-20msにしたら直りました。
--Ring Indicate EnableはI/Oモードのときのみ意味を持つbitなのですが、よくわからないので常に0にしています。
-Card Status Change (?, 0x04):
--bit0:Battery Dead Change / STSCHG# Change
--bit1:Battery Warning Change
--bit2:READY Change
--bit3:Card Detect Change
--bit4:GPI Change
--bit5-7:分かりません (すみません)
--確か該当する状態変化があると1になるんだったと思います。それでこのレジスタをリードすると0にもどる、だったかなあ。実はあまりよく分かっていません。
-Card Status Change Interrupt Configuration (R/W, 0x05):
--bit0:Battery Dead Enable / STSCHG# Enable
--bit1:Battery Warning Enable
--bit2:READY Enable
--bit3:Card Detect Enable
--bit4-7:Socket IRQ Selection
--bit0-2はどれも非常に古いデバイスでしか意味を持たない状態信号なので、検出する必要は多分無いでしょう。1にするとIRQでの報告対象になります。
--Card Detect Enableを1にするとCD1#、CD2#の変化でIRQが発生するようになります。
--Socket IRQ Selectionは、状態変化割り込みの際のIRQ番号を指定します。使わないときは0000にしましょう。
-Address Window Enable (R/W, 0x06):
--bit0:Memory Window 0
--bit1:Memory Window 1
--bit2:Memory Window 2
--bit3:Memory Window 3
--bit4:Memory Window 4
--bit5:-MEMCS16 Decode A23-A12
--bit6:I/O Window 0
--bit7:I/O Window 1
--bit0-4とbit6-7は簡単で、1にすると該当するアクセスウィンドウが有効になって、その範囲へのアクセスがPCカードに送られるようになります。0にすると、その範囲にアクセスしてもPCカードへは何も伝わりません。例えばもしメモリウィンドウ0の設定をいじるときは、まずこのレジスタでウィンドウをDisableにして、それからウィンドウの設定をいじって、最後にウィンドウをEnableにするのがいいと思います。そうでないと、設定中の中途半端な状態のウィンドウがメインメモリやI/Oデバイスとぶつかったりして、とんでもない誤動作を引き起こしかねません(たとえば設定ルーチンがあるメモリ空間とぶつかったら間違いなくやばそうです)。
--bit5がくせものなのですが、結局、常に1にしておけばいいのです。0にするとアドレスデコーダがA16-A12を無視するようになるらしいんですが、これが何のために有効なのかさっぱりです。ちゃんと全部のアドレスbitを見てもらうために、いつも1にしましょう。
-I/O Control (R/W, 0x07):
--bit0:I/O Window 0 Data Size
--bit1:I/O Window 0 -IOCS16 Source
--bit2:I/O Window 0 Zero Wait State (8bit用)
--bit3:I/O Window 0 Wait State (16bit用)
--bit4:I/O Window 1 Data Size
--bit5:I/O Window 1 -IOCS16 Source
--bit6:I/O Window 1 Zero Wait State (8bit用)
--bit7:I/O Window 1 Wait State (16bit用)
--Data Sizeを1にすると、16bitアクセスも認められるようになります。まあたいていは1にしておいて問題はないでしょう。
-- -IOCS16 Sourceを1にすると、カードが出力するIOIS16#信号からIOCS16信号が生成され、0にすると上記のData Sizeで決まるようになるらしいです。これも1にしておけばいいんじゃないでしょうか。
--Zero Wait Sateを1にすると、8bitアクセス時にシステムバスに-ZEROWS信号を出力して、I/Oアクセスが速くなるらしいです。16bitアクセス時にはこの設定は関係ないらしいです。一般には1にしておけばいいのかな?
--Wait Stateを1にすると、16bitアクセス時に1ウェイトが挿入されるらしいです。8bitアクセス時は無関係です。一般には0にしておけばいいのかな?
-I/O Window (R/W, 0x08-0x0b, 0x0c-0x0f)
--4バイトのレジスタ群です。
--+0のbit0-7:Start Low (A0-A7)
--+1のbit0-7:Start High (A8-A15)
--+2のbit0-7:Stop Low (A0-A7)
--+3のbit0-7:Stop High (A8-A15)
--CPUからのI/Oアクセス命令が発せられたとき、もしそのアドレスが「Start <= addr <= Stop」であれば、それはPCカードへのI/Oであると判断し、PCカードのaddrアドレスへアクセスします。
-Memory Window (R/W, 0x10-0x15, 0x18-0x1d, 0x20-0x25, 0x28-0x2d, 0x30-0x35)
--6バイトのレジスタ群です。
--+0のbit0-7:Start Low (A12-A19)
--+1のbit0-3:Start High (A20-A23)
--+1のbi4-5:リザーブ (0にせよ)
--+1のbit6:Zero Wait State (8bit用)
--+1のbit7:Data Size
--+2のbit0-7:Stop Low (A12-A19)
--+3のbit0-3:Stop High (A20-A23)
--+3のbit4-5:リザーブ (0にせよ)
--+3のbit6-7:Wait State (16bit用)
--+4のbit0-7:Offset Low (A12-A19)
--+5のbit0-5:Offset High (A20-A25)
--+5のbit6:REG Active
--+5のbit7:Write Protect
--CPUからのI/Oアクセス命令が発せられたとき、もしそのアドレスが「Start <= addr <= Stop」であれば、それはPCカードへのメモリアクセスであると判断します。なお、アクセス判定の比較に際して、Startの下位12bitについては0x000と見なして、Stopの下位12bitについては0xfffと見なします。したがってレジスタ上で、Start(A12-A23) == Low(A12-A23)となっていても、メモリウィンドウは4KB存在します。
--これでPCカード側のaddrをアクセスする、という仕組みならI/O Windowと同じで分かりやすいんですが、これだと64MBのPCカードメモリ空間のうちの16MB以降にはアクセスできませんし、最低でも512KBのメモリを塔載しているAT互換機では、最初の512KBもアクセスできないことになります。これではまずいのでOffsetというレジスタがあります。
--PCカードへのアクセスアドレスは、(addr + Offset)となります。Offsetの下位12bitは0x000が補われます。PCICはISAバス前提なので、加算結果が16MBを越えたら0x00ffffffでマスクされます。
--Zero Wait Stateを1にすると、このゾーンに8bitアクセスがあった場合に、waitサイクルを入れないことを意味します。アクセススピードに問題がなければ、1にしましょう。
--Data Sizeを1にすると、16bitアクセスが許可されます。常に1にしてもいいと思います。
--Wait Stateは16bitアクセス時のwaitをいくつ入れるかを決めます。アクセススピードに問題がなければ、00にしましょう。
--REG Activeを1にすると、属性メモリ空間モードになり、0にするとコモンメモリ空間モードになります。
--Write Protectを1にすると、このウィンドウへのアクセスではライトアクセスができなくなります。0だとどちらもできます。問題がなければここは0にするといいでしょう。
-Card Detect and General Control (?, 0x16)
--bit0:16Delay
--bit1:Configuration Reset
--bit2:GPI Enable
--bit3:GPI Control
--bit4:Resume
--bit5:Software IRQ
--bit6-7:分かりません (すみません)
--名前の分かっているbitも使い方はまだよくわかっていません。
-Global Control (?, 0x1e)
--bit0:Power Down
--bit1:CSC LEV
--bit2:WRBACK
--bit3:IRQ_0_LEV
--bit4:IRQ_1_LEV
--bit5-7:分かりません (すみません)
--名前の分かっているbitも使い方はまだよくわかっていません。
-Mode Control 1 (?, 0x1f)
--bit0-7:分かりません (すみません)
-Mode Control 2 (?, 0x2f)
--bit0-7:分かりません (すみません)
* 割り込みを使わない制御例
-割り込みを使わないで制御する例を紹介します。まずはこれを理解して、それから割り込みを使った本格的な制御方法を理解するのが、一番楽でしょう。
-まず最初は存在チェックです。Identification and Revision (0x00)を読んでやりましょう。CardBusのPCIC互換機能を使っている場合は、このチェックを省略してもいいでしょう。
-存在を確認できたら、誤動作を防ぐために、Address Window Enable (0x06)に0x20を書き込んで、全てのWindowをdisableにしておきます。また、Interrupt and General Control (0x03)に0x40を書いて、割り込みを使用しない設定にします。
-Interface Status (0x01)を読み込んで、bit2-3が11かどうかをチェックします。11ならカードが存在しますので、次に進みます。11以外であれば、カードは入っていないと判断していいでしょう。
--非11から11になった瞬間に、すぐにカードへのアクセスが可能だというわけではありません。ポーリングなどの場合は気をつけてください。これはPCICやCardBusの割り込みを利用してカード検出をやる場合にも当てはまります。抜き差しにあたっては、しばらく信号が不安定な状態になります。
--最初に変化を検出したらタイマーを仕掛けて、100ms~1000ms後くらいに再度チェックするのがよいと思います。
-カードが検出できたのなら、カードに電気を供給しましょう。Power and RESETDEV Control (0x02)のbit4を使います。設定したらしばらくタイマーなどで待って、Interface Status (0x01)のbit6を見ます。1になっていなかったら、何かがおかしいです。
--CardBusで16bitカードに電気を供給する方法は、CardBusのページのほうを見てください。
-カードに無事電気を供給できたら、次はカードリセットです。Interrupt and General Control (0x03)を使います。0x00を書いてしばらく待って、0x40を書いてしばらく待つ、という方法でリセットします。どのくらい待つべきなのかはInterrupt and General Control (0x03)の説明のところに書いたので、そこを見てください。
-リセットできたら、次は属性メモリ空間へのアクセスです。仮にタプルを解析しないでカードタイプを決め打ちで操作するにしても、属性メモリ空間のCCORを設定する必要はあるでしょうから、やはり属性メモリ空間へのアクセスは必要です。
--CompactFlashをメモリマップトモードで利用する場合は例外かもしれませんが(CompactFlashフル互換の[[PCカードATA]]の場合も含む)。CompactFlashであっても、CCORの設定を省略しないほうが、気分的にはかなりいいと[[K]]は思います。
--ここでは例として、0xd0000~0xd0fffに属性メモリ空間の最初の4KBをマッピングしてみます。メモリウィンドウ0を使ってみます。
--レジスタ0x10~0x15に、次の値をセットします。
--0xd0, 0xc0, 0xd0, 0x00, 0x30, 0x7f
--もしCardBusのPCIC互換機能を使っている場合は、メモリウィンドウ0のA24-A31の設定も忘れずに。
--そしてAddress Window Enable (0x06)に0x21を書いてメモリウィンドウ0を有効にします。
--この状態で0xd0000から読んでいけば、[[(PCMCIA)CIS]]タプルが読めるわけです。またCompactFlashでは0xd0200にCCORが現われるので、ここに書き込めばカードのモードを設定して早速操作できます。
-ここから先はカードによって違うので、説明はここまでにしたいと思います。
-カードの電源を切る場合も一応書いておきます。ここでもやはりPower and RESETDEV Control (0x02)のbit4を使います。設定したらしばらくタイマーなどで待って、Interface Status (0x01)のbit6を見ます。0になっていなかったら、何かがおかしいです。
--CardBusの場合は、Power and RESETDEV Control (0x02)のbit4を0にした後にCardBusの電源制御レジスタで電源を切り、そしてCardBus側の電源ステータスで切れたかどうかを確認するのがいいようです。
* こめんと欄
-こちらの記事大変参考になりました。ありがとうございます。こちらFreeDOSでENE CB1410を試しているのですが、メモリウインドウにCISが出てきません。なにかご存知の事あれば教えていただけると助かります。 -- ''やもり'' SIZE(10){2022-01-19 (水) 18:47:50}
-0x02にbit7を立てたら見えました。お騒がせしました。 -- ''やもり'' SIZE(10){2022-01-21 (金) 13:05:34}
#comment