aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mx6/pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-mx6/pm.c')
-rw-r--r--arch/arm/mach-mx6/pm.c600
1 files changed, 600 insertions, 0 deletions
diff --git a/arch/arm/mach-mx6/pm.c b/arch/arm/mach-mx6/pm.c
new file mode 100644
index 00000000..a6b4c748
--- /dev/null
+++ b/arch/arm/mach-mx6/pm.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2011-2013 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/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/anatop-regulator.h>
+#include <linux/proc_fs.h>
+#include <linux/iram_alloc.h>
+#include <linux/fsl_devices.h>
+#include <asm/mach-types.h>
+#include <asm/cacheflush.h>
+#include <asm/tlb.h>
+#include <asm/delay.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+#include <mach/imx-pm.h>
+#include <mach/arc_otg.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#ifdef CONFIG_ARCH_MX6Q
+#include <mach/iomux-mx6q.h>
+#endif
+#include "crm_regs.h"
+#include "src-reg.h"
+#include "regs-anadig.h"
+
+#define SCU_CTRL_OFFSET 0x00
+#define GPC_IMR1_OFFSET 0x08
+#define GPC_IMR2_OFFSET 0x0c
+#define GPC_IMR3_OFFSET 0x10
+#define GPC_IMR4_OFFSET 0x14
+#define GPC_ISR1_OFFSET 0x18
+#define GPC_ISR2_OFFSET 0x1c
+#define GPC_ISR3_OFFSET 0x20
+#define GPC_ISR4_OFFSET 0x24
+#define GPC_CNTR_OFFSET 0x0
+#define GPC_PGC_DISP_PGCR_OFFSET 0x240
+#define GPC_PGC_DISP_PUPSCR_OFFSET 0x244
+#define GPC_PGC_DISP_PDNSCR_OFFSET 0x248
+#define GPC_PGC_DISP_SR_OFFSET 0x24c
+#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 UART_UCR3_OFFSET 0x88
+#define UART_USR1_OFFSET 0x94
+#define UART_UCR3_AWAKEN (1 << 4)
+#define UART_USR1_AWAKE (1 << 4)
+#define LOCAL_TWD_LOAD_OFFSET 0x0
+#define LOCAL_TWD_COUNT_OFFSET 0x4
+#define LOCAL_TWD_CONTROL_OFFSET 0x8
+#define LOCAL_TWD_INT_OFFSET 0xc
+#define ANATOP_REG_1P1_OFFSET 0x110
+#define ANATOP_REG_2P5_OFFSET 0x130
+#define ANATOP_REG_CORE_OFFSET 0x140
+#define VDD3P0_VOLTAGE 3200000
+
+static struct clk *cpu_clk;
+static struct clk *axi_clk;
+static struct clk *periph_clk;
+static struct clk *pll3_usb_otg_main_clk;
+static struct regulator *vdd3p0_regulator;
+
+static struct pm_platform_data *pm_data;
+
+
+#if defined(CONFIG_CPU_FREQ)
+extern int set_cpu_freq(int wp);
+#endif
+extern void mx6_suspend(suspend_state_t state);
+extern void mx6_init_irq(void);
+extern unsigned int gpc_wake_irq[4];
+extern bool enable_wait_mode;
+extern unsigned long save_ttbr1(void);
+extern void restore_ttbr1(u32 ttbr1);
+extern int pu_disable(struct anatop_regulator *sreg);
+
+static struct device *pm_dev;
+struct clk *gpc_dvfs_clk;
+static void __iomem *scu_base;
+static void __iomem *gpc_base;
+static void __iomem *src_base;
+static void __iomem *local_twd_base;
+static void __iomem *gic_dist_base;
+static void __iomem *gic_cpu_base;
+static void __iomem *anatop_base;
+
+static void (*suspend_in_iram)(suspend_state_t state,
+ unsigned long iram_paddr, unsigned long suspend_iram_base, unsigned int cpu_type) = NULL;
+static unsigned long cpaddr;
+
+static u32 ccm_ccr, ccm_clpcr, scu_ctrl;
+static u32 gpc_imr[4], gpc_cpu_pup, gpc_cpu_pdn, gpc_cpu, gpc_ctr, gpc_disp;
+static u32 anatop[3], ccgr1, ccgr2, ccgr3, ccgr6;
+static u32 ccm_analog_pfd528;
+static u32 ccm_analog_pll3_480;
+static u32 ccm_anadig_ana_misc2;
+static bool usb_vbus_wakeup_enabled;
+
+void *suspend_iram_base;
+unsigned long suspend_iram_phys_addr;
+
+/*
+ * The USB VBUS wakeup should be disabled to avoid vbus wake system
+ * up due to vbus comparator is closed at weak 2p5 mode.
+ */
+static void usb_power_down_handler(void)
+{
+ u32 temp;
+ bool usb_oh3_clk_already_on;
+ if ((__raw_readl(anatop_base + HW_ANADIG_ANA_MISC0)
+ & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) != 0) {
+ usb_vbus_wakeup_enabled = false;
+ return;
+ }
+ /* enable usb oh3 clock if needed*/
+ temp = __raw_readl(MXC_CCM_CCGR6);
+ usb_oh3_clk_already_on = \
+ ((temp & (MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET)) \
+ == (MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET));
+ if (!usb_oh3_clk_already_on) {
+ temp |= MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET;
+ __raw_writel(temp, MXC_CCM_CCGR6);
+ }
+ /* disable vbus wakeup */
+ usb_vbus_wakeup_enabled = !!(USB_OTG_CTRL & UCTRL_WKUP_VBUS_EN);
+ if (usb_vbus_wakeup_enabled) {
+ USB_OTG_CTRL &= ~UCTRL_WKUP_VBUS_EN;
+ }
+ /* disable usb oh3 clock */
+ if (!usb_oh3_clk_already_on) {
+ temp = __raw_readl(MXC_CCM_CCGR6);
+ temp &= ~(MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET);
+ __raw_writel(temp, MXC_CCM_CCGR6);
+ }
+}
+
+static void usb_power_up_handler(void)
+{
+ /* enable vbus wakeup at runtime if needed */
+ if (usb_vbus_wakeup_enabled) {
+ u32 temp;
+ bool usb_oh3_clk_already_on;
+ /* enable usb oh3 clock if needed*/
+ temp = __raw_readl(MXC_CCM_CCGR6);
+ usb_oh3_clk_already_on = \
+ ((temp & (MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET)) \
+ == (MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET));
+ if (!usb_oh3_clk_already_on) {
+ temp |= MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET;
+ __raw_writel(temp, MXC_CCM_CCGR6);
+ }
+
+ /* restore usb wakeup enable setting */
+ USB_OTG_CTRL |= UCTRL_WKUP_VBUS_EN;
+
+ /* disable usb oh3 clock */
+ if (!usb_oh3_clk_already_on) {
+ temp = __raw_readl(MXC_CCM_CCGR6);
+ temp &= ~(MXC_CCM_CCGRx_CG_MASK << MXC_CCM_CCGRx_CG0_OFFSET);
+ __raw_writel(temp, MXC_CCM_CCGR6);
+ }
+ }
+}
+
+
+static void disp_power_down(void)
+{
+ if (cpu_is_mx6sl() && (mx6sl_revision() >= IMX_CHIP_REVISION_1_2)) {
+
+ __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_DISP_PUPSCR_OFFSET);
+ __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_DISP_PDNSCR_OFFSET);
+
+ __raw_writel(0x1, gpc_base + GPC_PGC_DISP_PGCR_OFFSET);
+ __raw_writel(0x10, gpc_base + GPC_CNTR_OFFSET);
+
+ /* Disable EPDC/LCDIF pix clock, and EPDC/LCDIF/PXP axi clock */
+ __raw_writel(ccgr3 &
+ ~MXC_CCM_CCGRx_CG5_MASK &
+ ~MXC_CCM_CCGRx_CG4_MASK &
+ ~MXC_CCM_CCGRx_CG3_MASK &
+ ~MXC_CCM_CCGRx_CG2_MASK &
+ ~MXC_CCM_CCGRx_CG1_MASK, MXC_CCM_CCGR3);
+
+ }
+}
+
+static void disp_power_up(void)
+{
+ if (cpu_is_mx6sl() && (mx6sl_revision() >= IMX_CHIP_REVISION_1_2)) {
+ /*
+ * Need to enable EPDC/LCDIF pix clock, and
+ * EPDC/LCDIF/PXP axi clock before power up.
+ */
+ __raw_writel(ccgr3 |
+ MXC_CCM_CCGRx_CG5_MASK |
+ MXC_CCM_CCGRx_CG4_MASK |
+ MXC_CCM_CCGRx_CG3_MASK |
+ MXC_CCM_CCGRx_CG2_MASK |
+ MXC_CCM_CCGRx_CG1_MASK, MXC_CCM_CCGR3);
+
+ __raw_writel(0x0, gpc_base + GPC_PGC_DISP_PGCR_OFFSET);
+ __raw_writel(0x20, gpc_base + GPC_CNTR_OFFSET);
+ __raw_writel(0x1, gpc_base + GPC_PGC_DISP_SR_OFFSET);
+ }
+}
+
+static void mx6_suspend_store(void)
+{
+ /* save some settings before suspend */
+ ccm_ccr = __raw_readl(MXC_CCM_CCR);
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR);
+ ccm_analog_pfd528 = __raw_readl(PFD_528_BASE_ADDR);
+ ccm_analog_pll3_480 = __raw_readl(PLL3_480_USB1_BASE_ADDR);
+ ccm_anadig_ana_misc2 = __raw_readl(MXC_PLL_BASE + HW_ANADIG_ANA_MISC2);
+ ccgr1 = __raw_readl(MXC_CCM_CCGR1);
+ ccgr2 = __raw_readl(MXC_CCM_CCGR2);
+ ccgr3 = __raw_readl(MXC_CCM_CCGR3);
+ ccgr6 = __raw_readl(MXC_CCM_CCGR6);
+ scu_ctrl = __raw_readl(scu_base + SCU_CTRL_OFFSET);
+ gpc_imr[0] = __raw_readl(gpc_base + GPC_IMR1_OFFSET);
+ gpc_imr[1] = __raw_readl(gpc_base + GPC_IMR2_OFFSET);
+ gpc_imr[2] = __raw_readl(gpc_base + GPC_IMR3_OFFSET);
+ gpc_imr[3] = __raw_readl(gpc_base + GPC_IMR4_OFFSET);
+ gpc_cpu_pup = __raw_readl(gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
+ gpc_cpu_pdn = __raw_readl(gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
+ gpc_cpu = __raw_readl(gpc_base + GPC_PGC_CPU_PDN_OFFSET);
+ gpc_ctr = __raw_readl(gpc_base + GPC_CNTR_OFFSET);
+ if (cpu_is_mx6sl())
+ gpc_disp = __raw_readl(gpc_base + GPC_PGC_DISP_PGCR_OFFSET);
+ anatop[0] = __raw_readl(anatop_base + ANATOP_REG_2P5_OFFSET);
+ anatop[1] = __raw_readl(anatop_base + ANATOP_REG_CORE_OFFSET);
+ anatop[2] = __raw_readl(anatop_base + ANATOP_REG_1P1_OFFSET);
+}
+
+static void mx6_suspend_restore(void)
+{
+ /* restore settings after suspend */
+ __raw_writel(anatop[0], anatop_base + ANATOP_REG_2P5_OFFSET);
+ __raw_writel(anatop[1], anatop_base + ANATOP_REG_CORE_OFFSET);
+ __raw_writel(anatop[2], anatop_base + ANATOP_REG_1P1_OFFSET);
+ /* Per spec, the count needs to be zeroed and reconfigured on exit from
+ * low power mode
+ */
+ __raw_writel(ccm_ccr & ~MXC_CCM_CCR_REG_BYPASS_CNT_MASK &
+ ~MXC_CCM_CCR_WB_COUNT_MASK, MXC_CCM_CCR);
+ udelay(50);
+ __raw_writel(ccm_ccr, MXC_CCM_CCR);
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+ __raw_writel(scu_ctrl, scu_base + SCU_CTRL_OFFSET);
+ __raw_writel(gpc_imr[0], gpc_base + GPC_IMR1_OFFSET);
+ __raw_writel(gpc_imr[1], gpc_base + GPC_IMR2_OFFSET);
+ __raw_writel(gpc_imr[2], gpc_base + GPC_IMR3_OFFSET);
+ __raw_writel(gpc_imr[3], gpc_base + GPC_IMR4_OFFSET);
+ __raw_writel(gpc_cpu_pup, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
+ __raw_writel(gpc_cpu_pdn, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
+ __raw_writel(gpc_cpu, gpc_base + GPC_PGC_CPU_PDN_OFFSET);
+ if (cpu_is_mx6sl())
+ __raw_writel(gpc_disp, gpc_base + GPC_PGC_DISP_PGCR_OFFSET);
+ __raw_writel(ccgr1, MXC_CCM_CCGR1);
+ __raw_writel(ccgr2, MXC_CCM_CCGR2);
+ __raw_writel(ccgr3, MXC_CCM_CCGR3);
+ __raw_writel(ccgr6, MXC_CCM_CCGR6);
+ __raw_writel(ccm_analog_pfd528, PFD_528_BASE_ADDR);
+ __raw_writel(ccm_analog_pll3_480, PLL3_480_USB1_BASE_ADDR);
+ __raw_writel(ccm_anadig_ana_misc2, MXC_PLL_BASE + HW_ANADIG_ANA_MISC2);
+}
+
+static int mx6_suspend_enter(suspend_state_t state)
+{
+ unsigned int wake_irq_isr[4];
+ unsigned int cpu_type;
+ struct gic_dist_state gds;
+ struct gic_cpu_state gcs;
+ bool arm_pg = false;
+
+ if (cpu_is_mx6q())
+ cpu_type = MXC_CPU_MX6Q;
+ else if (cpu_is_mx6dl())
+ cpu_type = MXC_CPU_MX6DL;
+ else
+ cpu_type = MXC_CPU_MX6SL;
+
+ wake_irq_isr[0] = __raw_readl(gpc_base +
+ GPC_ISR1_OFFSET) & gpc_wake_irq[0];
+ wake_irq_isr[1] = __raw_readl(gpc_base +
+ GPC_ISR2_OFFSET) & gpc_wake_irq[1];
+ wake_irq_isr[2] = __raw_readl(gpc_base +
+ GPC_ISR3_OFFSET) & gpc_wake_irq[2];
+ wake_irq_isr[3] = __raw_readl(gpc_base +
+ GPC_ISR4_OFFSET) & gpc_wake_irq[3];
+ if (wake_irq_isr[0] | wake_irq_isr[1] |
+ wake_irq_isr[2] | wake_irq_isr[3]) {
+ printk(KERN_INFO "There are wakeup irq pending,system resume!\n");
+ printk(KERN_INFO "wake_irq_isr[0-3]: 0x%x, 0x%x, 0x%x, 0x%x\n",
+ wake_irq_isr[0], wake_irq_isr[1],
+ wake_irq_isr[2], wake_irq_isr[3]);
+ return 0;
+ }
+ mx6_suspend_store();
+
+ /*
+ * i.MX6dl TO1.0/i.MX6dq TO1.1/1.0 TKT094231: can't support
+ * ARM_POWER_OFF mode.
+ */
+ if (state == PM_SUSPEND_MEM &&
+ ((mx6dl_revision() == IMX_CHIP_REVISION_1_0) ||
+ (cpu_is_mx6q() && mx6q_revision() <= IMX_CHIP_REVISION_1_1))) {
+ state = PM_SUSPEND_STANDBY;
+ }
+
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ disp_power_down();
+ usb_power_down_handler();
+#ifndef CONFIG_MXC_GPU_VIV
+ pu_disable(NULL);
+#endif
+ mxc_cpu_lp_set(ARM_POWER_OFF);
+ arm_pg = true;
+ break;
+ case PM_SUSPEND_STANDBY:
+ if (cpu_is_mx6sl()) {
+ disp_power_down();
+ usb_power_down_handler();
+#ifndef CONFIG_MXC_GPU_VIV
+ pu_disable(NULL);
+#endif
+ mxc_cpu_lp_set(STOP_XTAL_ON);
+ arm_pg = true;
+ } else
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * L2 can exit by 'reset' or Inband beacon (from remote EP)
+ * toggling phy_powerdown has same effect as 'inband beacon'
+ * So, toggle bit18 of GPR1, to fix errata
+ * "PCIe PCIe does not support L2 Power Down"
+ */
+ __raw_writel(__raw_readl(IOMUXC_GPR1) | (1 << 18), IOMUXC_GPR1);
+
+ if (state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY) {
+ u32 ttbr1;
+
+ flush_cache_all();
+
+ if (arm_pg) {
+ /* preserve gic state */
+ save_gic_dist_state(0, &gds);
+ save_gic_cpu_state(0, &gcs);
+ }
+
+ if (pm_data && pm_data->suspend_enter)
+ pm_data->suspend_enter();
+
+ ttbr1 = save_ttbr1();
+ suspend_in_iram(state, (unsigned long)suspend_iram_phys_addr,
+ (unsigned long)suspend_iram_base, cpu_type);
+ restore_ttbr1(ttbr1);
+
+ if (pm_data && pm_data->suspend_exit)
+ pm_data->suspend_exit();
+
+ /* Reset the RBC counter. */
+ /* All interrupts should be masked before the
+ * RBC counter is reset.
+ */
+ /* Mask all interrupts. These will be unmasked by
+ * the mx6_suspend_restore routine below.
+ */
+ __raw_writel(0xffffffff, gpc_base + 0x08);
+ __raw_writel(0xffffffff, gpc_base + 0x0c);
+ __raw_writel(0xffffffff, gpc_base + 0x10);
+ __raw_writel(0xffffffff, gpc_base + 0x14);
+
+ /* Clear the RBC counter and RBC_EN bit. */
+ /* Disable the REG_BYPASS_COUNTER. */
+ __raw_writel(__raw_readl(MXC_CCM_CCR) &
+ ~MXC_CCM_CCR_RBC_EN, MXC_CCM_CCR);
+ /* Make sure we clear REG_BYPASS_COUNT*/
+ __raw_writel(__raw_readl(MXC_CCM_CCR) &
+ (~MXC_CCM_CCR_REG_BYPASS_CNT_MASK), MXC_CCM_CCR);
+ /* Need to wait for a minimum of 2 CLKILS (32KHz) for the
+ * counter to clear and reset.
+ */
+ udelay(80);
+
+ if (arm_pg) {
+ /* restore gic registers */
+ restore_gic_dist_state(0, &gds);
+ restore_gic_cpu_state(0, &gcs);
+ }
+ if (state == PM_SUSPEND_MEM || (cpu_is_mx6sl())) {
+ usb_power_up_handler();
+ disp_power_up();
+ }
+
+ mx6_suspend_restore();
+
+ __raw_writel(BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG,
+ anatop_base + HW_ANADIG_ANA_MISC0_CLR);
+
+ } else {
+ cpu_do_idle();
+ }
+
+ /*
+ * L2 can exit by 'reset' or Inband beacon (from remote EP)
+ * toggling phy_powerdown has same effect as 'inband beacon'
+ * So, toggle bit18 of GPR1, to fix errata
+ * "PCIe PCIe does not support L2 Power Down"
+ */
+ __raw_writel(__raw_readl(IOMUXC_GPR1) & (~(1 << 18)), IOMUXC_GPR1);
+
+ return 0;
+}
+
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx6_suspend_prepare(void)
+{
+ int ret;
+ ret = regulator_disable(vdd3p0_regulator);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to disable 3p0 regulator Err: %d\n",
+ __func__, ret);
+ }
+ return 0;
+}
+
+/*
+ * Called before devices are re-setup.
+ */
+static void mx6_suspend_finish(void)
+{
+ int ret;
+ ret = regulator_enable(vdd3p0_regulator);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to enable 3p0 regulator Err: %d\n",
+ __func__, ret);
+ }
+}
+
+static int mx6_suspend_begin(suspend_state_t state)
+{
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+
+static int mx6_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx6_suspend_ops = {
+ .valid = mx6_pm_valid,
+ .begin = mx6_suspend_begin,
+ .prepare = mx6_suspend_prepare,
+ .enter = mx6_suspend_enter,
+ .finish = mx6_suspend_finish,
+};
+
+static int __devinit mx6_pm_probe(struct platform_device *pdev)
+{
+ pm_dev = &pdev->dev;
+ pm_data = pdev->dev.platform_data;
+
+ return 0;
+}
+
+static struct platform_driver mx6_pm_driver = {
+ .driver = {
+ .name = "imx_pm",
+ },
+ .probe = mx6_pm_probe,
+};
+
+static int __init pm_init(void)
+{
+ int ret = 0;
+ scu_base = IO_ADDRESS(SCU_BASE_ADDR);
+ gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+ src_base = IO_ADDRESS(SRC_BASE_ADDR);
+ gic_dist_base = IO_ADDRESS(IC_DISTRIBUTOR_BASE_ADDR);
+ gic_cpu_base = IO_ADDRESS(IC_INTERFACES_BASE_ADDR);
+ local_twd_base = IO_ADDRESS(LOCAL_TWD_ADDR);
+ anatop_base = IO_ADDRESS(ANATOP_BASE_ADDR);
+
+ pr_info("Static Power Management for Freescale i.MX6\n");
+
+ pr_info("wait mode is %s for i.MX6\n", enable_wait_mode ?
+ "enabled" : "disabled");
+
+ if (platform_driver_register(&mx6_pm_driver) != 0) {
+ printk(KERN_ERR "mx6_pm_driver register failed\n");
+ return -ENODEV;
+ }
+
+ suspend_set_ops(&mx6_suspend_ops);
+ /* Use preallocated IRAM memory. */
+ suspend_iram_phys_addr = MX6_SUSPEND_IRAM_CODE;
+
+ /* Dont ioremap the address, we have fixed the IRAM address at IRAM_BASE_ADDR_VIRT */
+ suspend_iram_base = IRAM_BASE_ADDR_VIRT + (suspend_iram_phys_addr - IRAM_BASE_ADDR);
+
+ pr_info("cpaddr = %x suspend_iram_base=%x\n",
+ (unsigned int)cpaddr, (unsigned int)suspend_iram_base);
+
+ /*
+ * Need to run the suspend code from IRAM as the DDR needs
+ * to be put into low power mode manually.
+ */
+ memcpy((void *)suspend_iram_base, mx6_suspend, MX6_SUSPEND_CODE_SIZE);
+
+ suspend_in_iram = (void *)suspend_iram_base;
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+ axi_clk = clk_get(NULL, "axi_clk");
+ if (IS_ERR(axi_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_clk\n", __func__);
+ return PTR_ERR(axi_clk);
+ }
+ periph_clk = clk_get(NULL, "periph_clk");
+ if (IS_ERR(periph_clk)) {
+ printk(KERN_DEBUG "%s: failed to get periph_clk\n", __func__);
+ return PTR_ERR(periph_clk);
+ }
+ pll3_usb_otg_main_clk = clk_get(NULL, "pll3_main_clk");
+ if (IS_ERR(pll3_usb_otg_main_clk)) {
+ printk(KERN_DEBUG "%s: failed to get pll3_main_clk\n", __func__);
+ return PTR_ERR(pll3_usb_otg_main_clk);
+ }
+
+ vdd3p0_regulator = regulator_get(NULL, "cpu_vdd3p0");
+ if (IS_ERR(vdd3p0_regulator)) {
+ printk(KERN_ERR "%s: failed to get 3p0 regulator Err: %d\n",
+ __func__, ret);
+ return PTR_ERR(vdd3p0_regulator);
+ }
+ ret = regulator_set_voltage(vdd3p0_regulator, VDD3P0_VOLTAGE,
+ VDD3P0_VOLTAGE);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to set 3p0 regulator voltage Err: %d\n",
+ __func__, ret);
+ }
+ ret = regulator_enable(vdd3p0_regulator);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to enable 3p0 regulator Err: %d\n",
+ __func__, ret);
+ }
+
+ printk(KERN_INFO "PM driver module loaded\n");
+
+ return 0;
+}
+
+static void __exit pm_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mx6_pm_driver);
+ regulator_put(vdd3p0_regulator);
+}
+
+module_init(pm_init);
+module_exit(pm_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("PM driver");
+MODULE_LICENSE("GPL");
+