1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2efc1bb8aSSekhar Nori /* 3efc1bb8aSSekhar Nori * DaVinci Power Management Routines 4efc1bb8aSSekhar Nori * 5fb01eb36SAlexander A. Klimov * Copyright (C) 2009 Texas Instruments, Inc. https://www.ti.com/ 6efc1bb8aSSekhar Nori */ 7efc1bb8aSSekhar Nori 8efc1bb8aSSekhar Nori #include <linux/pm.h> 9efc1bb8aSSekhar Nori #include <linux/suspend.h> 10efc1bb8aSSekhar Nori #include <linux/module.h> 11efc1bb8aSSekhar Nori #include <linux/platform_device.h> 12efc1bb8aSSekhar Nori #include <linux/clk.h> 13efc1bb8aSSekhar Nori #include <linux/spinlock.h> 14efc1bb8aSSekhar Nori 15efc1bb8aSSekhar Nori #include <asm/cacheflush.h> 16efc1bb8aSSekhar Nori #include <asm/delay.h> 17b7f080cfSAlexey Dobriyan #include <asm/io.h> 18efc1bb8aSSekhar Nori 19*ca31807bSArnd Bergmann #include "common.h" 20*ca31807bSArnd Bergmann #include "da8xx.h" 21*ca31807bSArnd Bergmann #include "mux.h" 22*ca31807bSArnd Bergmann #include "pm.h" 23efc1bb8aSSekhar Nori #include "clock.h" 24aa9aa1ecSKevin Hilman #include "psc.h" 25aa9aa1ecSKevin Hilman #include "sram.h" 26efc1bb8aSSekhar Nori 27aa9aa1ecSKevin Hilman #define DA850_PLL1_BASE 0x01e1a000 28efc1bb8aSSekhar Nori #define DEEPSLEEP_SLEEPCOUNT_MASK 0xFFFF 29aa9aa1ecSKevin Hilman #define DEEPSLEEP_SLEEPCOUNT 128 30efc1bb8aSSekhar Nori 31efc1bb8aSSekhar Nori static void (*davinci_sram_suspend) (struct davinci_pm_config *); 32aa9aa1ecSKevin Hilman static struct davinci_pm_config pm_config = { 33aa9aa1ecSKevin Hilman .sleepcount = DEEPSLEEP_SLEEPCOUNT, 34aa9aa1ecSKevin Hilman .ddrpsc_num = DA8XX_LPSC1_EMIF3C, 35aa9aa1ecSKevin Hilman }; 36aa9aa1ecSKevin Hilman 37efc1bb8aSSekhar Nori static void davinci_sram_push(void *dest, void *src, unsigned int size) 38efc1bb8aSSekhar Nori { 39efc1bb8aSSekhar Nori memcpy(dest, src, size); 40efc1bb8aSSekhar Nori flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); 41efc1bb8aSSekhar Nori } 42efc1bb8aSSekhar Nori 43efc1bb8aSSekhar Nori static void davinci_pm_suspend(void) 44efc1bb8aSSekhar Nori { 45efc1bb8aSSekhar Nori unsigned val; 46efc1bb8aSSekhar Nori 471428ed1aSKevin Hilman if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) { 48efc1bb8aSSekhar Nori 49efc1bb8aSSekhar Nori /* Switch CPU PLL to bypass mode */ 501428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 51efc1bb8aSSekhar Nori val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); 521428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 53efc1bb8aSSekhar Nori 54efc1bb8aSSekhar Nori udelay(PLL_BYPASS_TIME); 55efc1bb8aSSekhar Nori 56efc1bb8aSSekhar Nori /* Powerdown CPU PLL */ 571428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 58efc1bb8aSSekhar Nori val |= PLLCTL_PLLPWRDN; 591428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 60efc1bb8aSSekhar Nori } 61efc1bb8aSSekhar Nori 62efc1bb8aSSekhar Nori /* Configure sleep count in deep sleep register */ 631428ed1aSKevin Hilman val = __raw_readl(pm_config.deepsleep_reg); 64efc1bb8aSSekhar Nori val &= ~DEEPSLEEP_SLEEPCOUNT_MASK, 651428ed1aSKevin Hilman val |= pm_config.sleepcount; 661428ed1aSKevin Hilman __raw_writel(val, pm_config.deepsleep_reg); 67efc1bb8aSSekhar Nori 68efc1bb8aSSekhar Nori /* System goes to sleep in this call */ 691428ed1aSKevin Hilman davinci_sram_suspend(&pm_config); 70efc1bb8aSSekhar Nori 711428ed1aSKevin Hilman if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) { 72efc1bb8aSSekhar Nori 73efc1bb8aSSekhar Nori /* put CPU PLL in reset */ 741428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 75efc1bb8aSSekhar Nori val &= ~PLLCTL_PLLRST; 761428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 77efc1bb8aSSekhar Nori 78efc1bb8aSSekhar Nori /* put CPU PLL in power down */ 791428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 80efc1bb8aSSekhar Nori val &= ~PLLCTL_PLLPWRDN; 811428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 82efc1bb8aSSekhar Nori 83efc1bb8aSSekhar Nori /* wait for CPU PLL reset */ 84efc1bb8aSSekhar Nori udelay(PLL_RESET_TIME); 85efc1bb8aSSekhar Nori 86efc1bb8aSSekhar Nori /* bring CPU PLL out of reset */ 871428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 88efc1bb8aSSekhar Nori val |= PLLCTL_PLLRST; 891428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 90efc1bb8aSSekhar Nori 91efc1bb8aSSekhar Nori /* Wait for CPU PLL to lock */ 92efc1bb8aSSekhar Nori udelay(PLL_LOCK_TIME); 93efc1bb8aSSekhar Nori 94efc1bb8aSSekhar Nori /* Remove CPU PLL from bypass mode */ 951428ed1aSKevin Hilman val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL); 96efc1bb8aSSekhar Nori val &= ~PLLCTL_PLLENSRC; 97efc1bb8aSSekhar Nori val |= PLLCTL_PLLEN; 981428ed1aSKevin Hilman __raw_writel(val, pm_config.cpupll_reg_base + PLLCTL); 99efc1bb8aSSekhar Nori } 100efc1bb8aSSekhar Nori } 101efc1bb8aSSekhar Nori 102efc1bb8aSSekhar Nori static int davinci_pm_enter(suspend_state_t state) 103efc1bb8aSSekhar Nori { 104efc1bb8aSSekhar Nori int ret = 0; 105efc1bb8aSSekhar Nori 106efc1bb8aSSekhar Nori switch (state) { 107efc1bb8aSSekhar Nori case PM_SUSPEND_MEM: 108efc1bb8aSSekhar Nori davinci_pm_suspend(); 109efc1bb8aSSekhar Nori break; 110efc1bb8aSSekhar Nori default: 111efc1bb8aSSekhar Nori ret = -EINVAL; 112efc1bb8aSSekhar Nori } 113efc1bb8aSSekhar Nori 114efc1bb8aSSekhar Nori return ret; 115efc1bb8aSSekhar Nori } 116efc1bb8aSSekhar Nori 1172f55ac07SLionel Debroux static const struct platform_suspend_ops davinci_pm_ops = { 118efc1bb8aSSekhar Nori .enter = davinci_pm_enter, 119efc1bb8aSSekhar Nori .valid = suspend_valid_only_mem, 120efc1bb8aSSekhar Nori }; 121efc1bb8aSSekhar Nori 122aa9aa1ecSKevin Hilman int __init davinci_pm_init(void) 123efc1bb8aSSekhar Nori { 124aa9aa1ecSKevin Hilman int ret; 125aa9aa1ecSKevin Hilman 126aa9aa1ecSKevin Hilman ret = davinci_cfg_reg(DA850_RTC_ALARM); 127aa9aa1ecSKevin Hilman if (ret) 128aa9aa1ecSKevin Hilman return ret; 129aa9aa1ecSKevin Hilman 1301428ed1aSKevin Hilman pm_config.ddr2_ctlr_base = da8xx_get_mem_ctlr(); 1311428ed1aSKevin Hilman pm_config.deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG); 132aa9aa1ecSKevin Hilman 1331428ed1aSKevin Hilman pm_config.cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K); 1341428ed1aSKevin Hilman if (!pm_config.cpupll_reg_base) 135aa9aa1ecSKevin Hilman return -ENOMEM; 136aa9aa1ecSKevin Hilman 1371428ed1aSKevin Hilman pm_config.ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K); 1381428ed1aSKevin Hilman if (!pm_config.ddrpll_reg_base) { 139aa9aa1ecSKevin Hilman ret = -ENOMEM; 140aa9aa1ecSKevin Hilman goto no_ddrpll_mem; 141aa9aa1ecSKevin Hilman } 142aa9aa1ecSKevin Hilman 1431428ed1aSKevin Hilman pm_config.ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K); 1441428ed1aSKevin Hilman if (!pm_config.ddrpsc_reg_base) { 145aa9aa1ecSKevin Hilman ret = -ENOMEM; 146aa9aa1ecSKevin Hilman goto no_ddrpsc_mem; 147efc1bb8aSSekhar Nori } 148efc1bb8aSSekhar Nori 149efc1bb8aSSekhar Nori davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); 150efc1bb8aSSekhar Nori if (!davinci_sram_suspend) { 151aa9aa1ecSKevin Hilman pr_err("PM: cannot allocate SRAM memory\n"); 152f3f6cc81SChristophe JAILLET ret = -ENOMEM; 153f3f6cc81SChristophe JAILLET goto no_sram_mem; 154efc1bb8aSSekhar Nori } 155efc1bb8aSSekhar Nori 156efc1bb8aSSekhar Nori davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, 157efc1bb8aSSekhar Nori davinci_cpu_suspend_sz); 158efc1bb8aSSekhar Nori 159efc1bb8aSSekhar Nori suspend_set_ops(&davinci_pm_ops); 160efc1bb8aSSekhar Nori 16195d7c1f1SChristophe JAILLET return 0; 16295d7c1f1SChristophe JAILLET 163f3f6cc81SChristophe JAILLET no_sram_mem: 164f3f6cc81SChristophe JAILLET iounmap(pm_config.ddrpsc_reg_base); 165aa9aa1ecSKevin Hilman no_ddrpsc_mem: 1661428ed1aSKevin Hilman iounmap(pm_config.ddrpll_reg_base); 167aa9aa1ecSKevin Hilman no_ddrpll_mem: 1681428ed1aSKevin Hilman iounmap(pm_config.cpupll_reg_base); 169aa9aa1ecSKevin Hilman return ret; 170efc1bb8aSSekhar Nori } 171