diff options
author | Anson Huang <b20788@freescale.com> | 2014-10-10 19:42:48 +0800 |
---|---|---|
committer | Anson Huang <b20788@freescale.com> | 2014-10-20 14:34:13 +0800 |
commit | d813058e05d0b5fc28ad5b6b0301d2c621f5db6a (patch) | |
tree | 95a91cce65f5d8bc1adadda37f5eed4be531814f | |
parent | 60a0b96783042dc8da41eab8ec3d4a6eb0d55986 (diff) |
MLK-9674-2 arm: imx: improve A9-M4 low power protocol
1. improve busfreq enter/exit protocol, M4 will request
high/low busfreq to A9, M4 will be in TCM when it
release high busfreq, A9 only need to responce when
M4 request high busfreq, no need handshake;
2. only when M4 release high busfreq, A9 is allow to do
linux kernel suspend.
Signed-off-by: Anson Huang <b20788@freescale.com>
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 37 | ||||
-rw-r--r-- | arch/arm/mach-imx/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 29 | ||||
-rw-r--r-- | arch/arm/mach-imx/mcc_linux.c | 13 | ||||
-rw-r--r-- | arch/arm/mach-imx/pm-imx6.c | 13 | ||||
-rw-r--r-- | include/linux/mcc_imx6sx.h | 9 |
6 files changed, 53 insertions, 50 deletions
diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c index b889c3907faf..9a02659ce6f3 100644 --- a/arch/arm/mach-imx/busfreq-imx6.c +++ b/arch/arm/mach-imx/busfreq-imx6.c @@ -82,8 +82,6 @@ static unsigned int ddr_low_rate; extern unsigned long iram_tlb_phys_addr; extern int unsigned long iram_tlb_base_addr; -extern struct completion wait_m4_done; - extern int init_mmdc_lpddr2_settings(struct platform_device *dev); extern int init_mmdc_ddr3_settings_imx6q(struct platform_device *dev); extern int init_mmdc_ddr3_settings_imx6sx(struct platform_device *dev); @@ -122,34 +120,21 @@ static u32 pll2_org_rate; static struct delayed_work low_bus_freq_handler; static struct delayed_work bus_freq_daemon; -static int imx_lpm_handshake(u32 data) +static bool check_m4_sleep(void) { - unsigned long timeout; - - init_completion(&wait_m4_done); - - mcc_send_via_mu_buffer(MU_LPM_HANDSHAKE_INDEX, data); - - if (!wait_for_completion_timeout(&wait_m4_done, - msecs_to_jiffies(3000))) { - pr_err("lpm handshake(0x%x) with M4 timeout!!!\n", - data); - /* M4 need to make sure in wfi for busfreq change */ - dump_stack(); - } + unsigned long timeout = jiffies + msecs_to_jiffies(500); - timeout = jiffies + msecs_to_jiffies(500); while (imx_gpc_is_m4_sleeping() == 0) if (time_after(jiffies, timeout)) - pr_err("M4 is NOT sleeping!!!\n"); - - return 0; + return false; + return true; } static void enter_lpm_imx6sx(void) { if (imx_src_is_m4_enabled()) - imx_lpm_handshake(MU_LPM_HANDSHAKE_ENTER); + if (!check_m4_sleep()) + pr_err("M4 is NOT in sleep!!!\n"); /* set periph_clk2 to source from OSC for periph */ imx_clk_set_parent(periph_clk2_sel, osc_clk); @@ -202,9 +187,6 @@ static void enter_lpm_imx6sx(void) static void exit_lpm_imx6sx(void) { - if (imx_src_is_m4_enabled()) - imx_lpm_handshake(MU_LPM_HANDSHAKE_ENTER); - clk_prepare_enable(pll2_400); /* @@ -425,10 +407,6 @@ static void reduce_bus_freq(void) med_bus_freq_mode = 0; high_bus_freq_mode = 0; - /* wake up M4 */ - if (cpu_is_imx6sx() && imx_src_is_m4_enabled()) - imx_lpm_handshake(MU_LPM_HANDSHAKE_EXIT); - if (audio_bus_freq_mode) dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count:\ high %d, med %d, audio %d\n", @@ -544,9 +522,6 @@ static int set_high_bus_freq(int high_bus_freq) audio_bus_freq_mode = 0; clk_disable_unprepare(pll3); - /* wake up M4 */ - if (cpu_is_imx6sx() && imx_src_is_m4_enabled()) - imx_lpm_handshake(MU_LPM_HANDSHAKE_EXIT); if (high_bus_freq_mode) dev_dbg(busfreq_dev, "Bus freq set to high mode. Count:\ diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 0c62dad804ba..d46194bd93a0 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -164,6 +164,8 @@ extern void imx6sl_set_wait_clk(bool enter); extern void imx6_enet_mac_init(const char *compatible); extern int imx_mmdc_get_ddr_type(void); extern unsigned int imx_gpc_is_m4_sleeping(void); +extern void imx_gpc_hold_m4_in_sleep(void); +extern void imx_gpc_release_m4_in_sleep(void); extern void imx_cpu_die(unsigned int cpu); extern int imx_cpu_kill(unsigned int cpu); diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index dbf47ccf3b4b..ff8074b329a3 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c @@ -56,6 +56,10 @@ #define GPC_M4_LPSR 0x2c #define GPC_M4_LPSR_M4_SLEEPING_SHIFT 4 #define GPC_M4_LPSR_M4_SLEEPING_MASK 0x1 +#define GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK 0x1 +#define GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT 0 +#define GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_MASK 0x1 +#define GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_SHIFT 1 #define IMR_NUM 4 @@ -209,6 +213,31 @@ void imx_gpc_pre_suspend(bool arm_power_off) } } +void imx_gpc_hold_m4_in_sleep() +{ + int val; + + val = readl_relaxed(gpc_base + GPC_M4_LPSR); + val &= ~(GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK << + GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT); + writel_relaxed(val, gpc_base + GPC_M4_LPSR); + + while (readl_relaxed(gpc_base + GPC_M4_LPSR) + & (GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_MASK << + GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_SHIFT)) + ; +} + +void imx_gpc_release_m4_in_sleep() +{ + int val; + + val = readl_relaxed(gpc_base + GPC_M4_LPSR); + val |= GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK << + GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT; + writel_relaxed(val, gpc_base + GPC_M4_LPSR); +} + void imx_gpc_post_resume(void) { void __iomem *reg_imr1 = gpc_base + GPC_IMR1; diff --git a/arch/arm/mach-imx/mcc_linux.c b/arch/arm/mach-imx/mcc_linux.c index e4acc58ab1c3..39c9e19cbbb3 100644 --- a/arch/arm/mach-imx/mcc_linux.c +++ b/arch/arm/mach-imx/mcc_linux.c @@ -39,8 +39,6 @@ static unsigned int cpu_to_cpu_isr_vector = MCC_VECTOR_NUMBER_INVALID; static struct delayed_work mu_work; unsigned int m4_message; -DECLARE_COMPLETION(wait_m4_done); - unsigned int imx_mcc_buffer_freed = 0, imx_mcc_buffer_queued = 0; DECLARE_WAIT_QUEUE_HEAD(buffer_freed_wait_queue); /* Used for blocking send */ DECLARE_WAIT_QUEUE_HEAD(buffer_queued_wait_queue); /* Used for blocking recv */ @@ -117,14 +115,6 @@ static irqreturn_t mcc_cpu_to_cpu_isr(int irq, void *param) if (irqs & (1 << 27)) { m4_message = mcc_handle_mu_receive_irq(); schedule_delayed_work(&mu_work, 0); - /* - * MU delay work may sleep to wait for completion, so - * we need to set completion here if the message is what - * we expected, can NOT do it in delay work. - */ - if (m4_message == (~MU_LPM_HANDSHAKE_ENTER) || - m4_message == (~MU_LPM_HANDSHAKE_EXIT)) - complete(&wait_m4_done); } return IRQ_HANDLED; @@ -216,10 +206,9 @@ static void mu_work_handler(struct work_struct *work) switch (m4_message) { case MU_LPM_M4_REQUEST_HIGH_BUS: - case MU_LPM_M4_IN_HIGHFREQ: request_bus_freq(BUS_FREQ_HIGH); mcc_send_via_mu_buffer(MU_LPM_HANDSHAKE_INDEX, - MU_LPM_A9_READY_FOR_M4); + MU_LPM_BUS_HIGH_READY_FOR_M4); break; case MU_LPM_M4_RELEASE_HIGH_BUS: release_bus_freq(BUS_FREQ_HIGH); diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c index 7921b85f1b4d..d6476e4993df 100644 --- a/arch/arm/mach-imx/pm-imx6.c +++ b/arch/arm/mach-imx/pm-imx6.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/mcc_imx6sx.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -354,6 +355,15 @@ static int imx6_pm_enter(suspend_state_t state) struct regmap *g; unsigned int console_saved_reg[11] = {0}; + if (imx_src_is_m4_enabled()) { + if (imx_gpc_is_m4_sleeping()) { + imx_gpc_hold_m4_in_sleep(); + } else { + pr_info("M4 is busy, can NOT suspend!\n"); + return 0; + } + } + if (!iram_tlb_base_addr) { pr_warn("No IRAM/OCRAM memory allocated for suspend/resume code. \ Please ensure device tree has an entry for fsl,lpm-sram.\n"); @@ -440,6 +450,9 @@ static int imx6_pm_enter(suspend_state_t state) !IMX6Q_GPR1_PCIE_TEST_PD); } + if (imx_src_is_m4_enabled()) + imx_gpc_release_m4_in_sleep(); + return 0; } diff --git a/include/linux/mcc_imx6sx.h b/include/linux/mcc_imx6sx.h index b6e60b5d3271..51824f79e3c3 100644 --- a/include/linux/mcc_imx6sx.h +++ b/include/linux/mcc_imx6sx.h @@ -26,14 +26,9 @@ #define MU_ARR0_OFFSET 0x10 #define MU_LPM_HANDSHAKE_INDEX 0 -#define MU_LPM_HANDSHAKE_ENTER 0xAAAA1111 -#define MU_LPM_HANDSHAKE_EXIT 0xBBBB2222 -#define MU_LPM_M4_REQUEST_HIGH_BUS 0x1111AAAA -#define MU_LPM_A9_READY_FOR_M4 0xFFFF6666 +#define MU_LPM_BUS_HIGH_READY_FOR_M4 0xFFFF6666 +#define MU_LPM_M4_REQUEST_HIGH_BUS 0x2222CCCC #define MU_LPM_M4_RELEASE_HIGH_BUS 0x2222BBBB -#define MU_LPM_GET_M4_FREQ 0xCCCC3333 -#define MU_LPM_M4_IN_HIGHFREQ 0x33330000 -#define MU_LPM_M4_IN_LOWFREQ 0x33330001 enum { /* FIXME */ |