明日にはでっかい太陽が昇るかもしれません。

「覚悟」とは!! 暗闇の荒野に!!進むべき道を切り開く事だッ!

U-Boot (aarch64) on Qemu を動作させたい (6)

現在は、仕事で触れている freescale の ls1046a Reference Design Board の U-Boot を Qemu で動作させるべく、仮想ボードを作成中。

CPU, RAM, UART と何とか整えてきて、i2c の調整を行っています。

U-Boot 2016.092.0+ga06b20925c (Oct 22 2017 - 21:04:46 +0900)

SoC:  LS1046AE Rev1.0 (0x87070010)
Clock Configuration:
       CPU0(   ):800  MHz  CPU1(   ):800  MHz  CPU2(   ):800  MHz  
       CPU3(   ):800  MHz  
       Bus:      600  MHz  DDR:      2100 MT/s  FMAN:     800  MHz
Reset Configuration Word (RCW):
       00000000: 0c150010 0e000000 00000000 00000000
       00000010: 11335559 40000012 60040000 c1000000
       00000020: 00000000 00000000 00000000 00238800
       00000030: 20124000 00003000 00000096 00000001
Model: LS1046A RDB Board
Board: LS1046ARDB, boot from Invalid setting of SW5
CPLD:  V0.0
PCBA:  V0.0
SERDES Reference Clocks:
SD1_CLK1 = 100.00MHZ, SD1_CLK2 = 100.00MHZ
I2C:   ready
DRAM:  wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=0
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=1
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x36 retry=2
i2c_init_transfer: give up i2c_regs=0x2180000
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=0
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=1
wait_for_sr_state: failed sr=a1 cr=38 state=202
i2c_init_transfer: failed for chip 0x51 retry=2
i2c_init_transfer: give up i2c_regs=0x2180000
DDR: failed to read SPD from address 81
Error: No valid SPD detected.
16 EiB (DDR not enabled)

"Synchronous Abort" handler, esr 0x96000045
ELR:     fff638e8
LR:      ffefd214
x0 : 00000007ffe00000 x1 : 0000000000000000
x2 : 0000000000001000 x3 : 00000007ffe01000
x4 : 00000007ffe00000 x5 : 0000000000000000
x6 : 00000000000001e8 x7 : 0000000000000001
x8 : 0000000082070550 x9 : 000000007defb000
x10: 0000000000000403 x11: 0000000082059274
x12: 0000000000000002 x13: 0000000000000000
x14: 0000000000000000 x15: 0000000082003228
x16: 0000000000000000 x17: 0000000000000000
x18: 00000000ffdf8d70 x19: 00000007ffe00000
x20: 00000007ffe00000 x21: 0000000000000000
x22: 00000000fff89490 x23: 0000000000000000
x24: 0000000000000000 x25: 0000000000000000
x26: 0000000000000000 x27: 0000000000000000
x28: 0000000082001cd8 x29: 00000000ffdf6680

Resetting CPU ...

resetting ...
guts: Unknown register read: b0
guts: Unknown register write: b0 = 2

最終的には Abort してしまっているけど、原因がどこかは現時点では未調査。

タイムアウトの原因を GDB で追いかけると、 i2c_init_transfer_() から tx_byte() に入り、 I2DR にスレーブアドレスを書き込んだあとに、 I2SR_IIF ビット (割り込み) が立たないため、タイムアウトが発生していた。

Qemu 側も GDB で追いかけると、imx_i2c.c から i2c/core.c の i2c_start_transfer() を呼び出したところで QTAILQ_FOREACH() マクロの処理が行われていないため、 I2SR_IIF ビットを立てる処理が実施されていないことがわかった。

QTAILQ_FOREACH() マクロは、 &bus->qbus.children.tqh_first にデータがある場合に実行される処理で、タイムアウトの場合は NULL となっていたため、 bus->current_devs.lh_first も空のままになり、下の QLIST_EMPTY で return 1 となっていた。

    if (QLIST_EMPTY(&bus->current_devs)) {
        QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
            DeviceState *qdev = kid->child;
            I2CSlave *candidate = I2C_SLAVE(qdev);
            if ((candidate->address == address) || (bus->broadcast)) {
                node = g_malloc(sizeof(struct I2CNode));
                node->elt = candidate;
                QLIST_INSERT_HEAD(&bus->current_devs, node, next);
                if (!bus->broadcast) {
                    break;
                }
            }
        }
        bus_scanned = true;
    }

    if (QLIST_EMPTY(&bus->current_devs)) {
        return 1;
    }

bus->qbus.children.tqh_first にデータが入る条件を調べると、 qdev_set_parent_bus() が呼ばれた場合であることがわかった。

qdev_set_parent_bus() は、システムバズに仮想デバイスをつなぐ場合に呼び出している処理で、特定のバスに仮想デバイスを接続する機能を持っている。

ということは、現在は i2c バスにデバイスが接続されていないから、スレーブアドレスの書き込みでタイムアウトになっている、ということ?

backtrace を確認すると、 DDRSPD アクセスに使用していることがわかった。

DDR の情報が取得できないので、 Abort につながっているようだ。