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