linux/arch/arm/mach-davinci/pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * DaVinci Power Management Routines
   4 *
   5 * Copyright (C) 2009 Texas Instruments, Inc. https://www.ti.com/
   6 */
   7
   8#include <linux/pm.h>
   9#include <linux/suspend.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/clk.h>
  13#include <linux/spinlock.h>
  14
  15#include <asm/cacheflush.h>
  16#include <asm/delay.h>
  17#include <asm/io.h>
  18
  19#include <mach/common.h>
  20#include <mach/da8xx.h>
  21#include <mach/mux.h>
  22#include <mach/pm.h>
  23
  24#include "clock.h"
  25#include "psc.h"
  26#include "sram.h"
  27
  28#define DA850_PLL1_BASE         0x01e1a000
  29#define DEEPSLEEP_SLEEPCOUNT_MASK       0xFFFF
  30#define DEEPSLEEP_SLEEPCOUNT            128
  31
  32static void (*davinci_sram_suspend) (struct davinci_pm_config *);
  33static struct davinci_pm_config pm_config = {
  34        .sleepcount = DEEPSLEEP_SLEEPCOUNT,
  35        .ddrpsc_num = DA8XX_LPSC1_EMIF3C,
  36};
  37
  38static void davinci_sram_push(void *dest, void *src, unsigned int size)
  39{
  40        memcpy(dest, src, size);
  41        flush_icache_range((unsigned long)dest, (unsigned long)(dest + size));
  42}
  43
  44static void davinci_pm_suspend(void)
  45{
  46        unsigned val;
  47
  48        if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
  49
  50                /* Switch CPU PLL to bypass mode */
  51                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  52                val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
  53                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
  54
  55                udelay(PLL_BYPASS_TIME);
  56
  57                /* Powerdown CPU PLL */
  58                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  59                val |= PLLCTL_PLLPWRDN;
  60                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
  61        }
  62
  63        /* Configure sleep count in deep sleep register */
  64        val = __raw_readl(pm_config.deepsleep_reg);
  65        val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
  66        val |= pm_config.sleepcount;
  67        __raw_writel(val, pm_config.deepsleep_reg);
  68
  69        /* System goes to sleep in this call */
  70        davinci_sram_suspend(&pm_config);
  71
  72        if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
  73
  74                /* put CPU PLL in reset */
  75                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  76                val &= ~PLLCTL_PLLRST;
  77                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
  78
  79                /* put CPU PLL in power down */
  80                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  81                val &= ~PLLCTL_PLLPWRDN;
  82                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
  83
  84                /* wait for CPU PLL reset */
  85                udelay(PLL_RESET_TIME);
  86
  87                /* bring CPU PLL out of reset */
  88                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  89                val |= PLLCTL_PLLRST;
  90                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
  91
  92                /* Wait for CPU PLL to lock */
  93                udelay(PLL_LOCK_TIME);
  94
  95                /* Remove CPU PLL from bypass mode */
  96                val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
  97                val &= ~PLLCTL_PLLENSRC;
  98                val |= PLLCTL_PLLEN;
  99                __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
 100        }
 101}
 102
 103static int davinci_pm_enter(suspend_state_t state)
 104{
 105        int ret = 0;
 106
 107        switch (state) {
 108        case PM_SUSPEND_MEM:
 109                davinci_pm_suspend();
 110                break;
 111        default:
 112                ret = -EINVAL;
 113        }
 114
 115        return ret;
 116}
 117
 118static const struct platform_suspend_ops davinci_pm_ops = {
 119        .enter          = davinci_pm_enter,
 120        .valid          = suspend_valid_only_mem,
 121};
 122
 123int __init davinci_pm_init(void)
 124{
 125        int ret;
 126
 127        ret = davinci_cfg_reg(DA850_RTC_ALARM);
 128        if (ret)
 129                return ret;
 130
 131        pm_config.ddr2_ctlr_base = da8xx_get_mem_ctlr();
 132        pm_config.deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG);
 133
 134        pm_config.cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K);
 135        if (!pm_config.cpupll_reg_base)
 136                return -ENOMEM;
 137
 138        pm_config.ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K);
 139        if (!pm_config.ddrpll_reg_base) {
 140                ret = -ENOMEM;
 141                goto no_ddrpll_mem;
 142        }
 143
 144        pm_config.ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K);
 145        if (!pm_config.ddrpsc_reg_base) {
 146                ret = -ENOMEM;
 147                goto no_ddrpsc_mem;
 148        }
 149
 150        davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
 151        if (!davinci_sram_suspend) {
 152                pr_err("PM: cannot allocate SRAM memory\n");
 153                ret = -ENOMEM;
 154                goto no_sram_mem;
 155        }
 156
 157        davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
 158                                                davinci_cpu_suspend_sz);
 159
 160        suspend_set_ops(&davinci_pm_ops);
 161
 162        return 0;
 163
 164no_sram_mem:
 165        iounmap(pm_config.ddrpsc_reg_base);
 166no_ddrpsc_mem:
 167        iounmap(pm_config.ddrpll_reg_base);
 168no_ddrpll_mem:
 169        iounmap(pm_config.cpupll_reg_base);
 170        return ret;
 171}
 172