summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2014-12-04 12:22:20 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:23:05 +0800
commit2b79fa63b21ab53f394b8acddd308d8090ac6949 (patch)
tree7f65484d3fbb64b1be3499673b1564a2244485cc /arch
parentd208a308feb17ae40e982ce2124ae5cfe26bec49 (diff)
MLK-11488-9 arm: imx: add A9-M4 clk shared management
As A9 and M4 share many resources on i.MX6SX, especially for clk and power related resource, so we need to handle the hardware conflict between these two cores, there are two cases that we need to consider currently: clk management: for every clk node, only when both A9 and M4 do NOT need it, then we can disable it from hardware; Here we use MU and hardware SEMA4 to achieve our goal, MU is for communiation between A9 and M4, SEMA4 is to protect the shared memory. For clk management, we use shared memory to maintain the clk status for both A9 and M4 side, and this shared memory is protected by hardware SEMA4, A9 and M4 will maintain their own clk tree info in their SW environment, and get other CORE's clk tree info from shared memory to decide whether to perform a hardware setting change when they plan to. Signed-off-by: Anson Huang <b20788@freescale.com> Also made SOC_IMX6SX select IMX_SEMA4 as part of this commit to fix build failures. Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--arch/arm/mach-imx/common.h11
-rw-r--r--arch/arm/mach-imx/gpc.c70
-rw-r--r--arch/arm/mach-imx/mu.c167
-rw-r--r--arch/arm/mach-imx/src.c14
5 files changed, 262 insertions, 1 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 673a1c4620f0..592688f713c8 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -529,6 +529,7 @@ config SOC_IMX6SX
select SOC_IMX6
select HAVE_IMX_RPMSG
select RPMSG
+ select IMX_SEMA4
help
This enables support for Freescale i.MX6 SoloX processor.
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 2e6db33eb5f2..3383432ff992 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -12,11 +12,13 @@
#define __ASM_ARCH_MXC_COMMON_H__
#include <linux/reboot.h>
+#include <soc/imx/src.h>
struct irq_data;
struct platform_device;
struct pt_regs;
struct clk;
+struct clk_hw;
struct device_node;
enum mxc_cpu_pwr_mode;
struct of_device_id;
@@ -62,6 +64,15 @@ void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw);
void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw);
void imx25_pm_init(void);
void imx27_pm_init(void);
+unsigned int imx_gpc_is_mf_mix_off(void);
+void imx6sx_set_m4_highfreq(bool high_freq);
+void imx_mu_enable_m4_irqs_in_gic(bool enable);
+void imx_gpc_add_m4_wake_up_irq(u32 irq, bool enable);
+void imx_gpc_hold_m4_in_sleep(void);
+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);
enum mxc_cpu_pwr_mode {
WAIT_CLOCKED, /* wfi only */
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 839156bad990..f3864145420c 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -27,6 +27,13 @@
#define GPC_PGC_CPU_PDNSCR 0x2a8
#define GPC_PGC_SW2ISO_SHIFT 0x8
#define GPC_PGC_SW_SHIFT 0x0
+#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
#define GPC_MAX_IRQS (IMR_NUM * 32)
@@ -38,6 +45,66 @@ static u32 gpc_mf_irqs[IMR_NUM];
static u32 gpc_mf_request_on[IMR_NUM];
static DEFINE_SPINLOCK(gpc_lock);
+void imx_gpc_add_m4_wake_up_irq(u32 hwirq, bool enable)
+{
+ unsigned int idx = hwirq / 32;
+ unsigned long flags;
+ u32 mask;
+
+ /* Sanity check for SPI irq */
+ if (hwirq < 32)
+ return;
+
+ mask = 1 << hwirq % 32;
+ spin_lock_irqsave(&gpc_lock, flags);
+ gpc_wake_irqs[idx] = enable ? gpc_wake_irqs[idx] | mask :
+ gpc_wake_irqs[idx] & ~mask;
+ spin_unlock_irqrestore(&gpc_lock, flags);
+}
+
+void imx_gpc_hold_m4_in_sleep(void)
+{
+ int val;
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+
+ /* wait M4 in wfi before asserting hold request */
+ while (!imx_gpc_is_m4_sleeping())
+ if (time_after(jiffies, timeout))
+ pr_err("M4 is NOT in expected sleep!\n");
+
+ 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);
+
+ timeout = jiffies + msecs_to_jiffies(500);
+ while (readl_relaxed(gpc_base + GPC_M4_LPSR)
+ & (GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_MASK <<
+ GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_SHIFT))
+ if (time_after(jiffies, timeout))
+ pr_err("Wait M4 hold ack timeout!\n");
+}
+
+void imx_gpc_release_m4_in_sleep(void)
+{
+ 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);
+}
+
+unsigned int imx_gpc_is_m4_sleeping(void)
+{
+ if (readl_relaxed(gpc_base + GPC_M4_LPSR) &
+ (GPC_M4_LPSR_M4_SLEEPING_MASK <<
+ GPC_M4_LPSR_M4_SLEEPING_SHIFT))
+ return 1;
+
+ return 0;
+}
+
unsigned int imx_gpc_is_mf_mix_off(void)
{
return readl_relaxed(gpc_base + GPC_PGC_MF_PDN);
@@ -111,11 +178,14 @@ void imx_gpc_post_resume(void)
static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
{
unsigned int idx = d->hwirq / 32;
+ unsigned long flags;
u32 mask;
mask = 1 << d->hwirq % 32;
+ spin_lock_irqsave(&gpc_lock, flags);
gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
gpc_wake_irqs[idx] & ~mask;
+ spin_unlock_irqrestore(&gpc_lock, flags);
/*
* Do *not* call into the parent, as the GIC doesn't have any
diff --git a/arch/arm/mach-imx/mu.c b/arch/arm/mach-imx/mu.c
index 650d27a5ec29..9c4fe025c763 100644
--- a/arch/arm/mach-imx/mu.c
+++ b/arch/arm/mach-imx/mu.c
@@ -9,6 +9,7 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#include <linux/busfreq-imx.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -31,6 +32,19 @@
#define MU_LPM_HANDSHAKE_INDEX 0
#define MU_RPMSG_HANDSHAKE_INDEX 1
+#define MU_LPM_BUS_HIGH_READY_FOR_M4 0xFFFF6666
+#define MU_LPM_M4_FREQ_CHANGE_READY 0xFFFF7777
+#define MU_LPM_M4_REQUEST_HIGH_BUS 0x2222CCCC
+#define MU_LPM_M4_RELEASE_HIGH_BUS 0x2222BBBB
+#define MU_LPM_M4_WAKEUP_SRC_VAL 0x55555000
+#define MU_LPM_M4_WAKEUP_SRC_MASK 0xFFFFF000
+#define MU_LPM_M4_WAKEUP_IRQ_MASK 0xFF0
+#define MU_LPM_M4_WAKEUP_IRQ_SHIFT 0x4
+#define MU_LPM_M4_WAKEUP_ENABLE_MASK 0xF
+#define MU_LPM_M4_WAKEUP_ENABLE_SHIFT 0x0
+
+#define MU_LPM_HANDSHAKE_INDEX 0
+#define MU_RPMSG_HANDSHAKE_INDEX 1
struct imx_mu_rpmsg_box {
const char *name;
@@ -45,6 +59,70 @@ static void __iomem *mu_base;
static u32 m4_message;
static struct delayed_work rpmsg_work;
+static u32 mu_int_en;
+static struct delayed_work mu_work, rpmsg_work;
+static u32 m4_wake_irqs[4];
+static bool m4_freq_low;
+struct irq_domain *domain;
+
+bool imx_mu_is_m4_in_low_freq(void)
+{
+ return m4_freq_low;
+}
+
+void imx_mu_enable_m4_irqs_in_gic(bool enable)
+{
+ int i, j;
+
+ for (i = 0; i < 4; i++) {
+ if (m4_wake_irqs[i] == 0)
+ continue;
+ for (j = 0; j < 32; j++) {
+ if (m4_wake_irqs[i] & (1 << j)) {
+ if (enable)
+ enable_irq(irq_find_mapping(
+ domain, i * 32 + j));
+ else
+ disable_irq(irq_find_mapping(
+ domain, i * 32 + j));
+ }
+ }
+ }
+}
+
+static irqreturn_t mcc_m4_dummy_isr(int irq, void *param)
+{
+ return IRQ_HANDLED;
+}
+
+int imx_mcc_bsp_int_disable(void)
+{
+ u32 val;
+
+ /* Disablethe bit31(GIE3) and bit19(GIR3) of MU_ACR */
+ mu_int_en = val = readl_relaxed(mu_base + MU_ACR);
+ val &= ~mu_int_en;
+ writel_relaxed(val, mu_base + MU_ACR);
+
+ /* flush */
+ val = readl_relaxed(mu_base + MU_ACR);
+ return 0;
+}
+
+int imx_mcc_bsp_int_enable(void)
+{
+ u32 val;
+
+ /* Enable bit31(GIE3) and bit19(GIR3) of MU_ACR */
+ val = readl_relaxed(mu_base + MU_ACR);
+ val |= mu_int_en;
+ writel_relaxed(val, mu_base + MU_ACR);
+
+ /* flush */
+ val = readl_relaxed(mu_base + MU_ACR);
+ return 0;
+}
+
static int imx_mu_send_message(unsigned int index, unsigned int data)
{
u32 val, ep;
@@ -99,6 +177,73 @@ static int imx_mu_send_message(unsigned int index, unsigned int data)
return 0;
}
+static void mu_work_handler(struct work_struct *work)
+{
+ int ret;
+ u32 irq, enable, idx, mask, virq;
+ struct of_phandle_args args;
+
+ pr_debug("receive M4 message 0x%x\n", m4_message);
+
+ switch (m4_message) {
+ case MU_LPM_M4_REQUEST_HIGH_BUS:
+ request_bus_freq(BUS_FREQ_HIGH);
+ imx6sx_set_m4_highfreq(true);
+ imx_mu_send_message(MU_LPM_HANDSHAKE_INDEX,
+ MU_LPM_BUS_HIGH_READY_FOR_M4);
+ m4_freq_low = false;
+ break;
+ case MU_LPM_M4_RELEASE_HIGH_BUS:
+ release_bus_freq(BUS_FREQ_HIGH);
+ imx6sx_set_m4_highfreq(false);
+ imx_mu_send_message(MU_LPM_HANDSHAKE_INDEX,
+ MU_LPM_M4_FREQ_CHANGE_READY);
+ m4_freq_low = true;
+ break;
+ default:
+ if ((m4_message & MU_LPM_M4_WAKEUP_SRC_MASK) ==
+ MU_LPM_M4_WAKEUP_SRC_VAL) {
+ irq = (m4_message & MU_LPM_M4_WAKEUP_IRQ_MASK) >>
+ MU_LPM_M4_WAKEUP_IRQ_SHIFT;
+
+ enable = (m4_message & MU_LPM_M4_WAKEUP_ENABLE_MASK) >>
+ MU_LPM_M4_WAKEUP_ENABLE_SHIFT;
+
+ /* to hwirq start from 0 */
+ irq -= 32;
+
+ idx = irq / 32;
+ mask = 1 << irq % 32;
+
+ args.np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-gpc");
+ args.args_count = 3;
+ args.args[0] = 0;
+ args.args[1] = irq;
+ args.args[2] = IRQ_TYPE_LEVEL_HIGH;
+
+ virq = irq_create_of_mapping(&args);
+
+ if (enable && can_request_irq(virq, 0)) {
+ ret = request_irq(virq, mcc_m4_dummy_isr,
+ IRQF_NO_SUSPEND, "imx-m4-dummy", NULL);
+ if (ret) {
+ pr_err("%s: register interrupt %d failed, rc %d\n",
+ __func__, virq, ret);
+ break;
+ }
+ disable_irq(virq);
+ m4_wake_irqs[idx] = m4_wake_irqs[idx] | mask;
+ }
+ imx_gpc_add_m4_wake_up_irq(irq, enable);
+ }
+ break;
+ }
+ m4_message = 0;
+ /* enable RIE3 interrupt */
+ writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(27),
+ mu_base + MU_ACR);
+}
+
int imx_mu_rpmsg_send(unsigned int rpmsg)
{
return imx_mu_send_message(MU_RPMSG_HANDSHAKE_INDEX, rpmsg);
@@ -152,6 +297,15 @@ static irqreturn_t imx_mu_isr(int irq, void *param)
schedule_delayed_work(&rpmsg_work, 0);
}
+ if (irqs & (1 << 27)) {
+ /* get message from receive buffer */
+ m4_message = readl_relaxed(mu_base + MU_ARR0_OFFSET);
+ /* disable RIE3 interrupt */
+ writel_relaxed(readl_relaxed(mu_base + MU_ACR) & (~BIT(27)),
+ mu_base + MU_ACR);
+ schedule_delayed_work(&mu_work, 0);
+ }
+
return IRQ_HANDLED;
}
@@ -167,7 +321,6 @@ static int imx_mu_probe(struct platform_device *pdev)
WARN_ON(!mu_base);
irq = platform_get_irq(pdev, 0);
-
ret = request_irq(irq, imx_mu_isr,
IRQF_EARLY_RESUME, "imx-mu", &mu_rpmsg_box);
if (ret) {
@@ -191,6 +344,18 @@ static int imx_mu_probe(struct platform_device *pdev)
return ret;
}
}
+ } else {
+ INIT_DELAYED_WORK(&mu_work, mu_work_handler);
+
+ /* enable the bit27(RIE3) of MU_ACR */
+ writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(27),
+ mu_base + MU_ACR);
+ /* enable the bit31(GIE3) of MU_ACR, used for MCC */
+ writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(31),
+ mu_base + MU_ACR);
+
+ /* MU always as a wakeup source for low power mode */
+ imx_gpc_add_m4_wake_up_irq(irq_to_desc(irq)->irq_data.hwirq, true);
}
INIT_DELAYED_WORK(&rpmsg_work, rpmsg_work_handler);
diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c
index c40110b82c63..49ffb2e52c4a 100644
--- a/arch/arm/mach-imx/src.c
+++ b/arch/arm/mach-imx/src.c
@@ -40,6 +40,7 @@
static void __iomem *src_base;
static DEFINE_SPINLOCK(src_lock);
+static bool m4_is_enabled;
static const int sw_reset_bits[5] = {
BP_SRC_SCR_SW_GPU_RST,
@@ -49,6 +50,11 @@ static const int sw_reset_bits[5] = {
BP_SRC_SCR_SW_IPU2_RST
};
+bool imx_src_is_m4_enabled(void)
+{
+ return m4_is_enabled;
+}
+
static int imx_src_reset_module(struct reset_controller_dev *rcdev,
unsigned long sw_reset_idx)
{
@@ -174,6 +180,14 @@ void __init imx_src_init(void)
*/
spin_lock(&src_lock);
val = readl_relaxed(src_base + SRC_SCR);
+
+ /* bit 4 is m4c_non_sclr_rst on i.MX6SX */
+ if (cpu_is_imx6sx() && ((val &
+ (1 << BP_SRC_SCR_SW_OPEN_VG_RST)) == 0))
+ m4_is_enabled = true;
+ else
+ m4_is_enabled = false;
+
val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE);
writel_relaxed(val, src_base + SRC_SCR);
spin_unlock(&src_lock);