U-Boot board_init_f の内部動作:Mini2440 移植における初期化シーケンス

board_init_f の概要

U-Boot の第 1 段階である board_init_f は、リロケーション(RAM へのコード複写)前にグローバルデータ gd を整え、各種周辺機器とメモリ空間を準備する。本稿では Mini2440(S3C2440)を対象に、この関数がどのように U-Boot を DRAM 上へ展開する準備を進めるかを追う。

gd 構造体と初期フラグ

gd_t は U-Boot 全体で共有されるグローバルデータで、起動中の状態・メモリ情報・デバイスハンドラ等を保持する。board_init_f ではまず gd->flags と gd->have_console に初期値をセットし、以降の関数ポインタテーブルを順に実行する。

void board_init_f(ulong boot_flags)
{
    gd_t data;

    gd = &data;
    zero_global_data();

    gd->flags = boot_flags;
    gd->have_console = 0;

    if (initcall_run_list(init_sequence_f))
        hang();
}

init_sequence_f テーブル

init_sequence_f は init_fnc_t 型の関数ポインタ配列で、条件コンパイルにより不要なエントリが除去される。Mini2440 では実質的に以下の関数が実行される。

  • setup_mon_len : U-Boot イメージサイズ測定
  • initf_malloc : 早期 malloc 領域設定
  • initf_console_record : コンソール記録初期化
  • arch_cpu_init / initf_dm / arch_cpu_init_dm : 架構・ドライバモデル初期化
  • mark_bootstage : ブートステージ記録
  • board_early_init_f : クロック・GPIO 初期化
  • timer_init : タイマー初期化
  • env_init : 環境変数の検証
  • init_baud_rate / serial_init / console_init_f : シリアル/コンソール
  • display_options / display_text_info / print_cpuinfo : 情報表示
  • announce_dram_init / dram_init : DRAM 検出
  • メモリ予約群 : setup_dest_addr ~ setup_reloc

U-Boot サイズの測定

setup_mon_len は U-Boot 全体の長さを gd->mon_len に格納する。

static int setup_mon_len(void)
{
#if defined(__ARM__)
    gd->mon_len = (ulong)&__bss_end - (ulong)&_start;
#else
    gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
#endif
    return 0;
}

クロックと GPIO の初期化

board_early_init_f では S3C2440 の MPLL/UPLL を設定し、必要な GPIO を初期化する。以下は実装の要点を整理したものである。

int board_early_init_f(void)
{
    struct s3c24x0_clock_power *clk =
            s3c24x0_get_base_clock_power();
    struct s3c24x0_gpio *io = s3c24x0_get_base_gpio();

    /* ロックタイム最小化 */
    writel(0xFFFFFF, &clk->locktime);

    /* MPLL: FCLK 生成 */
    writel((M_MDIV << 12) | (M_PDIV << 4) | M_SDIV,
           &clk->mpllcon);
    pll_delay(4000);

    /* UPLL: USB 用クロック */
    writel((U_MDIV << 12) | (U_PDIV << 4) | U_SDIV,
           &clk->upllcon);
    pll_delay(8000);

    /* GPIO ポート設定 */
    writel(0x007FFFFF, &io->gpacon);
    writel(0x00044555, &io->gpbcon);
    writel(0x000007FF, &io->gpbup);
    /* ... 以下ポート設定 ... */

    return 0;
}

タイマー設定

timer_init は PWM Timer 4 を 10 ms 間隔で動作させ、以降の get_timer() 等で利用できるよう gd->arch 内のカウンタ情報を初期化する。

int timer_init(void)
{
    struct s3c24x0_timers *t = s3c24x0_get_base_timers();
    ulong ctrl;

    /* prescaler = 16 */
    writel(0x0f00, &t->tcfg0);

    if (gd->arch.tbu == 0) {
        gd->arch.tbu = get_PCLK() / (2 * 16 * 100);
        gd->arch.timer_rate_hz = get_PCLK() / (2 * 16);
    }

    writel(gd->arch.tbu, &t->tcntb4);

    ctrl = readl(&t->tcon) & ~0x0700000;
    writel(ctrl | 0x0600000, &t->tcon);   /* 手動ロード */
    writel(ctrl | 0x0500000, &t->tcon);   /* 自動ロード&開始 */

    gd->arch.lastinc = 0;
    gd->arch.tbl = 0;

    return 0;
}

