summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2014-10-10 19:42:48 +0800
committerAnson Huang <b20788@freescale.com>2014-10-20 14:34:13 +0800
commitd813058e05d0b5fc28ad5b6b0301d2c621f5db6a (patch)
tree95a91cce65f5d8bc1adadda37f5eed4be531814f
parent60a0b96783042dc8da41eab8ec3d4a6eb0d55986 (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.c37
-rw-r--r--arch/arm/mach-imx/common.h2
-rw-r--r--arch/arm/mach-imx/gpc.c29
-rw-r--r--arch/arm/mach-imx/mcc_linux.c13
-rw-r--r--arch/arm/mach-imx/pm-imx6.c13
-rw-r--r--include/linux/mcc_imx6sx.h9
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 */