linux/arch/arm/mach-mmp/pm-mmp2.c
<<
>>
Prefs
   1/*
   2 * MMP2 Power Management Routines
   3 *
   4 * This software program is licensed subject to the GNU General Public License
   5 * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
   6 *
   7 * (C) Copyright 2012 Marvell International Ltd.
   8 * All Rights Reserved
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/errno.h>
  13#include <linux/err.h>
  14#include <linux/time.h>
  15#include <linux/delay.h>
  16#include <linux/suspend.h>
  17#include <linux/irq.h>
  18#include <linux/io.h>
  19#include <linux/interrupt.h>
  20#include <asm/mach-types.h>
  21#include <mach/hardware.h>
  22#include <mach/cputype.h>
  23#include <mach/addr-map.h>
  24#include <mach/pm-mmp2.h>
  25#include <mach/regs-icu.h>
  26#include <mach/irqs.h>
  27
  28int mmp2_set_wake(struct irq_data *d, unsigned int on)
  29{
  30        int irq = d->irq;
  31        struct irq_desc *desc = irq_to_desc(irq);
  32        unsigned long data = 0;
  33
  34        if (unlikely(irq >= nr_irqs)) {
  35                pr_err("IRQ nubmers are out of boundary!\n");
  36                return -EINVAL;
  37        }
  38
  39        if (on) {
  40                if (desc->action)
  41                        desc->action->flags |= IRQF_NO_SUSPEND;
  42        } else {
  43                if (desc->action)
  44                        desc->action->flags &= ~IRQF_NO_SUSPEND;
  45        }
  46
  47        /* enable wakeup sources */
  48        switch (irq) {
  49        case IRQ_MMP2_RTC:
  50        case IRQ_MMP2_RTC_ALARM:
  51                data = MPMU_WUCRM_PJ_WAKEUP(4) | MPMU_WUCRM_PJ_RTC_ALARM;
  52                break;
  53        case IRQ_MMP2_PMIC:
  54                data = MPMU_WUCRM_PJ_WAKEUP(7);
  55                break;
  56        case IRQ_MMP2_MMC2:
  57                /* mmc use WAKEUP2, same as GPIO wakeup source */
  58                data = MPMU_WUCRM_PJ_WAKEUP(2);
  59                break;
  60        }
  61        if (on) {
  62                if (data) {
  63                        data |= __raw_readl(MPMU_WUCRM_PJ);
  64                        __raw_writel(data, MPMU_WUCRM_PJ);
  65                }
  66        } else {
  67                if (data) {
  68                        data = ~data & __raw_readl(MPMU_WUCRM_PJ);
  69                        __raw_writel(data, MPMU_WUCRM_PJ);
  70                }
  71        }
  72        return 0;
  73}
  74
  75static void pm_scu_clk_disable(void)
  76{
  77        unsigned int val;
  78
  79        /* close AXI fabric clock gate */
  80        __raw_writel(0x0, CIU_REG(0x64));
  81        __raw_writel(0x0, CIU_REG(0x68));
  82
  83        /* close MCB master clock gate */
  84        val = __raw_readl(CIU_REG(0x1c));
  85        val |= 0xf0;
  86        __raw_writel(val, CIU_REG(0x1c));
  87
  88        return ;
  89}
  90
  91static void pm_scu_clk_enable(void)
  92{
  93        unsigned int val;
  94
  95        /* open AXI fabric clock gate */
  96        __raw_writel(0x03003003, CIU_REG(0x64));
  97        __raw_writel(0x00303030, CIU_REG(0x68));
  98
  99        /* open MCB master clock gate */
 100        val = __raw_readl(CIU_REG(0x1c));
 101        val &= ~(0xf0);
 102        __raw_writel(val, CIU_REG(0x1c));
 103
 104        return ;
 105}
 106
 107static void pm_mpmu_clk_disable(void)
 108{
 109        /*
 110         * disable clocks in MPMU_CGR_PJ register
 111         * except clock for APMU_PLL1, APMU_PLL1_2 and AP_26M
 112         */
 113        __raw_writel(0x0000a010, MPMU_CGR_PJ);
 114}
 115
 116static void pm_mpmu_clk_enable(void)
 117{
 118        unsigned int val;
 119
 120        __raw_writel(0xdffefffe, MPMU_CGR_PJ);
 121        val = __raw_readl(MPMU_PLL2_CTRL1);
 122        val |= (1 << 29);
 123        __raw_writel(val, MPMU_PLL2_CTRL1);
 124
 125        return ;
 126}
 127
 128void mmp2_pm_enter_lowpower_mode(int state)
 129{
 130        uint32_t idle_cfg, apcr;
 131
 132        idle_cfg = __raw_readl(APMU_PJ_IDLE_CFG);
 133        apcr = __raw_readl(MPMU_PCR_PJ);
 134        apcr &= ~(MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD | MPMU_PCR_PJ_APBSD
 135                 | MPMU_PCR_PJ_AXISD | MPMU_PCR_PJ_VCTCXOSD | (1 << 13));
 136        idle_cfg &= ~APMU_PJ_IDLE_CFG_PJ_IDLE;
 137
 138        switch (state) {
 139        case POWER_MODE_SYS_SLEEP:
 140                apcr |= MPMU_PCR_PJ_SLPEN;              /* set the SLPEN bit */
 141                apcr |= MPMU_PCR_PJ_VCTCXOSD;           /* set VCTCXOSD */
 142                /* fall through */
 143        case POWER_MODE_CHIP_SLEEP:
 144                apcr |= MPMU_PCR_PJ_SLPEN;
 145                /* fall through */
 146        case POWER_MODE_APPS_SLEEP:
 147                apcr |= MPMU_PCR_PJ_APBSD;              /* set APBSD */
 148                /* fall through */
 149        case POWER_MODE_APPS_IDLE:
 150                apcr |= MPMU_PCR_PJ_AXISD;              /* set AXISDD bit */
 151                apcr |= MPMU_PCR_PJ_DDRCORSD;           /* set DDRCORSD bit */
 152                idle_cfg |= APMU_PJ_IDLE_CFG_PJ_PWRDWN; /* PJ power down */
 153                apcr |= MPMU_PCR_PJ_SPSD;
 154                /* fall through */
 155        case POWER_MODE_CORE_EXTIDLE:
 156                idle_cfg |= APMU_PJ_IDLE_CFG_PJ_IDLE;   /* set the IDLE bit */
 157                idle_cfg &= ~APMU_PJ_IDLE_CFG_ISO_MODE_CNTRL_MASK;
 158                idle_cfg |= APMU_PJ_IDLE_CFG_PWR_SW(3)
 159                        | APMU_PJ_IDLE_CFG_L2_PWR_SW;
 160                break;
 161        case POWER_MODE_CORE_INTIDLE:
 162                apcr &= ~MPMU_PCR_PJ_SPSD;
 163                break;
 164        }
 165
 166        /* set reserve bits */
 167        apcr |= (1 << 30) | (1 << 25);
 168
 169        /* finally write the registers back */
 170        __raw_writel(idle_cfg, APMU_PJ_IDLE_CFG);
 171        __raw_writel(apcr, MPMU_PCR_PJ);        /* 0xfe086000 */
 172}
 173
 174static int mmp2_pm_enter(suspend_state_t state)
 175{
 176        int temp;
 177
 178        temp = __raw_readl(MMP2_ICU_INT4_MASK);
 179        if (temp & (1 << 1)) {
 180                printk(KERN_ERR "%s: PMIC interrupt is handling\n", __func__);
 181                return -EAGAIN;
 182        }
 183
 184        temp = __raw_readl(APMU_SRAM_PWR_DWN);
 185        temp |= ((1 << 19) | (1 << 18));
 186        __raw_writel(temp, APMU_SRAM_PWR_DWN);
 187        pm_mpmu_clk_disable();
 188        pm_scu_clk_disable();
 189
 190        printk(KERN_INFO "%s: before suspend\n", __func__);
 191        cpu_do_idle();
 192        printk(KERN_INFO "%s: after suspend\n", __func__);
 193
 194        pm_mpmu_clk_enable();           /* enable clocks in MPMU */
 195        pm_scu_clk_enable();            /* enable clocks in SCU */
 196
 197        return 0;
 198}
 199
 200/*
 201 * Called after processes are frozen, but before we shut down devices.
 202 */
 203static int mmp2_pm_prepare(void)
 204{
 205        mmp2_pm_enter_lowpower_mode(POWER_MODE_SYS_SLEEP);
 206
 207        return 0;
 208}
 209
 210/*
 211 * Called after devices are re-setup, but before processes are thawed.
 212 */
 213static void mmp2_pm_finish(void)
 214{
 215        mmp2_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE);
 216}
 217
 218static int mmp2_pm_valid(suspend_state_t state)
 219{
 220        return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM));
 221}
 222
 223/*
 224 * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
 225 */
 226static const struct platform_suspend_ops mmp2_pm_ops = {
 227        .valid          = mmp2_pm_valid,
 228        .prepare        = mmp2_pm_prepare,
 229        .enter          = mmp2_pm_enter,
 230        .finish         = mmp2_pm_finish,
 231};
 232
 233static int __init mmp2_pm_init(void)
 234{
 235        uint32_t apcr;
 236
 237        if (!cpu_is_mmp2())
 238                return -EIO;
 239
 240        suspend_set_ops(&mmp2_pm_ops);
 241
 242        /*
 243         * Set bit 0, Slow clock Select 32K clock input instead of VCXO
 244         * VCXO is chosen by default, which would be disabled in suspend
 245         */
 246        __raw_writel(0x5, MPMU_SCCR);
 247
 248        /*
 249         * Clear bit 23 of CIU_CPU_CONF
 250         * direct PJ4 to DDR access through Memory Controller slow queue
 251         * fast queue has issue and cause lcd will flick
 252         */
 253        __raw_writel(__raw_readl(CIU_REG(0x8)) & ~(0x1 << 23), CIU_REG(0x8));
 254
 255        /* Clear default low power control bit */
 256        apcr = __raw_readl(MPMU_PCR_PJ);
 257        apcr &= ~(MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD
 258                        | MPMU_PCR_PJ_APBSD | MPMU_PCR_PJ_AXISD | 1 << 13);
 259        __raw_writel(apcr, MPMU_PCR_PJ);
 260
 261        return 0;
 262}
 263
 264late_initcall(mmp2_pm_init);
 265