i.MX35从IRAM暂停CPU和DDR2

时间:2014-02-14 12:37:51

标签: linux arm cpu ram suspend

我必须将我的设备从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控制器设置为预充电掉电模式
  • 执行precharge all命令并访问内存,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的整个过程?

2 个答案:

答案 0 :(得分:1)

除了禁用 icache dcache 之外,还需要排空任何缓冲区。我只在IMX25上实现了这个功能;它是ARM926(armv5)。我正在为armv7开发​​,看起来像 dcache flush可能是合适的。即,确保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将进入通过物理ram调用的例程。你可以重新设置硬编码三个参数,然后将函数指针重新设置为r4。但是,你的

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

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

答案 1 :(得分: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)

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