2014-02-14 35 views
2

我必須將我的設備從Linux 2.6.38置入非常深的低功耗模式,因此有必要暫停所有組件,包括CPU和DDR2。i.MX35從IRAM暫停CPU和DDR2

到目前爲止我發現的是,我必須將核心彙編程序函數複製到處理器的內部存儲器中並從那裏執行它。基本上,它看起來像這樣:

cpaddr = iram_alloc(SZ_1K, &iram_addr); 
if(!cpaddr) return -ENOMEM; 
suspend_iram_base = __arm_ioremap(iram_addr, SZ_1K, MT_HIGH_VECTORS); 
memcpy(suspend_iram_base, cpu_v6_sdram_off, SZ_1K); 
flush_icache_range(suspend_iram_base, suspend_iram_base + SZ_1K); 
flush_cache_all(); 

__asm__ __volatile__(
    "ldr r0, %0\n" 
    "ldr r1, %1\n" 
    "ldr r2, %2\n" 
    "blx r2\n" 
    "nop\n" 
    : : "m" (esdctl_addr), 
     "m" (csd0_addr), 
     "m" (suspend_iram_base)); 

到目前爲止一切正常,我可以驗證代碼執行從內部存儲器(虛擬地址空間)與JTAG調試器。

如果我沒有理解這一切,我必須做的IRAM功能如下:

  • 禁止中斷和緩存
  • 設置SDRAM控制器進入預充電省電模式
  • 執行預充電所有帶有A10高位(例如0x400)的命令和存取存儲器有效關閉所有存儲庫
  • 通過執行WFI指令使CPU進入待機狀態
  • 重新啓用所有內容fterwards(在下面的源代碼冷落)

的記者代碼如下所示:

ENTRY(cpu_v6_sdram_off) 
    @ r0: esdctl base address 
    @ r1: csd0 address with a10 high 

    cpsid if 

    @ disable I and D cache 
    mrc  p15, 0, r2, c1, c0, 0 
    bic  r2, r2, #0x00001000 @ disable I cache 
    bic  r2, r2, #0x00000004 @ disable D cache 
    mcr  p15, 0, r2, c1, c0, 0 

    @ invalidate I cache 
    mov  r2, #0 
    mcr  p15, 0, r2, c7, c5, 0 

    @ clear and invalidate D cache 
    mov  r2, #0 
    mcr  p15, 0, r2, c7, c14, 0 

    @ precharge power down mode 
    ldr  r2, [r0] 
    bic  r2, r2, #0xc00 
    orr  r2, r2, #0x400 
    str  r2, [r0] 

    @ precharge all command 
    mov  r2, #0x92 
    lsl  r2, #24 
    orr  r2, r2, #0x228000 
    orr  r2, r2, #0x0400 
    str  r2, [r0] 
    mov  r2, #0x12 
    lsl  r2, #24 
    orr  r2, r2, #0x340000 
    orr  r2, r2, #0x5600 
    orr  r2, r2, #0x78 
    str  r2, [r1] @ dummy write access 

    @ execute wait for interrupt 
    mov  r1, #0 
    mcr  p15, 0, r1, c7, c10, 4 
    mcr  p15, 0, r1, c7, c0, 4 

    cpsie if 
    bx  lr 
ENDPROC(cpu_v6_sdram_off) 

的問題是在哪裏的RAM與僞寫入訪問點。它只會導致數據中止異常,然後CPU丟失。 如果我放棄這部分,DDR2似乎不會進入低功耗模式,因爲電流消耗不會下降。

現在我完全陷入困境,並且沒有想法。有人能給我一個暗示我做錯了什麼,或者我在這裏錯過了什麼? 或者是否有任何文檔或源代碼可用於演示Linux上的i.MX35的整個過程?

回答

1

除了禁用icachedcache,需要排空任何緩衝區。我只在IMX25上實現了這個功能;它是一個ARM926(armv5)。我現在正在開發一個armv7,它似乎是一個dcache沖洗也許是適當的。也就是說,確保CPU將所有內容轉儲到SDRAM。

