diff options
Diffstat (limited to 'arch/arm/mach-mx5/system.c')
-rw-r--r-- | arch/arm/mach-mx5/system.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/arch/arm/mach-mx5/system.c b/arch/arm/mach-mx5/system.c new file mode 100644 index 000000000000..ba9989b935f1 --- /dev/null +++ b/arch/arm/mach-mx5/system.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <asm/io.h> +#include <mach/hardware.h> +#include <asm/proc-fns.h> +#include <asm/system.h> +#include "crm_regs.h" + +/*! + * @defgroup MSL_MX51 i.MX51 Machine Specific Layer (MSL) + */ + +/*! + * @file mach-mx51/system.c + * @brief This file contains idle and reset functions. + * + * @ingroup MSL_MX51 + */ + +extern int mxc_jtag_enabled; +extern int iram_ready; +static struct clk *gpc_dvfs_clk; + +/* set cpu low power mode before WFI instruction */ +void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) +{ + u32 plat_lpc, arm_srpgcr, ccm_clpcr; + u32 empgc0, empgc1; + int stop_mode = 0; + + /* always allow platform to issue a deep sleep mode request */ + plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) & + ~(MXC_CORTEXA8_PLAT_LPC_DSM); + ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); + arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR); + empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR); + empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR); + + 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: + plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM + | MXC_CORTEXA8_PLAT_LPC_DBG_DSM; + if (mode == WAIT_UNCLOCKED_POWER_OFF) { + ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET); + ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; + ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; + stop_mode = 0; + } 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; + stop_mode = 1; + } + + arm_srpgcr |= MXC_SRPGCR_PCR; + if (stop_mode) { + empgc0 |= MXC_SRPGCR_PCR; + empgc1 |= MXC_SRPGCR_PCR; + } + + if (tzic_enable_wake(1) != 0) + return; + break; + case STOP_POWER_ON: + ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET); + break; + default: + printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); + return; + } + + __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC); + __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); + /* Need to fix this for MX53 and MX508 */ + if (cpu_is_mx51()) + __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR); + __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR); + if (stop_mode) { + __raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR); + } +} + +void mxc_pg_enable(struct platform_device *pdev) +{ + if (pdev == NULL) + return; + + if (strcmp(pdev->name, "mxc_ipu") == 0) { + __raw_writel(MXC_PGCR_PCR, MXC_PGC_IPU_PGCR); + __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR); + } else if (strcmp(pdev->name, "mxc_vpu") == 0) { + __raw_writel(MXC_PGCR_PCR, MXC_PGC_VPU_PGCR); + __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR); + } +} + +EXPORT_SYMBOL(mxc_pg_enable); + +void mxc_pg_disable(struct platform_device *pdev) +{ + if (pdev == NULL) + return; + + if (strcmp(pdev->name, "mxc_ipu") == 0) { + __raw_writel(0x0, MXC_PGC_IPU_PGCR); + if (__raw_readl(MXC_PGC_IPU_PGSR) & MXC_PGSR_PSR) + dev_dbg(&pdev->dev, "power gating successful\n"); + __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR); + } else if (strcmp(pdev->name, "mxc_vpu") == 0) { + __raw_writel(0x0, MXC_PGC_VPU_PGCR); + if (__raw_readl(MXC_PGC_VPU_PGSR) & MXC_PGSR_PSR) + dev_dbg(&pdev->dev, "power gating successful\n"); + __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR); + } +} + +EXPORT_SYMBOL(mxc_pg_disable); + +/* To change the idle power mode, need to set arch_idle_mode to a different + * power mode as in enum mxc_cpu_pwr_mode. + * May allow dynamically changing the idle mode. + */ +static int arch_idle_mode = WAIT_UNCLOCKED_POWER_OFF; +/*! + * This function puts the CPU into idle mode. It is called by default_idle() + * in process.c file. + */ +void mx51_idle(void) +{ +/* if (likely(!mxc_jtag_enabled))*/ { + if (gpc_dvfs_clk == NULL) + gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk"); + /* gpc clock is needed for SRPG */ + clk_enable(gpc_dvfs_clk); + mxc_cpu_lp_set(arch_idle_mode); + cpu_do_idle(); + clk_disable(gpc_dvfs_clk); + } +} |