MLK-11488-10 arm: imx: add A9-M4 power management
authorAnson Huang <b20788@freescale.com>
Thu, 4 Dec 2014 04:24:49 +0000 (12:24 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:24 +0000 (14:48 -0500)
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

arch/arm/mach-imx/busfreq-imx.c
arch/arm/mach-imx/common.h
arch/arm/mach-imx/pm-imx6.c

index 1924669..d84e707 100644 (file)
@@ -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) {
index 23faad6..d510cbf 100644 (file)
@@ -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 */
index 8f4c57c..07395d6 100644 (file)
@@ -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;
 }