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
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;
*/
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);
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) {
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 */
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);
return -EINVAL;
}
+ if (imx_src_is_m4_enabled()) {
+ imx_mu_enable_m4_irqs_in_gic(false);
+ imx_gpc_release_m4_in_sleep();
+ }
+
return 0;
}