/* * Copyright (C) 2011 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 #include #include #include #include #include #include #include #include #include #include "crm_regs.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 ANATOP_REG_2P5_OFFSET 0x130 #define ANATOP_REG_CORE_OFFSET 0x140 #define MODULE_CLKGATE (1 << 30) #define MODULE_SFTRST (1 << 31) /* static DEFINE_SPINLOCK(wfi_lock); */ extern unsigned int gpc_wake_irq[4]; /* static unsigned int cpu_idle_mask; */ static void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR); extern void (*mx6_wait_in_iram)(void *ccm_base); extern void mx6_wait(void); extern void *mx6_wait_in_iram_base; extern bool enable_wait_mode; 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; } /* 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); 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; 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; 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; ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 2; } 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; } if (stop_mode > 0) { gpc_set_wakeup(gpc_wake_irq); /* Power down and power up sequence */ __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET); __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET); /* dormant mode, need to power off the arm core */ if (stop_mode == 2) { __raw_writel(0x1, gpc_base + GPC_PGC_CPU_PDN_OFFSET); __raw_writel(0x1, gpc_base + GPC_PGC_GPU_PGCR_OFFSET); __raw_writel(0x1, gpc_base + GPC_CNTR_OFFSET); /* Enable weak 2P5 linear regulator */ anatop_val = __raw_readl(anatop_base + ANATOP_REG_2P5_OFFSET); anatop_val |= 1 << 18; __raw_writel(anatop_val, anatop_base + ANATOP_REG_2P5_OFFSET); /* Set ARM core power domain to 1V and PU domain set to off */ anatop_val = __raw_readl(anatop_base + ANATOP_REG_CORE_OFFSET); anatop_val &= 0xfffc0000; anatop_val |= 0xc; __raw_writel(anatop_val, anatop_base + ANATOP_REG_CORE_OFFSET); __raw_writel(__raw_readl(MXC_CCM_CCR) | MXC_CCM_CCR_RBC_EN, MXC_CCM_CCR); ccm_clpcr |= MXC_CCM_CLPCR_WB_PER_AT_LPM; } } __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); } void arch_idle(void) { if (enable_wait_mode) { if ((num_online_cpus() == num_present_cpus()) && mx6_wait_in_iram != NULL) { mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); mx6_wait_in_iram(MXC_CCM_BASE); } } else cpu_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; } 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; }