環境変数の検証

env_init は保存領域先頭の CRC を検証し、成否に応じて gd->env_valid を切り替える。Mini2440 のデフォルトでは NOR フラッシュ上の env_t を参照する。CRC が合わなければ default_environment(ビルド時に生成されるデフォルト値)が使われる。

シリアル・コンソール

serial_init は GD_FLG_SERIAL_READY フラグを立て、デフォルト UART(CONFIG_SERIAL1)を初期化する。get_current() で取得されるデバイス構造体の start メソッドが UART レジスタをセットアップする。

static int s3c_uart_setup(int idx)
{
    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(idx);
    unsigned int div;

    /* FIFO 有効、Tx/Rx クリア */
    writel(0x07, &uart->ufcon);
    writel(0x00, &uart->umcon);

    /* 8N1 */
    writel(0x03, &uart->ulcon);
    writel(0x245, &uart->ucon);

    div = get_PCLK() / (16 * gd->baudrate) - 1;
    writel(div, &uart->ubrdiv);

    return 0;
}

情報表示

コンソールが利用可能になった後、以下の関数が順に実行される。

  • display_options : U-Boot バージョン文字列を出力
  • display_text_info : デバッグ時に TEXT/BSS 情報を出力
  • print_cpuinfo : CPU ID および FCLK/HCLK/PCLK を出力
  • announce_dram_init : DRAM: と出力

DRAM サイズの確定

dram_init は gd->ram_size に実装済みの SDRAM サイズをセットする。Mini2440 では 64 MB。

int dram_init(void)
{
    gd->ram_size = PHYS_SDRAM_1_SIZE;   /* 64 MB */
    return 0;
}

DRAM 末尾からのメモリ予約

DRAM 点 0x30000000 + 64 MB から順に、U-Boot 実行に必要な領域を確保していく。各予約関数は gd->relocaddr(U-Boot リロケーション先)および gd->start_addr_sp(最終スタック)を更新する。

関数内容
setup_dest_addrDRAM 天端を ram_top、初期 relocaddr に設定
reserve_round_4krelocaddr を 4 KB 境界に切り下げ
reserve_mmuTLB テーブル(16 KB)を予約し 64 KB 整列
reserve_traceトレースバッファを予約
reserve_ubootU-Boot コード・データ・BSS 分を 4 KB 整列で予約
reserve_mallocmalloc プール(4 MB)を予約
reserve_boardbd_t 構造体を予約
setup_machineマシンタイプ ID を設定
reserve_global_data新 gd_t を予約
reserve_fdtFDT 用領域を予約
reserve_stacksスタックを 16 バイト境界に調整
setup_dram_configバンクごとの DRAM 情報を gd->bd に設定
show_dram_configDRAM 合計サイズを表示

リロケーション準備の完了

reloc_fdt は FDT を移動先にコピーし、setup_reloc は最終的なオフセット gd->reloc_off を計算し、現在の gd_t を新しい gd_t 領域へ複製する。

static int setup_reloc(void)
{
#ifdef CONFIG_SYS_TEXT_BASE
    gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
#endif
    memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));

    return 0;
}

最終的な gd フィールド(Mini2440 例)

以上の処理後、主要な gd フィールドは以下のようになる。

フィールド内容
ram_size64 MB
ram_top0x34000000
relocaddr0x34000000 - U-Boot サイズ - 64 KB 程度
start_addr_sprelocaddr より malloc/bd/gd/スタック 分を引いた位置
baudrate115200 bps
have_console1
env_valid0(初回はデフォルト環境変数)
mon_lenU-Boot イメージ全体のバイト数
]]>

タグ: U-Boot S3C2440 Mini2440 board_init_f BootLoader

10月23日 02:56 投稿