sh: Suspend in Ram on sh4
PATCH 1/1 sh: Suspend in Ram on sh4
Hi all
I would propose an other version of suspend in ram.
In ST we have several constraints:
- Several SOCs with different Clock IPs
- No cache memory mapped (it was removed in recently version of ST40)
- No IPREF instruction..
- 29/32 bits supports (where in 32 bits we are managing the PMB in a different manner respect the original kernel.org code).
For these reason an hard-coded suspend assembly code isn't easy to use for us.
To address the issue we are using a mini interpreter able to execute macro instructions SOC based. The assembly code preloads (with a jump sequence) in cache (both I$ and D$) the 'instruction-table' and after that it executes the instruction-table.
This solution allow me to have no assembly code SOCs specific and
for each SOC I have only to produce the right "tables" for standby and/or mem-standby.
Moreover a 3rd table is supported to be able to produce or save data on the fly for the standby it-self (for example if we have different action on the same SOC but different Cut).
Let me know.
Regards
Francesco
P.S.: in attach an example on how I'm using the suspend core code.
From dc7e67600f58bf04eae348c43854c1757f052f19 Mon Sep 17 00:00:00 2001
From: Francesco Virlinzi <[email protected]>
Date: Mon, 9 Mar 2009 16:05:18 +0100
Subject: [PATCH] Suspend in Ram support on Sh4
--- arch/sh/Kconfig | 1 + arch/sh/include/cpu-sh4/suspend.h | 260 ++++++++++++++++++++++++ arch/sh/kernel/cpu/sh4/Makefile | 4 + arch/sh/kernel/cpu/sh4/suspend-core.S | 350 +++++++++++++++++++++++++++++++++ arch/sh/kernel/cpu/sh4/suspend.c | 113 +++++++++++ 5 files changed, 728 insertions(+), 0 deletions(-) create mode 100644 arch/sh/include/cpu-sh4/suspend.h create mode 100644 arch/sh/kernel/cpu/sh4/suspend-core.S create mode 100644 arch/sh/kernel/cpu/sh4/suspend.c
arch/sh/Kconfig +1 -0
@@ -157,6 +157,7 @@ config CPU_SH4 bool select CPU_HAS_INTEVT select CPU_HAS_SR_RB + select ARCH_SUSPEND_POSSIBLE select CPU_HAS_PTEA if !CPU_SH4A || CPU_SHX2 select CPU_HAS_FPU if !CPU_SH4AL_DSP
arch/sh/include/cpu-sh4/suspend.h new file +260 -0
260 lines not on this page · raw message
arch/sh/kernel/cpu/sh4/Makefile +4 -0
@@ -27,3 +27,7 @@ endif clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o obj-y += $(clock-y) + +ifdef CONFIG_SUSPEND +obj-y += suspend.o suspend-core.o +endif
arch/sh/kernel/cpu/sh4/suspend-core.S new file +350 -0
350 lines not on this page · raw message
arch/sh/kernel/cpu/sh4/suspend.c new file +113 -0
113 lines not on this page · raw message
Signature · 1 line
1.5.6.6
Sorry here the example on now to use.
Regards
Francesco
Quoted 3 lines
P.S.: in attach an example on how I'm using the suspend core code.
/*
* -------------------------------------------------------------------------
* <linux_root>/arch/sh/kernel/cpu/sh4/suspend-stx7105.c
* -------------------------------------------------------------------------
* Copyright (C) 2009 STMicroelectronics
* Author: Francesco M. Virlinzi <[email protected]>
*
* May be copied or modified under the terms of the GNU General Public
* License V.2 ONLY. See linux/COPYING for more information.
*
* ------------------------------------------------------------------------- */
#include <linux/init.h>
#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/irqflags.h>
#include <linux/stm/pm.h>
#include <linux/stm/sysconf.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pm.h>
#include <asm/irq.h>
#include "./soc-stx7105.h"
#define _SYS_STA4 (7)
#define _SYS_STA4_MASK (8)
#define _SYS_STA3 (11)
#define _SYS_STA3_MASK (12)
#define _SYS_STA3_VALUE (13)
#define _SYS_CFG11 (9)
#define _SYS_CFG11_MASK (10)
#define _SYS_CFG38 (5)
#define _SYS_CFG38_MASK (6)
/* *************************
* STANDBY INSTRUCTION TABLE
* *************************
*/
static unsigned long stx7105_standby_table[] __cacheline_aligned = { /* 1. Move all the clock on OSC */ CLK_POKE(CKGA_CLKOPSRC_SWITCH_CFG(0x0), 0x0), CLK_POKE(CKGA_OSC_DIV_CFG(0x5), 29), /* ic_if_100 @ 1 MHz to be safe for Lirc*/
IMMEDIATE_DEST(0x1f),
/* reduces OSC_st40 */
CLK_STORE(CKGA_OSC_DIV_CFG(4)),
/* reduces OSC_clk_ic */
CLK_STORE(CKGA_OSC_DIV_CFG(0x0)),
/* END. */_END(),
DATA_LOAD(0x0),
CLK_STORE(CKGA_CLKOPSRC_SWITCH_CFG(0x0)),DATA_LOAD(0x1),
CLK_STORE(CKGA_OSC_DIV_CFG(0x0)),DATA_LOAD(0x2),
CLK_STORE(CKGA_OSC_DIV_CFG(5)),
/* END. */_END()
};
/* *********************
* MEM INSTRUCTION TABLE
* *********************
*/
static unsigned long stx7105_mem_table[] __cacheline_aligned = { /* 1. Enables the DDR self refresh mode */ DATA_OR_LONG(_SYS_CFG38, _SYS_CFG38_MASK),
/* waits until the ack bit is zero */ DATA_WHILE_NEQ(_SYS_STA4, _SYS_STA4_MASK, _SYS_STA4_MASK), /* 1.1 Turn-off the ClockGenD */ DATA_OR_LONG(_SYS_CFG11, _SYS_CFG11_MASK),
IMMEDIATE_DEST(0x1f),
/* reduces OSC_st40 */
CLK_STORE(CKGA_OSC_DIV_CFG(4)),
/* reduces OSC_clk_ic */
CLK_STORE(CKGA_OSC_DIV_CFG(0x0)),
/* reduces OSC_clk_ic_if_200 */
CLK_STORE(CKGA_OSC_DIV_CFG(17)),
/* 2. Move all the clock on OSC */
CLK_POKE(CKGA_OSC_DIV_CFG(5), 29), /* ic_if_100 @ 1MHz to be safe for Lirc*/
IMMEDIATE_DEST(0x0),
CLK_STORE(CKGA_CLKOPSRC_SWITCH_CFG(0x0)),
CLK_STORE(CKGA_CLKOPSRC_SWITCH_CFG(0x1)),
/* PLLs in power down */CLK_OR_LONG(CKGA_POWER_CFG, 0x3),
/* END. */_END(),
/* Turn-on the PLLs */CLK_AND_LONG(CKGA_POWER_CFG, ~3),
/* Wait PLLS lock */CLK_WHILE_NEQ(CKGA_PLL0_CFG, CKGA_PLL0_CFG_LOCK, CKGA_PLL0_CFG_LOCK), CLK_WHILE_NEQ(CKGA_PLL1_CFG, CKGA_PLL1_CFG_LOCK, CKGA_PLL1_CFG_LOCK),
/* 1. Turn-on the LMI ClocksGenD */ DATA_AND_NOT_LONG(_SYS_CFG11, _SYS_CFG11_MASK),
/* Wait LMI ClocksGenD lock */ DATA_WHILE_NEQ(_SYS_STA3, _SYS_STA3_MASK, _SYS_STA3_VALUE),
/* 2. Disables the DDR self refresh mode */ DATA_AND_NOT_LONG(_SYS_CFG38, _SYS_CFG38_MASK), /* waits until the ack bit is zero */ DATA_WHILE_EQ(_SYS_STA4, _SYS_STA4_MASK, _SYS_STA4_MASK),
IMMEDIATE_DEST(0x10000),
CLK_STORE(CKGA_PLL0LS_DIV_CFG(4)),
/* 3. Restore the previous clocks setting */DATA_LOAD(0x0),
CLK_STORE(CKGA_CLKOPSRC_SWITCH_CFG(0x0)),DATA_LOAD(0x1),
CLK_STORE(CKGA_CLKOPSRC_SWITCH_CFG(0x1)),DATA_LOAD(0x3),
CLK_STORE(CKGA_OSC_DIV_CFG(5)),DATA_LOAD(0x2),
CLK_STORE(CKGA_OSC_DIV_CFG(0x0)),DATA_LOAD(0x4),
CLK_STORE(CKGA_OSC_DIV_CFG(17)),
_DELAY(),_DELAY(),_DELAY(),_END()
};
static unsigned long stx7105_wrt_table[16] __cacheline_aligned;
static int stx7105_suspend_prepare(suspend_state_t state)
{
int ret = -EINVAL;
pm_message_t pms = {.event = PM_EVENT_SUSPEND, };
clk_pm_state(pms);
switch (state) {
case PM_SUSPEND_STANDBY:
stx7105_wrt_table[0] = /* swith config */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_CLKOPSRC_SWITCH_CFG(0));
stx7105_wrt_table[1] = /* clk_STNoc */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_OSC_DIV_CFG(0));
stx7105_wrt_table[2] = /* clk_ic_if_100 */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_OSC_DIV_CFG(5));
ret = 0;
break;
case PM_SUSPEND_MEM:
stx7105_wrt_table[0] = /* swith config */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_CLKOPSRC_SWITCH_CFG(0));
stx7105_wrt_table[1] = /* swith config 1 */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_CLKOPSRC_SWITCH_CFG(1));
stx7105_wrt_table[2] = /* clk_STNoc */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_OSC_DIV_CFG(0));
stx7105_wrt_table[3] = /* clk_ic_if_100 */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_OSC_DIV_CFG(5));
stx7105_wrt_table[4] = /* clk_ic_if_200 */
ioread32(CLOCKGENA_BASE_ADDR + CKGA_OSC_DIV_CFG(17));
ret = 0;
break;
}
return ret;
}/*
* The xxxx_finish function is called after the resume
* sysdev devices (i.e.: timer, cpufreq)
* But it isn't a big issue in our platform
*/
static int stx7105_suspend_finish(suspend_state_t state)
{
pm_message_t pms = {.event = PM_EVENT_ON, };
clk_pm_state(pms);
return 0;
}static unsigned long stx7105_iomem[2] __cacheline_aligned = {
stx7105_wrt_table,
CLOCKGENA_BASE_ADDR};static int stx7105_evt_to_irq(unsigned long evt)
{
return evt2irq(evt);
}static struct sh4_suspend_t st40data __cacheline_aligned = {
.iobase = stx7105_iomem,
.ops.finish = stx7105_suspend_finish,
.ops.prepare = stx7105_suspend_prepare,
.evt_to_irq = stx7105_evt_to_irq,
.stby_tbl = (unsigned long)stx7105_standby_table,
.stby_size = DIV_ROUND_UP(ARRAY_SIZE(stx7105_standby_table) * sizeof(long),
L1_CACHE_BYTES),
.mem_tbl = (unsigned long)stx7105_mem_table,
.mem_size = DIV_ROUND_UP(ARRAY_SIZE(stx7105_mem_table) * sizeof(long),
L1_CACHE_BYTES),
.wrt_tbl = (unsigned long)stx7105_wrt_table,
.wrt_size = DIV_ROUND_UP(ARRAY_SIZE(stx7105_wrt_table) * sizeof(long),
L1_CACHE_BYTES),
};static int __init suspend_platform_setup(void)
{
struct sysconf_field* sc; sc = sysconf_claim(SYS_CFG, 38, 20, 20, "pm"); stx7105_wrt_table[_SYS_CFG38] = (unsigned long)sysconf_address(sc); stx7105_wrt_table[_SYS_CFG38_MASK] = sysconf_mask(sc); sc = sysconf_claim(SYS_CFG, 11, 12, 12, "pm"); stx7105_wrt_table[_SYS_CFG11] = (unsigned long)sysconf_address(sc); stx7105_wrt_table[_SYS_CFG11_MASK] = sysconf_mask(sc); sc = sysconf_claim(SYS_STA, 4, 0, 0, "pm"); stx7105_wrt_table[_SYS_STA4] = (unsigned long)sysconf_address(sc); stx7105_wrt_table[_SYS_STA4_MASK] = sysconf_mask(sc); sc = sysconf_claim(SYS_STA, 3, 0, 0, "pm"); stx7105_wrt_table[_SYS_STA3] = (unsigned long)sysconf_address(sc); stx7105_wrt_table[_SYS_STA3_MASK] = sysconf_mask(sc); stx7105_wrt_table[_SYS_STA3_VALUE] = 0; return sh4_suspend_register(&st40data); }
late_initcall(suspend_platform_setup);
Hi Francesco!
Quoted 9 lines · Francesco VIRLINZI
Hi all
I would propose an other version of suspend in ram.In ST we have several constraints:
- Several SOCs with different Clock IPs
- No cache memory mapped (it was removed in recently version of ST40)
- No IPREF instruction..
- 29/32 bits supports (where in 32 bits we are managing the PMB in a different manner respect the original kernel.org code).
Just curious, how many different SoCs do you support with suspend?
Quoted 5 lines
For these reason an hard-coded suspend assembly code isn't easy to use for us.
To address the issue we are using a mini interpreter able to execute macro instructions SOC based.
Some week ago I was considering doing something similar, but then I decided not to. The reason for this was simplicity. My suspend patch only supports a few processors, so it's probably not fair to compare it with your case. So please take this with a pinch of salt. And correct me when I'm wrong.
The only code that needs special care is the self refresh code and whatever runs when the system ram is put in self refresh. What happens before and after can be written in C or assembly as usual. Having some assembly code snippet that does this is very simple. The code for suspend that I posted recently is relocatable and does exactly this. It should be possible to load that into the instruction cache too unless I'm mistaken.
I realize that the self-refresh mode switching code may vary with processor type and maybe board type as well. And having special case handling by ifdefs in the assembly snippet is not exacly clean.
Quoted 3 lines
The assembly code preloads (with a jump sequence) in cache (both I$ and D$) the 'instruction-table'
and after that it executes the instruction-table.
I tried to figure out how this cache population is working, but i still don't understand. =) Something is magical with your JUMPER(). And you need both populate both instruction and data cache, right?
Quoted 3 lines
This solution allow me to have no assembly code SOCs specific and for each SOC I have only to produce the right "tables" for standby and/or mem-standby.
I understand. This is pretty neat.
Quoted 3 lines
Moreover a 3rd table is supported to be able to produce or save data on the fly for the standby it-self (for example if we have different action on the same SOC but different Cut).
How do you save data without writing to the self-refreshing ram? Copy-back configuration with guaranteed space?
I like the idea with an interpreter. But I get the feeling that you're using the interpreter to set all sorts of register configurations - not only the ones that setup/restore self-refresh and sleep. Am I wrong?
I'm not sure what the best way forward is. Are you planning on submitting upstream support for some SoC? That would be nice so we could share the same power management code. If not then it's maybe best that I just fix up and resend my suspend patch and we can tie in some interpreter whenever it's needed.
Thanks for your help so far!
Cheers,
/ magnus
Hi again Magnus
Quoted 2 lines
...
Just curious, how many different SoCs do you support with suspend?
Currently seven... ~11-12 for the end of the year...
Quoted 6 lines
...
The only code that needs special care is the self refresh code and whatever runs when the system ram is put in self refresh. What happens before and after can be written in C or assembly as usual.
Agreed!
Quoted 7 lines
Quoted 3 lines
The assembly code preloads (with a jump sequence) in cache (both I$ and D$) the 'instruction-table'
and after that it executes the instruction-table.I tried to figure out how this cache population is working, but i still don't understand. =) Something is magical with your
JUMPER(). And you need both populate both instruction and data cache, right?
Yes you are right!
As I said in the ST40 we removed the ipref instruction and we removed also
the cache memory mapped therefore the only way to preload the code in
the ICache is executing it..
When the linux calls the 'sh4_suspend' the CPU begins a sequence of jumps "to ping" all the Icache line (but
I'm not really executing code... I'm only jumping from a label to the
next label...) until the
end of the code where it jumps on 'sh4_really_suspend' where the CPU
really executes the code (now in Icache).
Quoted 2 lines
How do you save data without writing to the self-refreshing ram?
Both the 'instruction table' and the 'writable-table' are preloaded in Dcache.
Quoted 6 lines
Copy-back configuration with guaranteed space?
I like the idea with an interpreter. But I get the feeling that you're using the interpreter to set all sorts of register configurations - not only the ones that setup/restore self-refresh and sleep. Am I wrong?
No you aren't.
You are right, I sets also some clocks but using the interpreter basically you can do what you want for example
in a SOC the last thing I do to enter in standby isn't a sleep... it's
a write in the ClockIP to turn-off the sh4_clk...
the wakeup event routed to the ClockIP will ("automatically") turn-on
again the sh4_clk.... therefore it's a kind
of 'hard' sleep instruction.... (without sleep instruction)
Also in this case the code to manage the clock has to be in icache.
Quoted 3 lines
I'm not sure what the best way forward is. Are you planning on submitting upstream support for some SoC?
Not so easy submit the per SOC code also because we use an older kernel than the original kernel.org (I'm on 2.6.23).
Quoted 2 lines
That would be nice so we
could share the same power management code.
I'm already sharing this code for that reason.
Ciao
Francesco
Quoted 5 lines · Francesco VIRLINZI
Quoted 2 lines
I'm not sure what the best way forward is. Are you planning on submitting upstream support for some SoC?
Not so easy submit the per SOC code also because we use an older kernel than the original kernel.org (I'm on 2.6.23).
I see. The joys of old kernels. =)
Quoted 4 lines
Quoted 2 lines
That would be nice so we
could share the same power management code.I'm already sharing this code for that reason.
I appreciate your effort. You may have noted that we cherry pick certain parts of the code that you post. Accepting everything would of course be better, but there is some hesitation since we don't have the full picture of your code base.
In my opinion, the best way to add new interfaces or change existing ones is to also submit code that is using your interfaces. WIthout such code it's difficult to understand the reason behind the change.
I wonder if it would be possible to take a single cpu and a single board from your out of tree code base, and add support for that system upstream. Then we can tie in whatever interfaces to that code. This may take quite a bit of time though, but it's probably worth doing in the long term.
Also, please try to submit your patches inline if possible. It makes review much easier. I use sendpatchset for this purpose.
Thanks for your help so far!
/ magnus