APC8750にNetBSD/evbarmを移植するための記録 8/25
割り込みコントローラをちゃんとしないといけないらしいが、まるで分らない。
ARMのドキュメントをあたると、Cortexなどは標準の割り込みコントローラが内蔵されているようにみえるが、ARM11は英語しかないのでわからん。が、Chapter 12. Vectored Interrupt Controller Portとかいうのがある。こいつはPL192とかいうやつらしいのでその仕様書を読めと書いてある。
Linuxのソースをあたっても、PL192とかいうのはWM8750では使ってなさそうな気がする。
そういえば、http://www.spinics.net/lists/arm-kernel/msg240125.htmlとかいうページを見つけた。vt8500-intcとか読めるので、VT8500というCPU(おそらく昔のVIAのARMで、それがWonderMediaに子会社化されたとかそういうの?)とcompatibleといっておる。VT8500のirc.cとWM8750のirc.cを比較すると、似ているといえば似ているけれど、だいぶ違うような。
http://lxr.free-electrons.com/source/drivers/irqchip/irq-vt8500.c
https://github.com/apc-io/apc-8750/blob/master/kernel/arch/arm/mach-wmt/irq.c
そもそもソースツリーの構造が違うのを比較しても意味ないかもしれんな。
NetBSDの割り込み処理を読んでも、よくわからん。もしかしたら、LinuxとNetBSDの両方にある評価ボードの割り込み処理を比較して、Linuxでこう書くところをNetBSDでこう書けばよい、とわかれば、それをWM8750のirq.cからNetBSDに変換することができるかもしれん。
例によってLinux側のgeminiを見てみるか。ソースはapc-8750/kernel/arch/arm/mach-gemini/irq.cだな。
まずは、レジスタは4刻みで定義されている。
関数の直後にdevice_initcall(wmt_irq_init_devicefs);という文があるのは初期化の際に呼ばれるもののよう。
関数の中身はデバイスクラスを登録する関数を読んでいるようだ。こいつもその直前の構造体を読むとsuspend,resumeはこの関数名ですよと言っているので、まぁ、最初は気にしなくてよいことにしよう。
ack_irqは先ほどのマクロIRQ_CLEARに1<< irqをraw_write1しているだけ。
mask_irqはIRQ_MASKを読んで~(1<<irq)のANDをとって再びIRQ_MASKに書いているだけ。
unmask_irqは|=(1<<irq)しているだけ。maskってintだから32bitとして、32個しか割り込みないのかな?
init_irqが初期化処理みたい。なんか、forループでNR_IRQSの数だけ回している。
geminiのヘッダを見ると
GPIOだけマクロが
ループの中では、set_irq_chip, set_irq_handler, set_irq_flagsを割り込みそれぞれに対して呼び出している模様。
handle_edge_irqの時だけはmode, levelのビットを立てている。
ループの後、すべての割り込みを禁止して、割り込みモードとして先のmode, levelをraw_writelで書き込んでいる。
geminiは割り込み32個をビットにして管理しているってことかな?
NetBSDのgemini_icu.cをみると、確かに割り込み32個あるようだ。sources[32]がそれだろう。
関数として、block, unblockというのがLinuxのmask, unmaskっぽいな。
Linuxのack_irqに相当するものはなさそう。
NetBSDの場合、find_pending_irqs, irq_handler,establish_irqがある。
find_pending_irqs, irq_handlerそれぞれ、最後はpic_から始まる関数を呼んでいる。attachでもadd_picなどとしている。こいつが共通的な何かをしてくれるものなのだろうか。
しかし、irq_handlerは何をしているのだろう、というか、pending処理以外、何もしていないような。ハンドリングしてくれるのかと思ったが...
establish_irqは、IRQの確立、つまり初期化処理なのだろう。UARTのattachで似たような関数を呼んでいた。つまり、何らかのデバイスドライバがこいつを呼ぶということであろうか。
Linuxは最初にループでIRQの初期化を回す。NetBSDはデバイスドライバが初期化されるごとに個々のIRQの初期化処理が走るということなのかもしれない。
WMTのLinuxコードを見ると、割り込みコントローラICDC0は次のようになっている。
ちなみにmmap.cでは
それはともかく、irq.cではICDC0_VAL(irq) = val;という値の代入しかやっていない。
初期化(wmt_init_irq)では0を入れており、mask, unmaskではICDC_ENABLEを立てたり・クリアしたりしている。
ICDC_ENABLEは
ICDC0_VAL(irq)を追っかけてみる。まずは、元の定義は
まず、ICDC_OFFSET_WMT(irq)は次のように定義されている。
初期化ルーチンを読んでみる。
まず、ICDC0_ADDRを先頭とした8ビット単位で64個のテーブルを作り、0で初期化しているというわけか。
次に、おのおののIRQで使うルーチンをset_irq_chipで指定。すべて、
次に、set_irq_handlerでデフォルトのハンドラhandle_level_irq(kernel/irq/chip.c)をセット。set_irq_flagにデフォルトのフラグをセットしている。
デフォルトのフラグは
つうことで、結局、gemini, wmtともにirq.cにはMDの部分だけが書かれていたということですね。
geminiは32ビットのうち、IRQビット目を立てる、という方式だし、WMTは8ビットつまり1バイトを64個ならべて、IRQ個目の1バイトの3ビット目を立てたり、クリアしたり、という部分だけでした。
そこをNetBSDで実装してあげればいいんですね、たぶん。
ARMのドキュメントをあたると、Cortexなどは標準の割り込みコントローラが内蔵されているようにみえるが、ARM11は英語しかないのでわからん。が、Chapter 12. Vectored Interrupt Controller Portとかいうのがある。こいつはPL192とかいうやつらしいのでその仕様書を読めと書いてある。
Linuxのソースをあたっても、PL192とかいうのはWM8750では使ってなさそうな気がする。
そういえば、http://www.spinics.net/lists/arm-kernel/msg240125.htmlとかいうページを見つけた。vt8500-intcとか読めるので、VT8500というCPU(おそらく昔のVIAのARMで、それがWonderMediaに子会社化されたとかそういうの?)とcompatibleといっておる。VT8500のirc.cとWM8750のirc.cを比較すると、似ているといえば似ているけれど、だいぶ違うような。
http://lxr.free-electrons.com/source/drivers/irqchip/irq-vt8500.c
https://github.com/apc-io/apc-8750/blob/master/kernel/arch/arm/mach-wmt/irq.c
そもそもソースツリーの構造が違うのを比較しても意味ないかもしれんな。
NetBSDの割り込み処理を読んでも、よくわからん。もしかしたら、LinuxとNetBSDの両方にある評価ボードの割り込み処理を比較して、Linuxでこう書くところをNetBSDでこう書けばよい、とわかれば、それをWM8750のirq.cからNetBSDに変換することができるかもしれん。
例によってLinux側のgeminiを見てみるか。ソースはapc-8750/kernel/arch/arm/mach-gemini/irq.cだな。
まずは、レジスタは4刻みで定義されている。
#define IRQ_SOURCE(base_addr) (base_addr + 0x00) #define IRQ_MASK(base_addr) (base_addr + 0x04) #define IRQ_CLEAR(base_addr) (base_addr + 0x08) #define IRQ_TMODE(base_addr) (base_addr + 0x0C) #define IRQ_TLEVEL(base_addr) (base_addr + 0x10) #define IRQ_STATUS(base_addr) (base_addr + 0x14) #define FIQ_SOURCE(base_addr) (base_addr + 0x20) #define FIQ_MASK(base_addr) (base_addr + 0x24) #define FIQ_CLEAR(base_addr) (base_addr + 0x28) #define FIQ_TMODE(base_addr) (base_addr + 0x2C) #define FIQ_LEVEL(base_addr) (base_addr + 0x30) #define FIQ_STATUS(base_addr) (base_addr + 0x34)geminiとwmtで関数を比較する。
- gemini_ack_irq
- gemini_mask_irq
- gemini_unmask_irq
- gemini_init_irq
- wmt_ack_irq
- wmt_mask_irq
- wmt_unmask_irq
- wmt_init_irq
- wmt_irq_suspend
- wmt_irq_resume
- wmt_irq_init_devicefs
関数の直後にdevice_initcall(wmt_irq_init_devicefs);という文があるのは初期化の際に呼ばれるもののよう。
関数の中身はデバイスクラスを登録する関数を読んでいるようだ。こいつもその直前の構造体を読むとsuspend,resumeはこの関数名ですよと言っているので、まぁ、最初は気にしなくてよいことにしよう。
ack_irqは先ほどのマクロIRQ_CLEARに1<< irqをraw_write1しているだけ。
mask_irqはIRQ_MASKを読んで~(1<<irq)のANDをとって再びIRQ_MASKに書いているだけ。
unmask_irqは|=(1<<irq)しているだけ。maskってintだから32bitとして、32個しか割り込みないのかな?
init_irqが初期化処理みたい。なんか、forループでNR_IRQSの数だけ回している。
geminiのヘッダを見ると
#define NORMAL_IRQ_NUM 32 #define GPIO_IRQ_BASE NORMAL_IRQ_NUM #define GPIO_IRQ_NUM (3 * 32) #define NR_IRQS (NORMAL_IRQ_NUM + GPIO_IRQ_NUM)つうことは、割り込みの数は32じゃなくて32 + 32 * 3 で128あるってことか。
GPIOだけマクロが
#define IRQ_GPIO(x) (22 + (x))ってなっているのがポイント? GPIOだけ特別扱いされているっぽい、って思っていればいいか。
ループの中では、set_irq_chip, set_irq_handler, set_irq_flagsを割り込みそれぞれに対して呼び出している模様。
handle_edge_irqの時だけはmode, levelのビットを立てている。
ループの後、すべての割り込みを禁止して、割り込みモードとして先のmode, levelをraw_writelで書き込んでいる。
geminiは割り込み32個をビットにして管理しているってことかな?
NetBSDのgemini_icu.cをみると、確かに割り込み32個あるようだ。sources[32]がそれだろう。
関数として、block, unblockというのがLinuxのmask, unmaskっぽいな。
Linuxのack_irqに相当するものはなさそう。
NetBSDの場合、find_pending_irqs, irq_handler,establish_irqがある。
find_pending_irqs, irq_handlerそれぞれ、最後はpic_から始まる関数を呼んでいる。attachでもadd_picなどとしている。こいつが共通的な何かをしてくれるものなのだろうか。
しかし、irq_handlerは何をしているのだろう、というか、pending処理以外、何もしていないような。ハンドリングしてくれるのかと思ったが...
establish_irqは、IRQの確立、つまり初期化処理なのだろう。UARTのattachで似たような関数を呼んでいた。つまり、何らかのデバイスドライバがこいつを呼ぶということであろうか。
Linuxは最初にループでIRQの初期化を回す。NetBSDはデバイスドライバが初期化されるごとに個々のIRQの初期化処理が走るということなのかもしれない。
WMTのLinuxコードを見ると、割り込みコントローラICDC0は次のようになっている。
#define ICDC0_ADDR (__IC_BASE + 0x40) /* dest_ctl_addr[0] */__IC_BASEは
#ifdef __IC_BASE #error "__IC_BASE has already been defined in another file." #endif #ifdef INTERRUPT_CTRL_BASE_ADDR /* From wmt_mmap.h */ #define __IC_BASE INTERRUPT0_CTRL_BASE_ADDR #else #define __IC_BASE 0xFE140000 /* 64K */ #endifてなぐあい。割り込みコントローラのVAであろう。
ちなみにmmap.cでは
#define INTERRUPT0_CTRL_BASE_ADDR (0xD8140000 + WMT_MMAP_OFFSET) /* 64K */なのだ。いずれにせよVAのアドレスになっている。bus_space_mapはPAばかりだったけど...
それはともかく、irq.cではICDC0_VAL(irq) = val;という値の代入しかやっていない。
初期化(wmt_init_irq)では0を入れており、mask, unmaskではICDC_ENABLEを立てたり・クリアしたりしている。
ICDC_ENABLEは
#define ICDC_ENABLE BIT3 /* Interrupt enable bit.*/なのでBIT3を立てている・クリアしているだけである。
ICDC0_VAL(irq)を追っかけてみる。まずは、元の定義は
#define ICDC0_VAL(x) (REG8_VAL(ICDC0_ADDR + ICDC_OFFSET_WMT(x)))となり、マクロが絡み合っているので読み解こう。
まず、ICDC_OFFSET_WMT(irq)は次のように定義されている。
#define ICDC_OFFSET_WMT(x) ((x) & ICDC_NUMMASK_WMT)NUMMASKなどと言っているが、
#define ICDC_NUMMASK_WMT 0x7F /* mask to avoid overflow */ということでほぼxと思えばよい。つまり、ICDC0_VALマクロは
#define ICDC0_VAL(x) (REG8_VAL(ICDC0_ADDR + x))である。で、残ったREG8_VALは最終的にこうなる。hardware.hに定義がある。
# define __REG8P(x) (((__regbase8 *)((x)&~4095))->offset[((x)&4095)>>0]) typedef struct { volatile u8 offset[4096]; /* 4K * 1 = SZ_4K */ } __regbase8;つまり、ICDC0_ADDRを先頭に、割り込み番号ごとに8ビット割り当てられて64個ならんでいると。で、ENABLEのやつにはその8ビットの3ビット目に1が立っていると。
初期化ルーチンを読んでみる。
まず、ICDC0_ADDRを先頭とした8ビット単位で64個のテーブルを作り、0で初期化しているというわけか。
次に、おのおののIRQで使うルーチンをset_irq_chipで指定。すべて、
static struct irq_chip wmt_normal_chip = { .name = "normal", .ack = wmt_ack_irq, .mask = wmt_mask_irq, .unmask = wmt_unmask_irq, };なので、自ファイル(irq.c)にある関数を呼び出すと。
次に、set_irq_handlerでデフォルトのハンドラhandle_level_irq(kernel/irq/chip.c)をセット。set_irq_flagにデフォルトのフラグをセットしている。
デフォルトのフラグは
#define IRQF_VALID (1 << 0)なので0x01ということか。
つうことで、結局、gemini, wmtともにirq.cにはMDの部分だけが書かれていたということですね。
geminiは32ビットのうち、IRQビット目を立てる、という方式だし、WMTは8ビットつまり1バイトを64個ならべて、IRQ個目の1バイトの3ビット目を立てたり、クリアしたり、という部分だけでした。
そこをNetBSDで実装してあげればいいんですね、たぶん。
コメント(0件)
- TB-URL http://www.tokuda.net/diary/0829/tb/