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