phaser-ags / firmware /boot /start.S
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
829b19c verified
Raw History Blame Contribute Delete
16 kB
/*
* ARM Cortex-A8 (OMAP3530) Low-Level Startup
* Runs from SRAM before any C code — modeled on SH7734 lowlevel_init.S
*
* Responsibilities:
* 1. CPU mode + FPU + endianness (CP15)
* 2. Disable WDT2
* 3. Invalidate caches + TLB
* 4. Configure DPLL1 (MPU 600MHz) and DPLL3 (CORE)
* 5. Enable peripheral clocks (UART, GPIO, GPTIMER, GPMC, SDRC)
* 6. Initialize SDRC (JEDEC DDR sequence with wait timers)
* 7. Set up stack, zero BSS
* 8. Jump to C entry (reset_handler)
*/
#include "platform_offsets.h"
.text
.arm
.align 2
.global _start
.global lowlevel_init
/* ================================================================
* Exception Vector Table — placed at 0xFFF00000 or 0x00000000
* ================================================================ */
_start:
b reset_entry /* 0x00 Reset */
b undef_handler /* 0x04 Undefined instr */
b svc_handler /* 0x08 SVC */
b prefetch_handler /* 0x0C Prefetch abort */
b data_handler /* 0x10 Data abort */
nop /* 0x14 Reserved */
b irq_handler /* 0x18 IRQ */
b fiq_handler /* 0x1C FIQ */
undef_handler:
svc_handler:
prefetch_handler:
data_handler:
fiq_handler:
b .
irq_handler:
sub lr, lr, #4
stmfd sp!, {r0-r12, lr}
bl intc_handle_irq
ldmfd sp!, {r0-r12, pc}^
/* ================================================================
* RESET ENTRY — first code executed
* ================================================================ */
reset_entry:
/* ---- 1. Enter SVC mode, disable IRQ/FIQ ---- */
mrs r0, cpsr
bic r0, r0, #0x1F /* Clear mode bits */
orr r0, r0, #0xD3 /* SVC mode + IRQ/FIQ disabled */
msr cpsr, r0
/* ---- 2. Disable WDT2 (OMAP3530 WDT2 at 0x48314000) ---- */
ldr r1, =WDT2_WSPR
ldr r2, =0x0000AAAA
str r2, [r1]
ldr r2, =0x00005555
str r2, [r1]
/* ---- 3. Invalidate caches and TLB ---- */
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 /* Invalidate I-cache */
mcr p15, 0, r0, c7, c6, 0 /* Invalidate D-cache */
mcr p15, 0, r0, c8, c7, 0 /* Invalidate entire TLB */
/* Disable caches + MMU for clean state */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #(1 << 0) /* MMU off */
bic r0, r0, #(1 << 2) /* D-cache off */
bic r0, r0, #(1 << 12) /* I-cache off */
bic r0, r0, #(1 << 11) /* Branch prediction off */
mcr p15, 0, r0, c1, c0, 0
dsb
isb
/* ---- 4. Set VBAR (vector base) to current location ---- */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 /* VBAR = _start */
/* ---- 5. Enable NEON/VFP ---- */
mrc p15, 0, r0, c1, c0, 2 /* CPACR */
orr r0, r0, #(0xF << 20) /* CP10 + CP11 full access */
mcr p15, 0, r0, c1, c0, 2
isb
mov r0, #0x40000000 /* FPEXC.EN = 1 */
vmsr FPEXC, r0
/* ---- 6. Jump to lowlevel_init ---- */
bl lowlevel_init
/* ---- 7. Set up stack and jump to C ---- */
ldr sp, =STACK_BASE_VAL
bl bss_zero
bl reset_handler
b . /* Should never reach here */
/* ================================================================
* LOWLEVEL INIT — OMAP3530 DPLL + Clock + SDRC
* Modeled on SH7734 lowlevel_init: WDT → MMU → FRQCR → CS → DBSC
* ================================================================ */
lowlevel_init:
/* ---- A. Wait for power sequencing (50ms from power-tree.json) ---- */
ldr r0, =POWER_SEQ_DELAY
bl wait_cycles
/* ---- B. Configure DPLL1 (MPU) — 600MHz from 26MHz ref ---- */
/* Read current DPLL1 state from CM_CLKSEL1_PLL_MPU */
ldr r1, =CM_CLKSEL1_PLL_MPU
ldr r0, [r1]
/* Check if DPLL1 is already at target (M=23, N=1) */
ldr r2, =DPLL1_MPU_TARGET
cmp r0, r2
beq dpll1_done
/* DPLL1 not at target — reconfigure */
/* Step 1: Put DPLL1 in bypass (low-power stop) */
ldr r1, =CM_CLKEN_PLL_MPU
ldr r0, [r1]
bic r0, r0, #0x07 /* Clear DPLL_EN bits */
orr r0, r0, #0x04 /* DPLL_EN = MN bypass */
str r0, [r1]
/* Wait for DPLL to enter bypass */
ldr r0, =DPLL_LOCK_DELAY
bl wait_cycles
/* Step 2: Set M and N dividers */
ldr r1, =CM_CLKSEL1_PLL_MPU
ldr r2, =DPLL1_MPU_TARGET
str r2, [r1]
/* Step 3: Re-lock DPLL1 */
ldr r1, =CM_CLKEN_PLL_MPU
ldr r0, [r1]
bic r0, r0, #0x07
orr r0, r0, #0x07 /* DPLL_EN = lock mode */
str r0, [r1]
/* Wait for DPLL1 lock (typically <100µs) */
ldr r1, =CM_IDLEST_PLL_MPU
dpll1_lock_wait:
ldr r0, [r1]
ands r0, r0, #0x01 /* Check ST_MPU_CLK bit */
bne dpll1_lock_wait /* 0 = locked, keep polling */
dpll1_done:
/* ---- C. Configure DPLL3 (CORE) — leave at ROM default for now ---- */
/* DPLL3 drives L3 interconnect. ROM typically sets 166MHz L3. */
/* For bring-up, we accept ROM defaults. */
/* ---- D. Configure DPLL5 (DDR) — 266MHz ---- */
ldr r1, =CM_CLKSEL2_PLL
ldr r0, [r1]
ldr r2, =DPLL5_DDR_TARGET
cmp r0, r2
beq dpll5_done
/* Bypass DPLL5 */
ldr r1, =CM_CLKEN_PLL
ldr r0, [r1]
bic r0, r0, #(0x07 << 8)
orr r0, r0, #(0x04 << 8)
str r0, [r1]
ldr r0, =DPLL_LOCK_DELAY
bl wait_cycles
/* Set DPLL5 M/N */
ldr r1, =CM_CLKSEL2_PLL
ldr r2, =DPLL5_DDR_TARGET
str r2, [r1]
/* Lock DPLL5 */
ldr r1, =CM_CLKEN_PLL
ldr r0, [r1]
bic r0, r0, #(0x07 << 8)
orr r0, r0, #(0x07 << 8)
str r0, [r1]
/* Wait for lock */
ldr r1, =CM_IDLEST_CORE
dpll5_lock_wait:
ldr r0, [r1]
ands r0, r0, #0x02
bne dpll5_lock_wait
dpll5_done:
/* ---- E. Enable peripheral clocks ---- */
ldr r1, =CM_FCLKEN1_CORE_VAL
ldr r0, [r1]
ldr r1, =CM_FCLKEN1_CORE
str r0, [r1]
ldr r1, =CM_ICLKEN1_CORE_VAL
ldr r0, [r1]
ldr r1, =CM_ICLKEN1_CORE
str r0, [r1]
ldr r1, =CM_FCLKEN_WKUP_VAL
ldr r0, [r1]
ldr r1, =CM_FCLKEN_WKUP
str r0, [r1]
ldr r1, =CM_ICLKEN_WKUP_VAL
ldr r0, [r1]
ldr r1, =CM_ICLKEN_WKUP
str r0, [r1]
/* Wait for clocks to become active */
ldr r0, =CLOCK_SETTLE_DELAY
bl wait_cycles
/* ---- F. Initialize SDRC — JEDEC DDR init sequence ---- */
/* Modeled on SH7734 DBSC3 init: power pad → NOP → PRECHARGE →
REFRESH ×2 → MRS → EMRS → normal, with wait timers */
bl sdrc_init_asm
/* ---- G. Initialize GPMC for NOR/NAND ---- */
bl gpmc_init_asm
/* ---- H. Return to caller ---- */
bx lr
/* ================================================================
* SDRC INIT — Assembly-level JEDEC DDR sequence
* OMAP3530 SDRC for MT48H32M16LF-7 (mobile SDR SDRAM)
* Mirrors SH7734 DBSC3 pattern: pad power → NOP → PRC → REF → MRS
* ================================================================ */
sdrc_init_asm:
/* Enable SDRC interface clock (should be on, but be safe) */
ldr r1, =SDRC_SYSCONFIG
ldr r0, =0x00000010 /* Smart idle, auto-wakeup */
str r0, [r1]
ldr r0, =WAIT_50US
bl wait_cycles
/* CS0 mapping: 256MB at 0x30000000 */
ldr r1, =SDRC_CS_CFG_REG
ldr r0, =0x00000030
str r0, [r1]
/* SDRC sharing */
ldr r1, =SDRC_SHARING_REG
ldr r0, =0x00000000
str r0, [r1]
/* MCFG0: Mobile SDRAM, 4 banks, 8K rows */
ldr r1, =SDRC_MCFG_0_REG
ldr r0, =SDRC_MCFG_0_VAL
str r0, [r1]
/* ACTIM_CTRLA0: tRFC=22, tXSR=22, tRP=2, tRCD=3 */
ldr r1, =SDRC_ACTIM_CTRLA_0_REG
ldr r0, =SDRC_ACTIM_CTRLA_0_VAL
str r0, [r1]
/* ACTIM_CTRLB0: tRAS=5, tRC=7, tWR=2, tRASMAX=9 */
ldr r1, =SDRC_ACTIM_CTRLB_0_REG
ldr r0, =SDRC_ACTIM_CTRLB_0_VAL
str r0, [r1]
/* RFR_CTRL0: Refresh rate — 7.8µs @ 133MHz = 1040 cycles */
ldr r1, =SDRC_RFR_CTRL_0_REG
ldr r0, =SDRC_RFR_CTRL_0_VAL
str r0, [r1]
/* ---- JEDEC Init Sequence (mirrors SH7734 DBPDCNT sequence) ---- */
/* Step 1: NOP command (DBPDCNT3_D equivalent — first pad power-on) */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000000 /* CMD = NOP */
str r0, [r1]
ldr r0, =WAIT_200US /* 200µs — matches SH7734 WAIT_200US_400 */
bl wait_cycles
/* Step 2: PRECHARGE-ALL */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000001 /* CMD = PRC */
str r0, [r1]
ldr r0, =WAIT_100US
bl wait_cycles
/* Step 3: AUTO-REFRESH #1 (SH7734 does 2× DBCMD_D1) */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000004 /* CMD = REF */
str r0, [r1]
ldr r0, =WAIT_32MCLK /* Wait 32 MCLK cycles */
bl wait_cycles
/* Step 4: AUTO-REFRESH #2 */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000004 /* CMD = REF */
str r0, [r1]
ldr r0, =WAIT_32MCLK
bl wait_cycles
/* Step 5: MRS — CAS latency 3, burst length 4, sequential */
ldr r1, =SDRC_MR_0_REG
ldr r0, =SDRC_MRS_VAL /* CMD=MRS | CL=3 | BL=4 | Sequential */
str r0, [r1]
ldr r0, =WAIT_16MCLK
bl wait_cycles
/* Step 6: EMRS — extended mode (drive strength, ODT if DDR2) */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000002 /* CMD = EMRS */
str r0, [r1]
ldr r0, =WAIT_16MCLK
bl wait_cycles
/* Step 7: Normal operation */
ldr r1, =SDRC_MR_0_REG
ldr r0, =0x00000000
str r0, [r1]
/* Dummy read from SDRAM (like SH7734 synco + mov.l @r1, r0) */
ldr r1, =SDRAM_BASE_ADDR
ldr r0, [r1]
dsb
ldr r0, =WAIT_10KMCLK /* Settle — matches SH7734 WAIT_10KMCLK */
bl wait_cycles
bx lr
/* ================================================================
* GPMC INIT — minimal NOR + NAND setup (SH7734 CS0CTRL/CSWCR)
* ================================================================ */
gpmc_init_asm:
/* Enable GPMC functional clock (should be on) */
/* GPMC SYSCONFIG: smart idle */
ldr r1, =GPMC_SYSCONFIG_REG
ldr r0, =0x00000010
str r0, [r1]
ldr r0, =WAIT_50US
bl wait_cycles
/* GPMC TIMEOUT: 16× timeout, enable */
ldr r1, =GPMC_TIMEOUT_CTRL_REG
ldr r0, =0x00010011
str r0, [r1]
/* CS0: NOR Flash 16-bit async at 0x08000000 */
ldr r1, =GPMC_CONFIG1_0_REG
ldr r0, =NOR_GPMC_CONFIG1
str r0, [r1]
ldr r1, =GPMC_CONFIG2_0_REG
ldr r0, =NOR_GPMC_CONFIG2
str r0, [r1]
ldr r1, =GPMC_CONFIG3_0_REG
ldr r0, =NOR_GPMC_CONFIG3
str r0, [r1]
ldr r1, =GPMC_CONFIG4_0_REG
ldr r0, =NOR_GPMC_CONFIG4
str r0, [r1]
ldr r1, =GPMC_CONFIG5_0_REG
ldr r0, =NOR_GPMC_CONFIG5
str r0, [r1]
ldr r1, =GPMC_CONFIG6_0_REG
ldr r0, =NOR_GPMC_CONFIG6
str r0, [r1]
/* CS0 base address + enable */
ldr r1, =GPMC_CONFIG7_0_REG
ldr r0, =NOR_GPMC_CONFIG7
str r0, [r1]
/* CS1: NAND Flash 8-bit at 0x40000000 */
ldr r1, =GPMC_CONFIG1_1_REG
ldr r0, =NAND_GPMC_CONFIG1
str r0, [r1]
ldr r1, =GPMC_CONFIG7_1_REG
ldr r0, =NAND_GPMC_CONFIG7
str r0, [r1]
/* Reset NAND device */
ldr r1, =GPMC_NAND_CMD_1
ldr r0, =0xFF /* NAND_CMD_RESET */
strb r0, [r1]
ldr r0, =WAIT_200US
bl wait_cycles
bx lr
/* ================================================================
* BSS ZERO — clear .bss section
* ================================================================ */
bss_zero:
ldr r0, =__bss_start
ldr r1, =__bss_end
mov r2, #0
bss_loop:
cmp r0, r1
bge bss_done
str r2, [r0], #4
b bss_loop
bss_done:
bx lr
/* ================================================================
* WAIT CYCLES — simple busy loop (SH7734 wait_timer pattern)
* ================================================================ */
wait_cycles:
subs r0, r0, #1
bne wait_cycles
bx lr
/* ================================================================
* LITERAL POOL — all constant addresses and values
* ================================================================ */
.align 2
/* ---- OMAP3530 PRCM (Power/Reset/Clock Management) ---- */
CM_CLKEN_PLL_MPU: .long 0x48004900
CM_IDLEST_PLL_MPU: .long 0x48004920
CM_CLKSEL1_PLL_MPU: .long 0x48004940
DPLL1_MPU_TARGET: .long 0x00170001 /* M=23, N=1 → 598MHz */
CM_CLKEN_PLL: .long 0x48004D00
CM_IDLEST_CORE: .long 0x48004A20
CM_CLKSEL2_PLL: .long 0x48004D44
DPLL5_DDR_TARGET: .long 0x000A0001 /* M=10, N=1 → 260MHz */
/* ---- Clock enable values ---- */
CM_FCLKEN1_CORE: .long 0x48004A00
CM_FCLKEN1_CORE_VAL: .long 0x04060026 /* EN_UART1|EN_UART2|EN_GPMC|EN_HSMMC1 */
CM_ICLKEN1_CORE: .long 0x48004A10
CM_ICLKEN1_CORE_VAL: .long 0x04068026 /* Interface clocks */
CM_FCLKEN_WKUP: .long 0x48004C00
CM_FCLKEN_WKUP_VAL: .long 0x00000014 /* EN_GPIO1 | EN_GPT1 */
CM_ICLKEN_WKUP: .long 0x48004C10
CM_ICLKEN_WKUP_VAL: .long 0x00000014
/* ---- WDT2 ---- */
WDT2_WSPR: .long 0x48314048
/* ---- SDRC registers ---- */
SDRC_SYSCONFIG: .long 0x6D000010
SDRC_CS_CFG_REG: .long 0x6D000040
SDRC_SHARING_REG: .long 0x6D000044
SDRC_MCFG_0_REG: .long 0x6D000080
SDRC_MR_0_REG: .long 0x6D000084
SDRC_ACTIM_CTRLA_0_REG: .long 0x6D00009C
SDRC_ACTIM_CTRLB_0_REG: .long 0x6D0000A0
SDRC_RFR_CTRL_0_REG: .long 0x6D0000A4
SDRC_MCFG_0_VAL: .long 0x02584011 /* DDR, 4 banks, 8K rows, 512 cols */
SDRC_ACTIM_CTRLA_0_VAL: .long 0x16A00902 /* tRFC=22, tXSR=22, tRP=2, tRCD=3 */
SDRC_ACTIM_CTRLB_0_VAL: .long 0x50290705 /* tRAS=5, tRC=7, tWR=2, tRASMAX=9 */
SDRC_RFR_CTRL_0_VAL: .long 0x00041001 /* 1040 cycles, enabled */
SDRC_MRS_VAL: .long 0x00000033 /* CMD=MRS, CL=3, BL=4, sequential */
SDRAM_BASE_ADDR: .long 0x30000000
/* ---- GPMC registers ---- */
GPMC_SYSCONFIG_REG: .long 0x6E000010
GPMC_TIMEOUT_CTRL_REG: .long 0x6E000040
GPMC_CONFIG1_0_REG: .long 0x6E000060
GPMC_CONFIG2_0_REG: .long 0x6E000064
GPMC_CONFIG3_0_REG: .long 0x6E000068
GPMC_CONFIG4_0_REG: .long 0x6E00006C
GPMC_CONFIG5_0_REG: .long 0x6E000070
GPMC_CONFIG6_0_REG: .long 0x6E000074
GPMC_CONFIG7_0_REG: .long 0x6E000078
GPMC_CONFIG1_1_REG: .long 0x6E000090
GPMC_CONFIG7_1_REG: .long 0x6E0000A8
GPMC_NAND_CMD_1: .long 0x6E0001D0
/* NOR Flash GPMC config (CS0: 16-bit async) */
NOR_GPMC_CONFIG1: .long 0x00001200 /* 16-bit, NOR async */
NOR_GPMC_CONFIG2: .long 0x001F1F08 /* CS on/off timing */
NOR_GPMC_CONFIG3: .long 0x00060602 /* ADV on/off */
NOR_GPMC_CONFIG4: .long 0x0C0C0303 /* OE/WE on/off */
NOR_GPMC_CONFIG5: .long 0x02020202 /* RD/WR cycle/access */
NOR_GPMC_CONFIG6: .long 0x00020200 /* WrDataMux, wr access */
NOR_GPMC_CONFIG7: .long 0x00000F4E /* Base=0x08, mask=0xF, CSVALID */
/* NAND Flash GPMC config (CS1: 8-bit NAND) */
NAND_GPMC_CONFIG1: .long 0x00000800 /* 8-bit, NAND */
NAND_GPMC_CONFIG7: .long 0x00000FCE /* Base=0x40, mask=0xF, CSVALID */
/* ---- Wait timer values (SH7734 pattern) ---- */
POWER_SEQ_DELAY: .long 30000000 /* ~50ms at 600MHz */
DPLL_LOCK_DELAY: .long 300000 /* ~0.5ms at 600MHz */
CLOCK_SETTLE_DELAY: .long 300000 /* ~0.5ms */
WAIT_200US: .long 120000 /* 200µs at 600MHz */
WAIT_100US: .long 60000 /* 100µs at 600MHz */
WAIT_50US: .long 30000
WAIT_32MCLK: .long 19200 /* 32 MCLK cycles at 133MHz → 240ns → ~144k CPU cycles */
WAIT_16MCLK: .long 9600
WAIT_10KMCLK: .long 6000000 /* 10000 MCLK at 133MHz ≈ 75µs → ~45000 CPU cycles; pad to 10ms */
WAIT_1MCLK: .long 600
/* ---- Stack ---- */
STACK_BASE_VAL: .long 0x20010000
/* ---- Linker symbols ---- */
.extern __bss_start
.extern __bss_end