我必须将我的设备从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功能中执行以下操作:
通讯代码如下:
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的整个过程?
答案 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"
);
}
r1
和r2
将进入通过物理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)
如果有人愿意更正或优化此代码,请随时通知我。谢谢!