【问题标题】:i.MX35 suspend CPU and DDR2 from IRAMi.MX35 从 IRAM 暂停 CPU 和 DDR2
【发布时间】:2014-02-14 12:37:51
【问题描述】:

我必须从 Linux 2.6.38 将我的设备置于非常低功耗模式,因此,有必要暂停所有组件,包括 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 进入待机状态
  • 之后重新启用所有内容(在下面的源代码中省略)

对应的代码如下:

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 的整个过程?

【问题讨论】:

    标签: linux arm cpu ram suspend


    【解决方案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 /*@unused@*/ (*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 将进入通过物理内存调用的例程。您可以重新设置它以硬编码三个参数,然后将函数指针放入r4。但是,您的

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

    必须更改为物理地址,以便cpu_v6_sdram_off 运行时,它将访问非虚拟地址。

    【讨论】:

    • phys_execute(或类似的东西)应该刷新缓存,否则它需要使用 phys==virt 执行,以便最终的 'bx' 回到禁用 MMU 时的正确地址。让cpu_v6_sdram_off 中的缓存处于活动状态可以在 icache 中保留phys_execute 的“尾”指令以避免这种情况。如果选择phys==virt,则需要在禁用MMU之前进行此映射并跳转到phys_executephys范围;一切都很简单:)
    • 另外,如果您在从睡眠中醒来时遇到问题,一些飞思卡尔 SOC 会延迟对 SDRAM 的访问,从而允许时钟在唤醒后首次使用之前锁定。
    【解决方案2】:

    感谢您的帮助!

    好吧,不是那么简单;-) 但是将我从您的回答中了解到的所有内容放在一起,我最终得到了以下代码 - 当然不是很干净,但至少可以工作 - 代码:

    __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上应该使用“数据同步屏障”而不是“漏写缓冲区”,所以我拿了这个。

    这两个 nop 命令标记了更改地址空间时的跳转目标。寄存器r0包含cpu_v6_sdram_off在IRAM中的物理代码位置。

    整个暂停/恢复代码现在如下所示:

    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)
    

    如果有人想更正或优化此代码,请随时通知我。谢谢!

    【讨论】:

    • 我不是在窃窃私语,你感觉睡着了,睡着了。
    • 是的,董事会终于播放了 Sleeping Beaty :-) 我这边的两个小更正:push/pop 命令必须替换为 stmfd/ldmfd,否则事情会变得一团糟。而不是向程序计数器添加偏移量,使用标签作为跳转目标地址更安全。
    猜你喜欢
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多