aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mx6/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-mx6/system.c')
-rw-r--r--arch/arm/mach-mx6/system.c682
1 files changed, 682 insertions, 0 deletions
diff --git a/arch/arm/mach-mx6/system.c b/arch/arm/mach-mx6/system.c
new file mode 100644
index 00000000..a195094b
--- /dev/null
+++ b/arch/arm/mach-mx6/system.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2011-2013 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pmic_external.h>
+#include <linux/clockchips.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include <asm/hardware/gic.h>
+#include "crm_regs.h"
+#include "regs-anadig.h"
+
+#include "ntx_hwconfig.h"
+
+
+#define SCU_CTRL 0x00
+#define SCU_CONFIG 0x04
+#define SCU_CPU_STATUS 0x08
+#define SCU_INVALIDATE 0x0c
+#define SCU_FPGA_REVISION 0x10
+#define GPC_CNTR_OFFSET 0x0
+#define GPC_PGC_GPU_PGCR_OFFSET 0x260
+#define GPC_PGC_CPU_PDN_OFFSET 0x2a0
+#define GPC_PGC_CPU_PUPSCR_OFFSET 0x2a4
+#define GPC_PGC_CPU_PDNSCR_OFFSET 0x2a8
+
+#define MODULE_CLKGATE (1 << 30)
+#define MODULE_SFTRST (1 << 31)
+
+extern unsigned int gpc_wake_irq[4];
+
+static void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+extern struct clk *mmdc_ch0_axi;
+
+volatile unsigned int num_cpu_idle;
+volatile unsigned int num_cpu_idle_lock = 0x0;
+int wait_mode_arm_podf;
+int cur_arm_podf;
+bool enet_is_active;
+void arch_idle_with_workaround(int cpu);
+
+extern void *mx6sl_wfi_iram_base;
+extern void (*mx6sl_wfi_iram)(int arm_podf, unsigned long wfi_iram_addr, \
+ int audio_mode);
+extern void mx6_wait(void *num_cpu_idle_lock, void *num_cpu_idle, \
+ int wait_arm_podf, int cur_arm_podf);
+extern unsigned long save_ttbr1(void);
+extern void restore_ttbr1(u32 ttbr1);
+
+extern bool enable_wait_mode;
+extern int low_bus_freq_mode;
+extern int audio_bus_freq_mode;
+extern bool mem_clk_on_in_wait;
+extern int chip_rev;
+
+extern volatile NTX_HWCONFIG *gptHWCFG;
+
+void gpc_set_wakeup(unsigned int irq[4])
+{
+ /* Mask all wake up source */
+ __raw_writel(~irq[0], gpc_base + 0x8);
+ __raw_writel(~irq[1], gpc_base + 0xc);
+ __raw_writel(~irq[2], gpc_base + 0x10);
+ __raw_writel(~irq[3], gpc_base + 0x14);
+
+ return;
+}
+
+void gpc_mask_single_irq(int irq, bool enable)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = gpc_base + 0x8 + (irq / 32 - 1) * 4;
+ val = __raw_readl(reg);
+ if (enable)
+ val |= 1 << (irq % 32);
+ else
+ val &= ~(1 << (irq % 32));
+ __raw_writel(val, reg);
+
+ return;
+}
+
+/* set cpu low power mode before WFI instruction */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+
+ int stop_mode = 0;
+ void __iomem *anatop_base = IO_ADDRESS(ANATOP_BASE_ADDR);
+ u32 ccm_clpcr, anatop_val;
+
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
+ /*
+ * CCM state machine has restriction that, everytime enable
+ * LPM mode, we need to make sure last wakeup from LPM mode
+ * is a dsm_wakeup_signal, which means the wakeup source
+ * must be seen by GPC, then CCM will clean its state machine
+ * and re-sample necessary signal to decide whether it can
+ * enter LPM mode. Here we force irq #32 to be always pending,
+ * unmask it before we enable LPM mode and mask it after LPM
+ * is enabled, this flow will make sure CCM state machine in
+ * reliable state before we enter LPM mode.
+ */
+ gpc_mask_single_irq(MXC_INT_GPR, false);
+
+ switch (mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ case STOP_POWER_OFF:
+ case ARM_POWER_OFF:
+ if (mode == WAIT_UNCLOCKED_POWER_OFF) {
+ ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
+ ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
+ if (cpu_is_mx6sl()) {
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY;
+ } else
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ stop_mode = 0;
+ } else if (mode == STOP_POWER_OFF) {
+ ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
+ ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
+ if (cpu_is_mx6sl()) {
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY;
+ } else
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ stop_mode = 1;
+ } else {
+ ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
+ ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
+ if (cpu_is_mx6sl()) {
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY;
+ } else
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ stop_mode = 2;
+ }
+ break;
+ case STOP_XTAL_ON:
+ ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
+ if (cpu_is_mx6sl()) {
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS;
+ ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY;
+ } else
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ stop_mode = 3;
+
+ break;
+ default:
+ printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
+ gpc_mask_single_irq(MXC_INT_GPR, true);
+ return;
+ }
+
+ if (stop_mode > 0) {
+ gpc_set_wakeup(gpc_wake_irq);
+ /* Power down and power up sequence */
+ /* The PUPSCR counter counts in terms of CLKIL (32KHz) cycles.
+ * The PUPSCR should include the time it takes for the ARM LDO to
+ * ramp up.
+ */
+ __raw_writel(0xf0f, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
+ /* The PDNSCR is a counter that counts in IPG_CLK cycles. This counter
+ * can be set to minimum values to power down faster.
+ */
+ __raw_writel(0x101, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
+ if (stop_mode >= 2) {
+ /* dormant mode, need to power off the arm core */
+ __raw_writel(0x1, gpc_base + GPC_PGC_CPU_PDN_OFFSET);
+#if 0
+ if (cpu_is_mx6q() || cpu_is_mx6dl() && (4!=gptHWCFG->m_val.bRamType) )
+#else
+ if( cpu_is_mx6q() || cpu_is_mx6dl() )
+#endif
+ {
+ /* If stop_mode_config is clear, then 2P5 will be off,
+ need to enable weak 2P5, as DDR IO need 2P5 as
+ pre-driver */
+ if ((__raw_readl(anatop_base + HW_ANADIG_ANA_MISC0)
+ & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) {
+ /* Enable weak 2P5 linear regulator */
+ anatop_val = __raw_readl(anatop_base +
+ HW_ANADIG_REG_2P5);
+ anatop_val |= BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG;
+ __raw_writel(anatop_val, anatop_base +
+ HW_ANADIG_REG_2P5);
+ }
+ if (mx6q_revision() != IMX_CHIP_REVISION_1_0) {
+ /* Enable fet_odrive */
+ anatop_val = __raw_readl(anatop_base +
+ HW_ANADIG_REG_CORE);
+ anatop_val |= BM_ANADIG_REG_CORE_FET_ODRIVE;
+ __raw_writel(anatop_val, anatop_base +
+ HW_ANADIG_REG_CORE);
+ }
+ } else {
+ if (stop_mode == 2) {
+#if 1
+ /* Disable VDDHIGH_IN to VDDSNVS_IN
+ * power path, only used when VDDSNVS_IN
+ * is powered by dedicated
+ * power rail */
+ anatop_val = __raw_readl(anatop_base +
+ HW_ANADIG_ANA_MISC0);
+ anatop_val |= BM_ANADIG_ANA_MISC0_RTC_RINGOSC_EN;
+ __raw_writel(anatop_val, anatop_base +
+ HW_ANADIG_ANA_MISC0);
+#endif
+ /* Need to enable pull down if 2P5 is disabled */
+ anatop_val = __raw_readl(anatop_base +
+ HW_ANADIG_REG_2P5);
+
+#if 1 //[
+ if ( /* <= E60Q2XA14 */
+ (33==gptHWCFG->m_val.bPCB && gptHWCFG->m_val.bPCB_REV<=0x14 ) ||
+ /* <= E60Q30BXX */
+ (36==gptHWCFG->m_val.bPCB && gptHWCFG->m_val.bPCB_LVL==1 ) ||
+ /* <= E60QBXA00 */
+ (37==gptHWCFG->m_val.bPCB && gptHWCFG->m_val.bPCB_REV<=0 ) )
+ {
+ //printk("%s<=E60Q2XA14|<=E60Q3XA00\n",__FUNCTION__);
+ /* Enable weak 2P5 linear regulator */
+ anatop_val |= BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG|
+ BM_ANADIG_REG_2P5_ENABLE_ILIMIT;
+ }
+ else
+ {
+ anatop_val |= (BM_ANADIG_REG_2P5_ENABLE_ILIMIT|
+ BM_ANADIG_REG_2P5_ENABLE_PULLDOWN);
+ }
+#endif //]
+
+ __raw_writel(anatop_val, anatop_base +
+ HW_ANADIG_REG_2P5);
+
+ anatop_val = __raw_readl(anatop_base +
+ HW_ANADIG_REG_1P1);
+ anatop_val |= BM_ANADIG_REG_1P1_ENABLE_ILIMIT;
+ __raw_writel(anatop_val, anatop_base +
+ HW_ANADIG_REG_1P1);
+ }
+ }
+ if (stop_mode != 3) {
+ /* Make sure we clear WB_COUNT
+ * and re-config it.
+ */
+ __raw_writel(__raw_readl(MXC_CCM_CCR) &
+ (~MXC_CCM_CCR_WB_COUNT_MASK),
+ MXC_CCM_CCR);
+ /* Reconfigure WB, need to set WB counter
+ * to 0x7 to make sure it work normally */
+ __raw_writel(__raw_readl(MXC_CCM_CCR) |
+ (0x7 << MXC_CCM_CCR_WB_COUNT_OFFSET),
+ MXC_CCM_CCR);
+
+ /* Set WB_PER enable */
+ 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;
+ if (!cpu_is_mx6sl() && stop_mode >= 2) {
+ /*
+ * For MX6QTO1.2 or later and MX6DLTO1.1 or later,
+ * ensure that the CCM_CGPR bit 17 is cleared before
+ * dormant mode is entered.
+ */
+ reg &= ~MXC_CCM_CGPR_WAIT_MODE_FIX;
+ }
+ __raw_writel(reg, MXC_CCM_CGPR);
+ }
+ }
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+ gpc_mask_single_irq(MXC_INT_GPR, true);
+}
+
+extern int tick_broadcast_oneshot_active(void);
+
+void ca9_do_idle(void)
+{
+ do {
+ cpu_do_idle();
+ } while (__raw_readl(gic_cpu_base_addr + GIC_CPU_HIGHPRI) == 1023);
+}
+
+void arch_idle_single_core(void)
+{
+ u32 reg;
+
+ if (cpu_is_mx6dl() && chip_rev > IMX_CHIP_REVISION_1_0) {
+ /*
+ * MX6DLS TO1.1 has the HW fix for the WAIT mode issue.
+ * Ensure that the CGPR bit 17 is set to enable the fix.
+ */
+ reg = __raw_readl(MXC_CCM_CGPR);
+ reg |= MXC_CCM_CGPR_WAIT_MODE_FIX;
+ __raw_writel(reg, MXC_CCM_CGPR);
+
+ ca9_do_idle();
+ } else {
+ if (low_bus_freq_mode || audio_bus_freq_mode) {
+ int ddr_usecount = 0;
+ if ((mmdc_ch0_axi != NULL))
+ ddr_usecount = clk_get_usecount(mmdc_ch0_axi);
+
+ if (cpu_is_mx6sl() && (ddr_usecount == 1) &&
+ (low_bus_freq_mode || audio_bus_freq_mode)) {
+ /* In this mode PLL2 i already in bypass,
+ * ARM is sourced from PLL1. The code in IRAM
+ * will set ARM to be sourced from STEP_CLK
+ * at 24MHz. It will also set DDR to 1MHz to
+ * reduce power.
+ */
+ u32 org_arm_podf = __raw_readl(MXC_CCM_CACRR);
+ u32 ttbr1;
+
+ outer_sync();
+ /* Need to run WFI code from IRAM so that
+ * we can lower DDR freq.
+ */
+ ttbr1 = save_ttbr1();
+ mx6sl_wfi_iram(org_arm_podf,
+ (unsigned long)mx6sl_wfi_iram_base,
+ audio_bus_freq_mode);
+ restore_ttbr1(ttbr1);
+ } else {
+ /* Need to set ARM to run at 24MHz since IPG
+ * is at 12MHz. This is valid for audio mode on
+ * MX6SL, and all low power modes on MX6DLS.
+ */
+ if (cpu_is_mx6sl() && low_bus_freq_mode) {
+ /* ARM is from PLL1, need to switch to
+ * STEP_CLK sourced from 24MHz.
+ */
+ /* Swtich STEP_CLK to 24MHz. */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg &= ~MXC_CCM_CCSR_STEP_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ /* Set PLL1_SW_CLK to be from
+ *STEP_CLK.
+ */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ } else {
+ /* PLL1_SW_CLK is sourced from
+ * PLL2_PFD2_400MHz at this point.
+ * Move it to bypassed PLL1.
+ */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ }
+ ca9_do_idle();
+
+ if (cpu_is_mx6sl() && low_bus_freq_mode) {
+ /* Set PLL1_SW_CLK to be from PLL1 */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ } else {
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ }
+ }
+ } else {
+ /*
+ * Implement the 12:5 ARM:IPG_CLK ratio
+ * workaround for the WAIT mode issue.
+ * We can directly use the divider to drop the ARM
+ * core freq in a single core environment.
+ * Set the ARM_PODF to get the max freq possible
+ * to avoid the WAIT mode issue when IPG is at 66MHz.
+ */
+ __raw_writel(wait_mode_arm_podf, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+ ca9_do_idle();
+
+ __raw_writel(cur_arm_podf - 1, MXC_CCM_CACRR);
+ }
+ }
+}
+
+void arch_idle_with_workaround(int cpu)
+{
+ u32 podf = wait_mode_arm_podf;
+
+ *((char *)(&num_cpu_idle_lock) + (char)cpu) = 0x0;
+
+ if (low_bus_freq_mode || audio_bus_freq_mode)
+ /* In case when IPG is at 12MHz, we need to ensure that
+ * ARM is at 24MHz, as the max freq ARM can run at is
+ *~28.8MHz.
+ */
+ podf = 0;
+
+ mx6_wait((void *)&num_cpu_idle_lock,
+ (void *)&num_cpu_idle,
+ podf, cur_arm_podf - 1);
+
+}
+
+void arch_idle_multi_core(int cpu)
+{
+ u32 reg;
+
+ /* iMX6Q and iMX6DL */
+ if ((cpu_is_mx6q() && chip_rev >= IMX_CHIP_REVISION_1_2) ||
+ (cpu_is_mx6dl() && chip_rev >= IMX_CHIP_REVISION_1_1)) {
+ /*
+ * This code should only be executed on MX6QTO1.2 or later
+ * and MX6DL TO1.1 or later.
+ * These chips have the HW fix for the WAIT mode issue.
+ * Ensure that the CGPR bit 17 is set to enable the fix.
+ */
+
+ reg = __raw_readl(MXC_CCM_CGPR);
+ reg |= MXC_CCM_CGPR_WAIT_MODE_FIX;
+ __raw_writel(reg, MXC_CCM_CGPR);
+
+ ca9_do_idle();
+ } else
+ arch_idle_with_workaround(cpu);
+}
+
+void arch_idle(void)
+{
+ int cpu = smp_processor_id();
+
+ if (enable_wait_mode) {
+#ifdef CONFIG_LOCAL_TIMERS
+ if (!tick_broadcast_oneshot_active()
+ || !tick_oneshot_mode_active())
+ return;
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
+#endif
+ if (enet_is_active)
+ /* Don't allow the chip to enter WAIT mode if enet is active
+ * and the GPIO workaround for ENET interrupts is not used,
+ * since all ENET interrupts donot wake up the SOC.
+ */
+ mxc_cpu_lp_set(WAIT_CLOCKED);
+ else
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+ if (mem_clk_on_in_wait) {
+ u32 reg;
+ /*
+ * MX6SL, MX6Q (TO1.2 or later) and
+ * MX6DL (TO1.1 or later) have a bit in
+ * CCM_CGPR that when cleared keeps the
+ * clocks to memories ON when ARM is in WFI.
+ * This mode can be used when IPG clock is
+ * very low (12MHz) and the ARM:IPG ratio
+ * perhaps cannot be maintained.
+ */
+ reg = __raw_readl(MXC_CCM_CGPR);
+ reg &= ~MXC_CCM_CGPR_MEM_IPG_STOP_MASK;
+ __raw_writel(reg, MXC_CCM_CGPR);
+
+ ca9_do_idle();
+ } else if (num_possible_cpus() == 1)
+ /* iMX6SL or iMX6DLS */
+ arch_idle_single_core();
+ else
+ arch_idle_multi_core(cpu);
+#ifdef CONFIG_LOCAL_TIMERS
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
+#endif
+ } else {
+ mxc_cpu_lp_set(WAIT_CLOCKED);
+ ca9_do_idle();
+ }
+}
+
+static int __mxs_reset_block(void __iomem *hwreg, int just_enable)
+{
+ u32 c;
+ int timeout;
+
+ /* the process of software reset of IP block is done
+ in several steps:
+
+ - clear SFTRST and wait for block is enabled;
+ - clear clock gating (CLKGATE bit);
+ - set the SFTRST again and wait for block is in reset;
+ - clear SFTRST and wait for reset completion.
+ */
+ c = __raw_readl(hwreg);
+ c &= ~MODULE_SFTRST; /* clear SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & MODULE_SFTRST) == 0)
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR "%s(%p): timeout when enabling\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~MODULE_CLKGATE; /* clear CLKGATE */
+ __raw_writel(c, hwreg);
+
+ if (!just_enable) {
+ c = __raw_readl(hwreg);
+ c |= MODULE_SFTRST; /* now again set SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* poll until CLKGATE set */
+ if (__raw_readl(hwreg) & MODULE_CLKGATE)
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR "%s(%p): timeout when resetting\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~MODULE_SFTRST; /* clear SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & MODULE_SFTRST) == 0)
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR "%s(%p): timeout when enabling "
+ "after reset\n", __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~MODULE_CLKGATE; /* clear CLKGATE */
+ __raw_writel(c, hwreg);
+ }
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & MODULE_CLKGATE) == 0)
+ break;
+
+ if (timeout <= 0) {
+ printk(KERN_ERR "%s(%p): timeout when unclockgating\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int _mxs_reset_block(void __iomem *hwreg, int just_enable)
+{
+ int try = 10;
+ int r;
+
+ while (try--) {
+ r = __mxs_reset_block(hwreg, just_enable);
+ if (!r)
+ break;
+ pr_debug("%s: try %d failed\n", __func__, 10 - try);
+ }
+ return r;
+}
+
+
+#define BOOT_MODE_SERIAL_ROM (0x00000030)
+#define PERSIST_WATCHDOG_RESET_BOOT (0x10000000)
+/*BOOT_CFG1[7..4] = 0x3 Boot from Serial ROM (I2C/SPI)*/
+
+#ifdef CONFIG_MXC_REBOOT_MFGMODE
+void do_switch_mfgmode(void)
+{
+ u32 reg;
+
+ /*
+ * During reset, if GPR10[28] is 1, ROM will copy GPR9[25:0]
+ * to SBMR1, which will determine what is the boot device.
+ * Here SERIAL_ROM mode is selected
+ */
+ reg = __raw_readl(SRC_BASE_ADDR + SRC_GPR9);
+ reg |= BOOT_MODE_SERIAL_ROM;
+ __raw_writel(reg, SRC_BASE_ADDR + SRC_GPR9);
+
+ reg = __raw_readl(SRC_BASE_ADDR + SRC_GPR10);
+ reg |= PERSIST_WATCHDOG_RESET_BOOT;
+ __raw_writel(reg, SRC_BASE_ADDR + SRC_GPR10);
+
+}
+
+void mxc_clear_mfgmode(void)
+{
+ u32 reg;
+ reg = __raw_readl(SRC_BASE_ADDR + SRC_GPR9);
+
+ reg &= ~BOOT_MODE_SERIAL_ROM;
+ __raw_writel(reg, SRC_BASE_ADDR + SRC_GPR9);
+
+ reg = __raw_readl(SRC_BASE_ADDR + SRC_GPR10);
+ reg &= ~PERSIST_WATCHDOG_RESET_BOOT;
+ __raw_writel(reg, SRC_BASE_ADDR + SRC_GPR10);
+}
+#endif
+
+#ifdef CONFIG_MXC_REBOOT_ANDROID_CMD
+/* This function will set a bit on SNVS_LPGPR[7-8] bits to enter
+ * special boot mode. These bits will not clear by watchdog reset, so
+ * it can be checked by bootloader to choose enter different mode.*/
+
+#define ANDROID_RECOVERY_BOOT (1 << 7)
+#define ANDROID_FASTBOOT_BOOT (1 << 8)
+
+void do_switch_recovery(void)
+{
+ u32 reg;
+
+ reg = __raw_readl(MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);
+ reg |= ANDROID_RECOVERY_BOOT;
+ __raw_writel(reg, MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);
+}
+
+void do_switch_fastboot(void)
+{
+ u32 reg;
+
+ reg = __raw_readl(MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);
+ reg |= ANDROID_FASTBOOT_BOOT;
+ __raw_writel(reg, MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);
+}
+#endif
+
+int mxs_reset_block(void __iomem *hwreg)
+{
+ return _mxs_reset_block(hwreg, false);
+}
+EXPORT_SYMBOL(mxs_reset_block);