summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2014-12-04 12:24:49 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:20:42 +0300
commit99f10f2627a7e95d22136c22eb43dda753c39046 (patch)
treedc8a3ab859ac6ba6d4cb81ed2df7e5e0f28ea3a2 /arch
parent611a82acc3df0044d3f90dbe8c127c6d9cd8bc3c (diff)
MLK-11488-10 arm: imx: add A9-M4 power management
this patch adds A9-M4 power management, including below features: 1. busfreq: M4 is registered as a high speed device of A9, when M4 is running at high speed, busfreq will NOT enter low bus mode, when M4 is entering its low power idle, A9 will be able to enter low bus mode according to its state machine; 2. low power idle: only when M4 is in its low power idle, busfreq is staying at low bus mode, low power idle is available for kernel; 3. suspend: when M4 is NOT in its low power idle, when linux is about to suspend, it will only force SOC enter WAIT mode, only when M4 is in its low power idle in TCM, linux suspend can enter DSM mode. M4 can request/release wakeup source via MU to A9. as M4 can NOT switch its clk parent due to glitch MUX, to handle this case, A9 will help switch M4's clk parent, the flow is as below: M4: 1. enter low power idle, send bus use count-- to A9; 2. enter wfi and only wait for MU interrupt; 3. receive A9's clk switch ready message, go into low power idle; 4. receive interrupt to exit low power idle, send request to A9 for increase busfreq and M4 freq, enter wfi and only wait for MU interrupt; 5. receive A9 ready message, go out of low power idle. A9: 1. when receive M4's message of entering low power idle, wait M4 into wfi, hold M4 in wfi by hardware, gate M4 clk, then switch M4's clk to OSC, ungate M4 clk, send ready command to wake up M4 into low power idle; 2. when receive M4's message of exiting low power idle, wait M4 into wfi, hold M4 in wfi by hardware, gate M4 clk, then switch M4's clk to origin high clk, ungate M4 clk, send ready command to wake up M4 to exit low power idle; Signed-off-by: Anson Huang <b20788@freescale.com> Conflicts: arch/arm/mach-imx/busfreq-imx6.c arch/arm/mach-imx/pm-imx6.c
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-imx/busfreq-imx.c18
-rw-r--r--arch/arm/mach-imx/common.h1
-rw-r--r--arch/arm/mach-imx/pm-imx6.c23
3 files changed, 42 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/busfreq-imx.c b/arch/arm/mach-imx/busfreq-imx.c
index 19246693f29e..d84e707e2228 100644
--- a/arch/arm/mach-imx/busfreq-imx.c
+++ b/arch/arm/mach-imx/busfreq-imx.c
@@ -107,6 +107,16 @@ static struct delayed_work bus_freq_daemon;
static RAW_NOTIFIER_HEAD(busfreq_notifier_chain);
+static bool check_m4_sleep(void)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+
+ while (imx_gpc_is_m4_sleeping() == 0)
+ if (time_after(jiffies, timeout))
+ return false;
+ return true;
+}
+
static int busfreq_notify(enum busfreq_event event)
{
int ret;
@@ -134,6 +144,10 @@ EXPORT_SYMBOL(unregister_busfreq_notifier);
*/
static void enter_lpm_imx6_up(void)
{
+ if (cpu_is_imx6sx() && imx_src_is_m4_enabled())
+ if (!check_m4_sleep())
+ pr_err("M4 is NOT in sleep!!!\n");
+
/* set periph_clk2 to source from OSC for periph */
clk_set_parent(periph_clk2_sel_clk, osc_clk);
clk_set_parent(periph_clk, periph_clk2_clk);
@@ -831,6 +845,10 @@ static int busfreq_probe(struct platform_device *pdev)
err = init_mmdc_ddr3_settings_imx6_up(pdev);
else if (ddr_type == IMX_DDR_TYPE_LPDDR2)
err = init_mmdc_lpddr2_settings(pdev);
+ /* if M4 is enabled and rate > 24MHz, add high bus count */
+ if (imx_src_is_m4_enabled() &&
+ (clk_get_rate(m4_clk) > LPAPM_CLK))
+ high_bus_count++;
}
if (err) {
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 23faad6a7bfb..d510cbf04cf9 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -74,6 +74,7 @@ void imx_gpc_release_m4_in_sleep(void);
void mcc_receive_from_mu_buffer(unsigned int index, unsigned int *data);
void mcc_send_via_mu_buffer(unsigned int index, unsigned int data);
unsigned int imx_gpc_is_m4_sleeping(void);
+bool imx_mu_is_m4_in_low_freq(void);
enum mxc_cpu_pwr_mode {
WAIT_CLOCKED, /* wfi only */
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index 8f4c57c306ed..07395d66b8b0 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -751,6 +751,24 @@ static int imx6q_pm_enter(suspend_state_t state)
unsigned int console_saved_reg[11] = {0};
static unsigned int ccm_ccgr4, ccm_ccgr6;
+ if (imx_src_is_m4_enabled()) {
+ if (imx_gpc_is_m4_sleeping() && imx_mu_is_m4_in_low_freq()) {
+ imx_gpc_hold_m4_in_sleep();
+ imx_mu_enable_m4_irqs_in_gic(true);
+ } else {
+ pr_info("M4 is busy, enter WAIT mode instead of STOP!\n");
+ imx6_set_lpm(WAIT_UNCLOCKED);
+ imx6_set_int_mem_clk_lpm(true);
+ imx_gpc_pre_suspend(false);
+ /* Zzz ... */
+ cpu_do_idle();
+ imx_gpc_post_resume();
+ imx6_set_lpm(WAIT_CLOCKED);
+
+ return 0;
+ }
+ }
+
switch (state) {
case PM_SUSPEND_STANDBY:
imx6_set_lpm(STOP_POWER_ON);
@@ -828,6 +846,11 @@ static int imx6q_pm_enter(suspend_state_t state)
return -EINVAL;
}
+ if (imx_src_is_m4_enabled()) {
+ imx_mu_enable_m4_irqs_in_gic(false);
+ imx_gpc_release_m4_in_sleep();
+ }
+
return 0;
}