現在,您似乎也錯過了關閉MMU的關鍵步驟。當你運行str r2, [r1] @ dummy write access時,你會得到一個TLB未命中並嘗試訪問可能在SDRAM中的頁表。我看到一個問題;-)。幸運的是,您擁有與PC相關的彙編程序,並且可隨時隨地運行。

這是一個示例函數,用於在物理調用例程之前禁用MMU。對於ARMV5,您需要將p15值更新爲CPU的功能等效值。

static void phys_execute(void /*@[email protected]*/ (*function_pointer)(void)) 
{ 
    __asm volatile (
     " push {r4-r12,lr}     \n" /* save everything */ 
     "1: mrc  p15, 0, r15, c7, c14, 3  \n" /* armv5 specific.. */ 
     " bne  1b       \n" /* dcache clean */ 
     " mov  r8, #0      \n" 
     " mcr  p15, 0, r8, c7, c5, 0  \n" /* invalidate icache */ 
     " mcr  p15, 0, r8, c7, c10, 4  \n" /* drain wb armv5 */ 
     " mrc  p15, 0, r10, c1, c0, 0  \n" /* caches/mmu off */ 
     " bic  r8, r10, #0x5    \n" 
     " bic  r8, r8, #0x1000    \n" 
     " mcr  p15, 0, r8, c1, c0, 0  \n" 
     " blx  r0       \n" /* Call r0 */ 
     " mcr  p15, 0, r10, c1, c0, 0  \n" /* caches on..* 
     "1: mrc  p15, 0, r15, c7, c14, 3  \n" /* armv5 again */ 
     " mov  r8, #0      \n" 
     " bne  1b       \n" 
     " mcr  p15, 0, r8, c7, c5, 0  \n" 
     " mcr  p15, 0, r8, c7, c10, 4  \n" 
     " pop  {r4-r12,pc}     \n" 
     ); 
} 

r1r2將使其對通過物理RAM叫做程序。您可以重新調整此參數以硬編碼三個參數,然後使用函數指針將其放入r4。然而,你的

@ r0: esdctl base address 
@ r1: csd0 address with a10 high 

必須改變爲物理地址,這樣,當cpu_v6_sdram_off運行時,它會訪問非虛擬地址。

+0

'phys_execute'(或類似的東西)應該刷新緩存,否則它需要使用* phys == virt *執行,以便在禁用MMU時最終的'bx'返回到正確的地址。將'cpu_v6_sdram_off'中的高速緩存保持活動狀態可以將'phys_execute'的'tail'指令保留在* icache *中以避免這種情況。如果你選擇* phys == virt *,在禁用MMU之前,你需要做這個映射並跳轉到'phys_execute'的* phys *範圍;所有非常簡單:) –

+0

另外,如果您在睡夢中遇到問題,某些Freescale SOC會誤將其延遲訪問SDRAM,從而允許時鐘在喚醒後首次使用前鎖定。 –

1

感謝您的幫助!

哦,不是那麼很簡單;-) 但把所有在一起,我已經從你的答案的理解,我結束了以下 - 肯定不是很乾淨,但至少工作 - 代碼:

