extern int epdc_enabled;
extern void mx6_cpu_regulator_init(void);
static int max17135_regulator_init(struct max17135 *max17135);
-extern volatile int num_cpu_idle_lock;
enum sd_pad_mode {
SD_PAD_MODE_LOW_SPEED,
spdif_pads_cnt = ARRAY_SIZE(mx6dl_arm2_spdif_pads);
flexcan_pads_cnt = ARRAY_SIZE(mx6dl_arm2_can_pads);
i2c3_pads_cnt = ARRAY_SIZE(mx6dl_arm2_i2c3_pads);
- num_cpu_idle_lock = 0xffff0000;
}
BUG_ON(!common_pads);
static int uart3_en;
static int tuner_en;
static int spinor_en;
-extern volatile int num_cpu_idle_lock;
static int __init spinor_enable(char *p)
{
i2c3_pads = mx6dl_i2c3_pads_rev_b;
i2c3_pads_cnt = ARRAY_SIZE(mx6dl_i2c3_pads_rev_b);
}
- num_cpu_idle_lock = 0xffff0000;
}
BUG_ON(!common_pads);
extern char *gp_reg_id;
extern int epdc_enabled;
-extern volatile int num_cpu_idle_lock;
static int max17135_regulator_init(struct max17135 *max17135);
else if (cpu_is_mx6dl()) {
mxc_iomux_v3_setup_multiple_pads(mx6dl_sabresd_pads,
ARRAY_SIZE(mx6dl_sabresd_pads));
- num_cpu_idle_lock = 0xffff0000;
}
#ifdef CONFIG_FEC_1588
extern struct cpu_op *(*get_cpu_op)(int *op);
extern int lp_high_freq;
extern int lp_med_freq;
+extern int wait_mode_arm_podf;
extern int lp_audio_freq;
+extern int cur_arm_podf;
+extern bool arm_mem_clked_in_wait;
+extern bool enable_wait_mode;
void __iomem *apll_base;
static struct clk ipu1_clk;
static struct clk openvg_axi_clk;
static struct clk enfc_clk;
static struct clk usdhc3_clk;
+static struct clk ipg_clk;
static struct cpu_op *cpu_op_tbl;
static int cpu_op_nr;
static void _clk_pll1_disable(struct clk *clk)
{
+ void __iomem *pllbase;
+ u32 reg;
+
pll1_enabled = false;
- _clk_pll_disable(clk);
+
+ /* Set PLL1 in bypass mode only. */
+ /* We need to be able to set the ARM-PODF bit
+ * when the system enters WAIT mode. And setting
+ * this bit requires PLL1_main to be enabled.
+ */
+ pllbase = _get_pll_base(clk);
+
+ reg = __raw_readl(pllbase);
+ reg |= ANADIG_PLL_BYPASS;
+ __raw_writel(reg, pllbase);
}
static int _clk_pll1_enable(struct clk *clk)
u32 div;
unsigned long parent_rate;
unsigned long flags;
+ unsigned long ipg_clk_rate, max_arm_wait_clk;
for (i = 0; i < cpu_op_nr; i++) {
if (rate == cpu_op_tbl[i].cpu_rate)
}
parent_rate = clk_get_rate(clk->parent);
div = parent_rate / rate;
+ /* Calculate the ARM_PODF to be applied when the system
+ * enters WAIT state. The max ARM clk is decided by the
+ * ipg_clk and has to follow the ratio of ARM_CLK:IPG_CLK of 12:5.
+ * For ex, when IPG is at 66MHz, ARM_CLK cannot be greater
+ * than 158MHz.
+ * Pre-calculate the optimal divider now.
+ */
+ ipg_clk_rate = clk_get_rate(&ipg_clk);
+ max_arm_wait_clk = (12 * ipg_clk_rate) / 5;
+ wait_mode_arm_podf = parent_rate / max_arm_wait_clk;
+ if (wait_mode_arm_podf > 7) {
+ /* IPG_CLK is too low and we cannot get a ARM_CLK
+ * that will satisfy the 12:5 ratio.
+ * Use the mem_ipg_stop_mask bit to ensure clocks to ARM
+ * memories are not gated during WAIT mode.
+ * This bit is NOT available on MX6DQ TO1.1/TO1.0 and
+ * MX6DL TO1.0.
+ * Else disable entry to WAIT mode.
+ */
+ if ((mx6q_revision() > IMX_CHIP_REVISION_1_1) ||
+ (mx6dl_revision() > IMX_CHIP_REVISION_1_0))
+ arm_mem_clked_in_wait = true;
+ else
+ enable_wait_mode = false;
+ }
+
if (div == 0)
div = 1;
spin_unlock_irqrestore(&clk_lock, flags);
return -1;
}
-
/* Need PLL1-MAIN to be ON to write to ARM-PODF bit. */
if (!pll1_enabled)
pll1_sys_main_clk.enable(&pll1_sys_main_clk);
+ cur_arm_podf = div;
+
__raw_writel(div - 1, MXC_CCM_CACRR);
while (__raw_readl(MXC_CCM_CDHIPR))
extern struct cpu_op *(*get_cpu_op)(int *op);
extern int lp_high_freq;
extern int lp_med_freq;
+extern int wait_mode_arm_podf;
extern int mx6q_revision(void);
+extern bool arm_mem_clked_in_wait;
+extern int cur_arm_podf;
static void __iomem *apll_base;
static struct clk pll1_sys_main_clk;
static struct clk pll6_enet_main_clk; /* Essentially same as PLL8 on MX6Q/DL */
static struct clk pll7_usb_host_main_clk;
static struct clk usdhc3_clk;
+static struct clk ipg_clk;
static struct cpu_op *cpu_op_tbl;
static int cpu_op_nr;
#define AUDIO_VIDEO_MIN_CLK_FREQ 650000000
#define AUDIO_VIDEO_MAX_CLK_FREQ 1300000000
+#define MAX_ARM_CLK_IN_WAIT 158000000
/* We need to check the exp status again after timer expiration,
* as there might be interrupt coming between the first time exp
int i;
u32 div;
u32 parent_rate;
-
+ unsigned long ipg_clk_rate, max_arm_wait_clk;
for (i = 0; i < cpu_op_nr; i++) {
if (rate == cpu_op_tbl[i].cpu_rate)
parent_rate = clk_get_rate(clk->parent);
div = parent_rate / rate;
+ /* Calculate the ARM_PODF to be applied when the system
+ * enters WAIT state.
+ * The max ARM clk is decided by the ipg_clk and has to
+ * follow the ratio of ARM_CLK:IPG_CLK of 12:5.
+ * For ex, when IPG is at 66MHz, ARM_CLK cannot be greater
+ * than 158MHz. Pre-calculate the optimal divider now.
+ */
+ ipg_clk_rate = clk_get_rate(&ipg_clk);
+ max_arm_wait_clk = (12 * ipg_clk_rate) / 5;
+ wait_mode_arm_podf = parent_rate / max_arm_wait_clk;
+ if (wait_mode_arm_podf > 7)
+ /* IPG_CLK is too low and we cannot get a ARM_CLK
+ * that will satisfy the 12:5 ratio.
+ * Use the mem_ipg_stop_mask bit to ensure clocks
+ * to ARM memories are not gated during WAIT mode.
+ * Else disable entry to WAIT mode.
+ */
+ arm_mem_clked_in_wait = true;
+ else
+ arm_mem_clked_in_wait = false;
if (div == 0)
div = 1;
if (div > 8)
return -1;
+ cur_arm_podf = div;
+
__raw_writel(div - 1, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+
return 0;
}
#include "crm_regs.h"
#include "cpu_op-mx6.h"
-extern unsigned int num_cpu_idle_lock;
-
-void *mx6_wait_in_iram_base;
-void (*mx6_wait_in_iram)(void);
-extern void mx6_wait(void);
-
struct cpu_op *(*get_cpu_op)(int *op);
-bool enable_wait_mode;
+bool enable_wait_mode = true;
u32 arm_max_freq = CPU_AT_1GHz;
+bool mem_clk_on_in_wait;
void __iomem *gpc_base;
void __iomem *ccm_base;
+extern unsigned int num_cpu_idle_lock;
+
static int cpu_silicon_rev = -1;
#define MX6_USB_ANALOG_DIGPROG 0x260
#define MX6SL_USB_ANALOG_DIGPROG 0x280
__raw_writel(reg, base + 0x50);
iounmap(base);
- if (enable_wait_mode) {
- /* Allow SCU_CLK to be disabled when all cores are in WFI*/
- base = IO_ADDRESS(SCU_BASE_ADDR);
- reg = __raw_readl(base);
- reg |= 0x20;
- __raw_writel(reg, base);
- }
+ /* Allow SCU_CLK to be disabled when all cores are in WFI*/
+ base = IO_ADDRESS(SCU_BASE_ADDR);
+ reg = __raw_readl(base);
+ reg |= 0x20;
+ __raw_writel(reg, base);
/* Disable SRC warm reset to work aound system reboot issue */
base = IO_ADDRESS(SRC_BASE_ADDR);
ccm_base = MX6_IO_ADDRESS(CCM_BASE_ADDR);
num_cpu_idle_lock = 0x0;
+ if (cpu_is_mx6dl())
+ num_cpu_idle_lock = 0xffff0000;
+
+#ifdef CONFIG_SMP
+ switch (setup_max_cpus) {
+ case 3:
+ num_cpu_idle_lock = 0xff000000;
+ break;
+ case 2:
+ num_cpu_idle_lock = 0xffff0000;
+ break;
+ case 1:
+ case 0:
+ num_cpu_idle_lock = 0xffffff00;
+ break;
+ }
+#endif
+ /*
+ * The option to keep ARM memory clocks enabled during WAIT
+ * is only available on MX6SL, MX6DQ TO1.2 (or later) and
+ * MX6DL TO1.1 (or later)
+ * So if the user specifies "mem_clk_on" on any other chip,
+ * ensure that it is disabled.
+ */
+ if (!cpu_is_mx6sl() && (mx6q_revision() < IMX_CHIP_REVISION_1_2) &&
+ (mx6dl_revision() < IMX_CHIP_REVISION_1_1))
+ mem_clk_on_in_wait = false;
return 0;
}
early_param("arm_freq", arm_core_max);
+static int __init enable_mem_clk_in_wait(char *p)
+{
+ mem_clk_on_in_wait = true;
+
+ return 0;
+}
+
+early_param("mem_clk_on", enable_mem_clk_in_wait);
+
+
#define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (1 << 4)
#define MXC_CCM_CGPR_MMDC_EXT_CLK_DIS (1 << 2)
#define MXC_CCM_CGPR_PMIC_DELAY_SCALER (1)
+#define MXC_CCM_CGPR_MEM_IPG_STOP_MASK (1 << 1)
/* Define the bits in registers CCGRx */
#define MXC_CCM_CCGRx_CG_MASK 0x3
*/
ENTRY(mx6_wait)
- push {r4, r5, r6}
+ push {r4, r5, r6, r7, r8}
+
+ mov r7, r2 /* Store the arm_podf to be used. */
+ mov r6, r3
+
+ ldr r2, =ANATOP_BASE_ADDR
+ add r2, r2, #PERIPBASE_VIRT
+
+ ldr r8, =CCM_BASE_ADDR
+ add r8, r8, #PERIPBASE_VIRT
- ldr r2, =ANATOP_BASE_ADDR
- add r2, r2, #PERIPBASE_VIRT
/* get the flags variables into the cache */
- ldr r3, [r0]
+ ldr r3, [r0]
/* get CPU ID */
mrc p15,0,r5,c0,c0,5
mov r3, #0xff
+ /* Check to see if we need to switch to 24MHz */
+ cmp r7, #0
+ bne use_podf
ldr r6, =(1 << 16)
str r6, [r2, #0x04]
+ b cont
+
+use_podf:
+ /* Change ARM_PODF to the max possible podf
+ * so that ARM_CLK to IPG_CLK is in 12:5 ratio.
+ */
+ str r7, [r8, #0x10]
+ /* Loop till podf is accepted. */
+podf_loop:
+ ldr r4, [r8, #0x48]
+ cmp r4, #0x0
+ bne podf_loop
/* dmb */
+cont:
str r3, [r1]
dsb
mvn r4, #0x0
ldr r3, [r0]
cmp r3, r4
- movne r3, #0x0
- strne r6, [r2, #0x08]
- strne r3, [r1]
+ beq DO_WFI
+
+ mov r3, #0x0
+ /* Switch to 24MHz or use ARM_PODF. */
+ cmp r7, #0x0
+ bne use_podf1
+ str r6, [r2, #0x08]
+ b DO_WFI
+use_podf1:
+ str r6, [r8, #0x10]
+
+ str r3, [r1]
DO_WFI:
dsb
bne DONE
mov r4, #0x0
+ cmp r7, #0x0
+ bne use_podf2
ldr r6, =(1 << 16)
- str r6, [r2, #0x08]
+ str r6, [r2, #0x08]
+ b cont1
+
+use_podf2:
+ str r6, [r8, #0x10]
+cont1:
mov r3, #0x0
str r3, [r1]
DONE:
- pop {r4,r5, r6}
+ pop {r4,r5, r6, r7, r8}
/* Restore registers */
mov pc, lr
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/sched.h>
#include <asm/cacheflush.h>
+extern unsigned int num_cpu_idle_lock;
+
static atomic_t cpu_die_done = ATOMIC_INIT(0);
int platform_cpu_kill(unsigned int cpu)
{
* we don't allow CPU 0 to be shutdown (it is still too special
* e.g. clock tick interrupts)
*/
+ *((char *)(&num_cpu_idle_lock) + (u8)cpu) = 0xff;
return cpu == 0 ? -EPERM : 0;
}
static void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+int wait_mode_arm_podf;
volatile unsigned int num_cpu_idle;
volatile unsigned int num_cpu_idle_lock = 0x0;
+int wait_mode_arm_podf;
+int cur_arm_podf;
+bool arm_mem_clked_in_wait;
-extern void mx6_wait(void *num_cpu_idle_lock, void *num_cpu_idle);
+extern void mx6_wait(void *num_cpu_idle_lock, void *num_cpu_idle, \
+ int wait_arm_podf, int cur_arm_podf);
extern bool enable_wait_mode;
+extern int low_bus_freq_mode;
+extern int audio_bus_freq_mode;
+extern bool mem_clk_on_in_wait;
void gpc_set_wakeup(unsigned int irq[4])
{
MXC_CCM_CCR_WB_COUNT_OFFSET), MXC_CCM_CCR);
ccm_clpcr |= MXC_CCM_CLPCR_WB_PER_AT_LPM;
}
+ if (cpu_is_mx6sl() ||
+ (mx6q_revision() > IMX_CHIP_REVISION_1_1) ||
+ (mx6dl_revision() > IMX_CHIP_REVISION_1_0)) {
+ u32 reg;
+ /* We need to allow the memories to be clock gated
+ * in STOP mode, else the power consumption will
+ * be very high.
+ */
+ reg = __raw_readl(MXC_CCM_CGPR);
+ reg |= MXC_CCM_CGPR_MEM_IPG_STOP_MASK;
+ __raw_writel(reg, MXC_CCM_CGPR);
+ }
}
__raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
}
extern int tick_broadcast_oneshot_active(void);
-void arch_idle(void)
+ void arch_idle(void)
{
if (enable_wait_mode) {
-#ifdef CONFIG_LOCAL_TIMERS
+ u32 reg;
int cpu = smp_processor_id();
+#ifdef CONFIG_LOCAL_TIMERS
if (!tick_broadcast_oneshot_active())
return;
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
#endif
- *((char *)(&num_cpu_idle_lock) + smp_processor_id()) = 0x0;
+ *((char *)(&num_cpu_idle_lock) + (char)cpu) = 0x0;
mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
- mx6_wait((void *)&num_cpu_idle_lock, (void *)&num_cpu_idle);
+ if (arm_mem_clked_in_wait || mem_clk_on_in_wait) {
+ reg = __raw_readl(MXC_CCM_CGPR);
+ reg &= ~MXC_CCM_CGPR_MEM_IPG_STOP_MASK;
+ __raw_writel(reg, MXC_CCM_CGPR);
+
+ cpu_do_idle();
+ } else if (num_possible_cpus() == 1) {
+ /* We can directly use the divider to drop the ARM
+ * core freq in a single core environment.
+ */
+ u32 podf = wait_mode_arm_podf;
+ /* Set the ARM_PODF to get the max freq possible
+ * to avoid the WAIT mode issue when IPG is at 66MHz.
+ */
+ if (low_bus_freq_mode)
+ podf = 7;
+
+ __raw_writel(podf, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+ cpu_do_idle();
+
+ __raw_writel(cur_arm_podf - 1, MXC_CCM_CACRR);
+ } else {
+ if (low_bus_freq_mode || audio_bus_freq_mode)
+ mx6_wait((void *)&num_cpu_idle_lock,
+ (void *)&num_cpu_idle,
+ 7, cur_arm_podf - 1);
+ else
+ mx6_wait((void *)&num_cpu_idle_lock,
+ (void *)&num_cpu_idle,
+ wait_mode_arm_podf, cur_arm_podf - 1);
+ }
#ifdef CONFIG_LOCAL_TIMERS
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
#endif
- } else
+ } else {
+ mxc_cpu_lp_set(WAIT_CLOCKED);
cpu_do_idle();
+ }
}
static int __mxs_reset_block(void __iomem *hwreg, int just_enable)