i.MX35 从 IRAM 挂起 CPU 和 DDR2 [英] i.MX35 suspend CPU and DDR2 from IRAM
问题描述
我必须让我的设备从 Linux 2.6.38 进入非常深的低功耗模式,因此,有必要暂停所有组件,包括 CPU 和 DDR2.
I have to put my device into a very deep low power mode from Linux 2.6.38 and therefore, it's necessary to suspend all components, including CPU und DDR2.
到目前为止我发现我必须将核心汇编器函数复制到处理器的内部存储器中并从那里执行它.基本上,它看起来像这样:
What I found out so far is that I have to copy the core assembler function into the processor's internal memory and execute it from there. Basically, it looks like this:
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 调试器从内部存储器(在虚拟地址空间中)验证代码执行情况.
So far everything works as expected, I can verify code execution from internal memory (in virtual address space) with the JTAG debugger.
如果我理解正确,我必须在 IRAM 函数中执行以下操作:
If I understand it all correctly, I have to do the following in the IRAM function:
- 禁用中断和缓存
- 将 SDRAM 控制器设置为预充电断电模式
- 执行预充电所有命令并以 A10 高电平(例如 0x400)访问内存以有效关闭所有 bank
- 通过执行 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 似乎不会进入低功耗模式,因为电流消耗不会下降.
The problem is at the point where the RAM is accessed with a dummy write. It simply results in a data abort exception and then the CPU gets lost. If I leave this part out, the DDR2 doesn't seem to be put into low power mode, because the current consumption doesn't go down.
现在我完全陷入困境并且没有想法.有人可以给我一个提示,我做错了什么或者我在这里遗漏了什么?或者是否有任何文档或源代码可用于演示 Linux 上 i.MX35 的整个过程?
Now I'm totally stuck and out of ideas here. Could someone please give me a hint what I'm doing wrong or what I'm missing here? Or is there any documentation or source code available demonstrating the whole procedure for the i.MX35 on Linux?
推荐答案
除了禁用 icache 和 dcache,还需要清空所有缓冲区.我只在 IMX25 上实现了这个;它是一个 ARM926 (armv5).我现在正在为 armv7 开发,看起来 dcache 刷新可能合适.即,确保 CPU 将所有内容转储到 SDRAM.
As well as disabling the icache and dcache, it is needed to drain any buffers. I have only implemented this on an IMX25; It is an ARM926 (armv5). I am now developing for an armv7 and it seems like a dcache flush maybe appropriate. Ie, ensure that the CPU dumps everything to SDRAM.
现在,您似乎也错过了关闭 MMU 的关键步骤.当您运行 str r2, [r1] @ dummy write access
时,您将遇到 TLB 未命中并尝试访问可能位于 SDRAM 中的页表.我看到一个问题;-).幸运的是,您拥有与 PC 相关的汇编程序,并且可以随时随地运行.
Now, it also seems you missed a key step of turning off the MMU. When you run str r2, [r1] @ dummy write access
, you will get a TLB miss and try to access the page tables, which are probably in SDRAM. I see a problem ;-). Luckily you have assembler which is PC relative and will run anywhere, anytime.
这是在物理调用例程之前禁用 MMU 的示例函数.它适用于 ARMV5,您需要将 p15
值更新为 CPU 的等效功能.
Here is a sample function to disable the MMU before calling the routine physically. It is for the ARMV5, you need to update the p15
values to the functional equivalents for your 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
将使其进入通过物理内存调用的例程.您可以重新编码它以硬编码三个参数,然后将函数指针放入r4
.但是,您的
r1
and r2
will make it to the routine called via physical ram. You can re-jig this to hard code three parameters and then the function pointer to put it in r4
. However, your
@ r0: esdctl base address
@ r1: csd0 address with a10 high
必须更改为物理地址,以便cpu_v6_sdram_off
运行时,将访问非虚拟地址.
must change to be physical addresses so that when cpu_v6_sdram_off
runs, it will be accessing the non-virtual addresses.
这篇关于i.MX35 从 IRAM 挂起 CPU 和 DDR2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!