18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * DaVinci Power Management Routines
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc. https://www.ti.com/
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/pm.h>
98c2ecf20Sopenharmony_ci#include <linux/suspend.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
168c2ecf20Sopenharmony_ci#include <asm/delay.h>
178c2ecf20Sopenharmony_ci#include <asm/io.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <mach/common.h>
208c2ecf20Sopenharmony_ci#include <mach/da8xx.h>
218c2ecf20Sopenharmony_ci#include <mach/mux.h>
228c2ecf20Sopenharmony_ci#include <mach/pm.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "clock.h"
258c2ecf20Sopenharmony_ci#include "psc.h"
268c2ecf20Sopenharmony_ci#include "sram.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define DA850_PLL1_BASE		0x01e1a000
298c2ecf20Sopenharmony_ci#define DEEPSLEEP_SLEEPCOUNT_MASK	0xFFFF
308c2ecf20Sopenharmony_ci#define DEEPSLEEP_SLEEPCOUNT		128
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void (*davinci_sram_suspend) (struct davinci_pm_config *);
338c2ecf20Sopenharmony_cistatic struct davinci_pm_config pm_config = {
348c2ecf20Sopenharmony_ci	.sleepcount = DEEPSLEEP_SLEEPCOUNT,
358c2ecf20Sopenharmony_ci	.ddrpsc_num = DA8XX_LPSC1_EMIF3C,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void davinci_sram_push(void *dest, void *src, unsigned int size)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	memcpy(dest, src, size);
418c2ecf20Sopenharmony_ci	flush_icache_range((unsigned long)dest, (unsigned long)(dest + size));
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void davinci_pm_suspend(void)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	unsigned val;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		/* Switch CPU PLL to bypass mode */
518c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
528c2ecf20Sopenharmony_ci		val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
538c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		udelay(PLL_BYPASS_TIME);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		/* Powerdown CPU PLL */
588c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
598c2ecf20Sopenharmony_ci		val |= PLLCTL_PLLPWRDN;
608c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* Configure sleep count in deep sleep register */
648c2ecf20Sopenharmony_ci	val = __raw_readl(pm_config.deepsleep_reg);
658c2ecf20Sopenharmony_ci	val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
668c2ecf20Sopenharmony_ci	val |= pm_config.sleepcount;
678c2ecf20Sopenharmony_ci	__raw_writel(val, pm_config.deepsleep_reg);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* System goes to sleep in this call */
708c2ecf20Sopenharmony_ci	davinci_sram_suspend(&pm_config);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		/* put CPU PLL in reset */
758c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
768c2ecf20Sopenharmony_ci		val &= ~PLLCTL_PLLRST;
778c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		/* put CPU PLL in power down */
808c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
818c2ecf20Sopenharmony_ci		val &= ~PLLCTL_PLLPWRDN;
828c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		/* wait for CPU PLL reset */
858c2ecf20Sopenharmony_ci		udelay(PLL_RESET_TIME);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		/* bring CPU PLL out of reset */
888c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
898c2ecf20Sopenharmony_ci		val |= PLLCTL_PLLRST;
908c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		/* Wait for CPU PLL to lock */
938c2ecf20Sopenharmony_ci		udelay(PLL_LOCK_TIME);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		/* Remove CPU PLL from bypass mode */
968c2ecf20Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
978c2ecf20Sopenharmony_ci		val &= ~PLLCTL_PLLENSRC;
988c2ecf20Sopenharmony_ci		val |= PLLCTL_PLLEN;
998c2ecf20Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int davinci_pm_enter(suspend_state_t state)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int ret = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	switch (state) {
1088c2ecf20Sopenharmony_ci	case PM_SUSPEND_MEM:
1098c2ecf20Sopenharmony_ci		davinci_pm_suspend();
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		ret = -EINVAL;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return ret;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops davinci_pm_ops = {
1198c2ecf20Sopenharmony_ci	.enter		= davinci_pm_enter,
1208c2ecf20Sopenharmony_ci	.valid		= suspend_valid_only_mem,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciint __init davinci_pm_init(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int ret;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	ret = davinci_cfg_reg(DA850_RTC_ALARM);
1288c2ecf20Sopenharmony_ci	if (ret)
1298c2ecf20Sopenharmony_ci		return ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	pm_config.ddr2_ctlr_base = da8xx_get_mem_ctlr();
1328c2ecf20Sopenharmony_ci	pm_config.deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	pm_config.cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K);
1358c2ecf20Sopenharmony_ci	if (!pm_config.cpupll_reg_base)
1368c2ecf20Sopenharmony_ci		return -ENOMEM;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	pm_config.ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K);
1398c2ecf20Sopenharmony_ci	if (!pm_config.ddrpll_reg_base) {
1408c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1418c2ecf20Sopenharmony_ci		goto no_ddrpll_mem;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	pm_config.ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K);
1458c2ecf20Sopenharmony_ci	if (!pm_config.ddrpsc_reg_base) {
1468c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1478c2ecf20Sopenharmony_ci		goto no_ddrpsc_mem;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
1518c2ecf20Sopenharmony_ci	if (!davinci_sram_suspend) {
1528c2ecf20Sopenharmony_ci		pr_err("PM: cannot allocate SRAM memory\n");
1538c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1548c2ecf20Sopenharmony_ci		goto no_sram_mem;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
1588c2ecf20Sopenharmony_ci						davinci_cpu_suspend_sz);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	suspend_set_ops(&davinci_pm_ops);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cino_sram_mem:
1658c2ecf20Sopenharmony_ci	iounmap(pm_config.ddrpsc_reg_base);
1668c2ecf20Sopenharmony_cino_ddrpsc_mem:
1678c2ecf20Sopenharmony_ci	iounmap(pm_config.ddrpll_reg_base);
1688c2ecf20Sopenharmony_cino_ddrpll_mem:
1698c2ecf20Sopenharmony_ci	iounmap(pm_config.cpupll_reg_base);
1708c2ecf20Sopenharmony_ci	return ret;
1718c2ecf20Sopenharmony_ci}
172