__asm__ __volatile__(
    "push {r4-r12, lr}\n" 
    "cpsid if\n" 
    "mov  r0, #0\n" 
    "orr  r0, r0, %0\n" 
    "mov  r2, #0\n" 
    "mcr  p15, 0, r2, c7, c14, 0\n" // clear and invalidate D cache 
    "mov  r2, #0\n" 
    "mcr  p15, 0, r2, c7, c5, 0\n" // invalidate I cache 
    "mov  r2, #0\n" 
    "mcr  p15, 0, r2, c7, c10, 4\n" // data synchronisation barrier (drain write buffer) 
    "mrc  p15, 0, r2, c1, c0, 0\n" 
    "bic  r2, r2, #0x00001000\n" // disable I cache 
    "bic  r2, r2, #0x00000004\n" // disable D cache 
    "bic  r2, r2, #0x00000001\n" // disable MMU 
    "mcr  p15, 0, r2, c1, c0, 0\n" 
    "add  r1, pc, #8\n" 
    "sub  r1, #0xc0000000\n" 
    "add  r1, #0x80000000\n" 
    "blx  r0\n" 
    "nop  \n" 
    "add  r1, pc, #28\n" 
    "sub  r1, #0x80000000\n" 
    "add  r1, #0xc0000000\n" 
    "mrc  p15, 0, r2, c1, c0, 0\n" 
    "orr  r2, r2, #0x00001000\n" // enable I cache 
    "orr  r2, r2, #0x00000004\n" // enable D cache 
    "orr  r2, r2, #0x00000001\n" // enable MMU 
    "mcr  p15, 0, r2, c1, c0, 0\n" 
    "bx  r1\n" 
    "nop  \n" 
    "cpsie if\n" 
    "pop  {r4-r12, pc}\n" 
    : : "r" (asm_func)); 

根據ARM1136技術參考手冊,在ARMv6上應該使用「數據同步屏障」代替「Drain Write Buffer」,所以我拿了這個。

當更改地址空間時,兩個nop命令會標記跳轉目標。寄存器r0包含IRAM中cpu_v6_sdram_off的物理代碼位置。

全部暫停/恢復的代碼現在看起來是這樣的:

ENTRY(cpu_v6_sdram_off) 
    @ r1: physical return address 

    @ precharge power down mode 
    ldr  r0, =MX35_ESDCTL_BASE_ADDR 
    ldr  r2, [r0] 
    bic  r2, r2, #0xc00 
    orr  r2, r2, #0x400 
    str  r2, [r0] 

    @ precharge all command 
    mov  r2, #0x92 
    lsl  r2, #24 
    orr  r2, r2, #0x228000 
    orr  r2, r2, #0x0400 
    str  r2, [r0] 

    ldr  r0, =MX35_CSD0_BASE_ADDR 
    add  r0, #0x400 
    mov  r2, #0x12 
    lsl  r2, #24 
    orr  r2, r2, #0x340000 
    orr  r2, r2, #0x5600 
    orr  r2, r2, #0x78 
    str  r2, [r0] 

    @ execute wait for interrupt 
    nop 
    mov  r2, #0 
    mcr  p15, 0, r2, c7, c10, 4 
    mcr  p15, 0, r2, c7, c0, 4 

    nop 
    nop 
    nop 
    nop 
    nop 

    @ precharge all command 
    ldr  r0, =MX35_ESDCTL_BASE_ADDR 
    mov  r2, #0x92 
    lsl  r2, #24 
    orr  r2, r2, #0x228000 
    str  r2, [r0] 

    @ set manual refresh mode 
    mov  r2, #0xa2 
    lsl  r2, #24 
    add  r2, r2, #0x220000 
    str  r2, [r0] 

    # access memory two times 
    ldr  r0, =MX35_CSD0_BASE_ADDR 
    mov  r2, #0x12 
    lsl  r2, #24 
    orr  r2, r2, #0x340000 
    orr  r2, r2, #0x5600 
    orr  r2, r2, #0x78 
    str  r2, [r0] 
    nop 
    str  r2, [r0] 

    @ enable auto-refresh 
    ldr  r0, =MX35_ESDCTL_BASE_ADDR 
    mov  r2, #0x82 
    lsl  r2, #24 
    add  r2, #0x228000 
    add  r2, #0x80 
    str  r2, [r0] 

    bx  r1 
ENDPROC(cpu_v6_sdram_off) 

如果有人也許喜歡糾正或優化該代碼,請通知我。謝謝!

+0

我只能低聲耳語,*你正在睡覺,很安靜。* –

+0

是的,董事會終於玩Sleeping Beaty :-)兩個小小的糾正:push/pop命令必須用stmfd/ldmfd替換,否則事情弄亂了。而不是爲程序計數器添加偏移量,爲跳轉目標地址使用標籤更爲安全。 –