メッセージ欄

2014年1月の日記

一覧で表示する

APC8750にNetBSD/evbarmを移植するための記録 8/25
2014/01/13(月) 18:12 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
最初の四つは似たような関数名だ。suspend, resumeはPMのifdefで囲われているので、パワーマネジメント関係が必要になってきたら頑張ればよいとして、関数の有無レベルでの差分は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で実装してあげればいいんですね、たぶん。

APC8750にNetBSD/evbarmを移植するための記録 8/24
2014/01/13(月) 16:44 NetBSD はてブ情報 はてブに登録 はてブ数

8/24からは書き溜めたメモから順次投下していく。敬体から常体に書きぶりも変わってマス。

二台目は普通に動いてうれしい。

u-bootに毎回同じコマンドを入れるのが面倒くさい。ということで対策。

tftpbootはデフォルトファイル名をC0A80002.imgが設定されているので、netbsd.binにシンボリックリンクを張れば少しだけサボれる。

結局、ほかのドライバと比較するときわかりにくいのでobioをgeminiのobioっぽく書き換え。oba -> obioに変更するなど。ドライバ全体に波及。

シリアルドライバ、matchが1を返すという実装から、geminiのような初期化を加える。
さらに、attachはsc->sc_typeをCOM_TYPE_16650_NOERSに変更。当たり前だが、起動時のメッセージが変わった。で、attachの前後にデバッグ文を入れてみたら、uartのattachはなぜか二回呼ばれている。

ねんがんのシリアル「入力」はできない。

良く考えたら、割り込みコントローラって、コンパイルを通るようにしただけなので、動いているわけがない。

シリアルの入力は、割り込みによって入力処理に飛ばされるはずだから、割り込みコントローラの実装をやらないとダメって気が。当たり前か。consinitで表示ができるのも、わりと謎だが...
WMT # set serverip 192.168.0.10
WMT # tftpboot 8000
EEP-less strapping = TRUE
*** Warning: no boot file name; using 'C0A80002.img'
TFTP from server 192.168.0.10; our IP address is 192.168.0.2
Filename 'C0A80002.img'.
Load address: 0x8000
Loading: #################################################################
         #################################################################
         ######################################T ###########################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ######################################
done
Bytes transferred = 2520256 (2674c0 hex)
WMT # go 8000
## Starting application at 0x00008000 ...
00008328
NetBSD
00000000 00000000 0048096b 00000412
00000000 00000412 00000004 00000412
c0000000 00000000 0048096b 0000041e
00003000 0000041e 00000004 0000041e
fe000000 d8000000 010fffff 00000412
00003f80 d8000412 00000010 00000412
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000



NetBSD/evbarm (APC) booting ...
[ Kernel symbol table missing! ]
Loaded initial symtab at 0xc01b4364, strtab at 0xc01d3494, # entries 7940
pmap_postinit: Allocated 35 static L1 descriptor tables
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 6.99.23 (APC) #28: Sat Aug 24 13:53:26 UTC 2013
        root@builder.localdomain:/usr/obj/sys/arch/evbarm/compile/APC
total memory = 512 MB
avail memory = 499 MB
mainbus0 (root)
cpu0 at mainbus0 core 0: ARM1176JZ-S r0p7 (ARM11J V6ZK core)
cpu0: DC enabled IC enabled WB enabled LABT
cpu0: 16KB/32B 4-way L1 Instruction cache
cpu0: 16KB/32B 4-way write-back-locking-C L1 Data cache
vfp0 at cpu0: VFP11
obio0 at mainbus0
<wm8750_uart_match>
comprobe1 0
</wm8750_uart_match rv=0>
wmicu0 at obio0 addr 0xd8140000-0xd814ffff
<wm8750_uart_match>
com0 at obio0 addr 0xd8200000-0xd820ffff intr 32  : ns16650, no ERS, working fifo
com0: console
wmtmr0 at obio0 addr 0xd8100000-0xd810ffff intr 48 : VC System Timer
rn_init: radix functions require max_keylen be set
boot device: <unknown>
root device:
<wm8750_uart_match>はデバッグ文である。むかしむかしJavaでこういう書き方したのでやってみたが、イマイチカッコ悪いな。

で、comprobe1でへくっとる。
根本的におかしいんじゃないのかなと思ってしまう。

geminiとか、consinitでbus_space_mapしてるんだよな。
やったほうがいいのかな。comcnattachだけでええんやろか?
よく考えたら、ここでもCOM_TYPE_NORMALってやってるけど、変えたほうがいいのか?

調べてみたら、COM_TYPE_NORMALじゃないやつって、COM_TYPE_PXA2x0を除けばgeminiのCOM_TYPE_16550_NOERSぐらいか。むしろgeminiが異端に見えるので真似しないほうがよいのかも...

やっぱり、シリアルコンソールの出力はマグレで表示されていると思ったほうがよさそうな気がしてきた。

APC8750にNetBSD/evbarmを移植するための記録 8/23
2014/01/12(日) 25:49 NetBSD はてブ情報 はてブに登録 はてブ数

二台目のAPCが手元に届きました。実は8/22に到着していたのですが、不在にて持ち帰りだったのです。

8/19に注文で8/22に到着とは、思いのほか早く手に入りました。

APC8750にNetBSD/evbarmを移植するための記録 8/19
2014/01/12(日) 25:43 NetBSD はてブ情報 はてブに登録 はてブ数

シリアルポートがぶっ壊れたので、販売元に修理できないかなぁ、とメールしてみた。けど、まぁ、台湾に送って、修理してもらって、送り返されて、などとやっているうちに、きっとすごい時間がかかるだろうし、今のモチベーションを保てる自信もないなぁ。

ということで、もう一台購入してしまいました。

これで、今より何も進まなかったら、すごい無駄遣いになるぞぉ。

動いてもいないハードウェアの2台目を買うなんて、自分でもどうかしてるんじゃないかと思います。

APC8750にNetBSD/evbarmを移植するための記録 8/17
2014/01/09(木) 27:46 NetBSD はてブ情報 はてブに登録 はてブ数

昨日の段階でシリアルコンソールの出力が眼で追える程度に遅いことをTwitterでつぶやいていたところ、清水さんがエスパー力を発揮して、何らかの理由でcomドライバのタイムアウト待ちをしているからじゃないかというアドバイスをもらいました。

アドバイスのとおりsrc/sys/dev/ic/com.c#com_common_putctimo = 150000;という部分を1/100の1500にしてみました。

すると、劇的に表示が早くなり、これまでとは別世界というか、本来の速度でシリアルコンソール出力が可能になりました!

依然として入力はできないもののシリアルコンソール出力もちゃんと動くようになって、root deivceを聞くところまでkernelは起動して、気分もよいので秋葉原に買い物に行ってしまいました。

秋葉原に出かけたのは気分転換もあったのですが、実はAPC8750のシリアルポートのピンヘッダにリード線を無造作につないでいるのが見苦しくて、ちゃんとしたコネクタできれいにつなぎたいなぁという思いがあり、コネクタ等の部品を調達しに行ったのでした。

しかし、これが大きな間違いで、コネクタを買ってきたのはよいのですが、誤った結線で動作確認をしてしまったため、シリアルの入力が物理的に壊れてしまったのです (なぜか出力は動く)。

シリアルの入力ができないとu-bootの操作もできませんし、なにより動いていないシリアルコンソールからの入力を実装することができません。

USBシリアルケーブルを疑ってみたり、リード線を疑ってみたりと試行錯誤をしましたが、やはり故障しているという事実は覆りませんでした。