Linux i、 MX35从IRAM暂停CPU和DDR2

Linux i、 MX35从IRAM暂停CPU和DDR2,linux,arm,cpu,ram,suspend,Linux,Arm,Cpu,Ram,Suspend,我必须从Linux2.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); m

我必须从Linux2.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));
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)
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)
到目前为止,一切正常,我可以使用JTAG调试器从内部内存(在虚拟地址空间中)验证代码执行

如果我理解正确,我必须在IRAM功能中执行以下操作:

  • 禁用中断和缓存
  • 将SDRAM控制器设置为预充电断电模式
  • 执行“全部预充电”命令,并以10高(例如0x400)访问内存,以有效关闭所有气缸组
  • 通过执行WFI指令将CPU置于备用状态
  • 之后重新启用所有内容(在下面的源代码中遗漏)
相应的代码如下所示:

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));
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)
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)
问题出现在使用伪写访问RAM的点上。它只会导致数据中止异常,然后CPU就会丢失。 如果我不考虑这一部分,DDR2似乎不会进入低功耗模式,因为电流消耗不会下降

现在我完全被困在这里,没有想法了。谁能给我一个提示,我做错了什么,或者我遗漏了什么? 或者是否有任何文档或源代码可用于演示Linux上i.MX35的整个过程?

以及禁用icache和dcache,这需要耗尽所有缓冲区。我只在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"
        );
}
r1
r2
将通过物理ram调用例程。您可以将其重新调整为硬编码三个参数,然后使用函数指针将其放入
r4
。然而,你的

 @ r0: esdctl base address
 @ r1: csd0 address with a10 high
必须更改为物理地址,以便在运行
cpu\u v6\u sdram\u off
时,它将访问非虚拟地址。

感谢您的帮助

嗯,不是很简单;-) 但是把我从你的答案中理解的所有内容放在一起,我最终得到了以下代码——当然不是很清晰,但至少是有效的代码:

__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包含IRAM中cpu_v6_sdram_off的物理代码位置

整个挂起/恢复代码现在如下所示:

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));
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)
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)

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

phys\u execute
(或类似的东西)应该刷新缓存,否则它需要使用phys==virt执行,以便在禁用MMU时,最终的“bx”返回到正确的地址。将
cpu\u v6\u sdram\u关闭时的缓存保持活动状态
可以将
phys\u execute
的“tail”指令保留在icache中以避免出现这种情况。如果选择phys==virt,则在禁用MMU之前,需要进行此映射并跳转到phys范围
phys\u execute
;所有这些都很简单:)另外,如果你从睡眠中醒来时出现问题,一些飞思卡尔SOC会延迟访问SDRAM,允许时钟在醒来后第一次使用前锁定。我比耳语要好,你感觉到睡眠,非常睡眠。是的,董事会终于播放了《熟睡的贝蒂》:-)我这边的两个小更正:必须用stmfd/ldmfd替换push/pop命令,否则事情会变得一团糟。与向程序计数器添加偏移量不同,为跳转目标地址使用标签更安全。