162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * DaVinci Power Management Routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc. https://www.ti.com/
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/pm.h>
962306a36Sopenharmony_ci#include <linux/suspend.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/cacheflush.h>
1662306a36Sopenharmony_ci#include <asm/delay.h>
1762306a36Sopenharmony_ci#include <asm/io.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "common.h"
2062306a36Sopenharmony_ci#include "da8xx.h"
2162306a36Sopenharmony_ci#include "mux.h"
2262306a36Sopenharmony_ci#include "pm.h"
2362306a36Sopenharmony_ci#include "clock.h"
2462306a36Sopenharmony_ci#include "psc.h"
2562306a36Sopenharmony_ci#include "sram.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DA850_PLL1_BASE		0x01e1a000
2862306a36Sopenharmony_ci#define DEEPSLEEP_SLEEPCOUNT_MASK	0xFFFF
2962306a36Sopenharmony_ci#define DEEPSLEEP_SLEEPCOUNT		128
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void (*davinci_sram_suspend) (struct davinci_pm_config *);
3262306a36Sopenharmony_cistatic struct davinci_pm_config pm_config = {
3362306a36Sopenharmony_ci	.sleepcount = DEEPSLEEP_SLEEPCOUNT,
3462306a36Sopenharmony_ci	.ddrpsc_num = DA8XX_LPSC1_EMIF3C,
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void davinci_sram_push(void *dest, void *src, unsigned int size)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	memcpy(dest, src, size);
4062306a36Sopenharmony_ci	flush_icache_range((unsigned long)dest, (unsigned long)(dest + size));
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void davinci_pm_suspend(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	unsigned val;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		/* Switch CPU PLL to bypass mode */
5062306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
5162306a36Sopenharmony_ci		val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
5262306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		udelay(PLL_BYPASS_TIME);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		/* Powerdown CPU PLL */
5762306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
5862306a36Sopenharmony_ci		val |= PLLCTL_PLLPWRDN;
5962306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Configure sleep count in deep sleep register */
6362306a36Sopenharmony_ci	val = __raw_readl(pm_config.deepsleep_reg);
6462306a36Sopenharmony_ci	val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
6562306a36Sopenharmony_ci	val |= pm_config.sleepcount;
6662306a36Sopenharmony_ci	__raw_writel(val, pm_config.deepsleep_reg);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* System goes to sleep in this call */
6962306a36Sopenharmony_ci	davinci_sram_suspend(&pm_config);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		/* put CPU PLL in reset */
7462306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
7562306a36Sopenharmony_ci		val &= ~PLLCTL_PLLRST;
7662306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		/* put CPU PLL in power down */
7962306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
8062306a36Sopenharmony_ci		val &= ~PLLCTL_PLLPWRDN;
8162306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		/* wait for CPU PLL reset */
8462306a36Sopenharmony_ci		udelay(PLL_RESET_TIME);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* bring CPU PLL out of reset */
8762306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
8862306a36Sopenharmony_ci		val |= PLLCTL_PLLRST;
8962306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		/* Wait for CPU PLL to lock */
9262306a36Sopenharmony_ci		udelay(PLL_LOCK_TIME);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		/* Remove CPU PLL from bypass mode */
9562306a36Sopenharmony_ci		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
9662306a36Sopenharmony_ci		val &= ~PLLCTL_PLLENSRC;
9762306a36Sopenharmony_ci		val |= PLLCTL_PLLEN;
9862306a36Sopenharmony_ci		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int davinci_pm_enter(suspend_state_t state)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	int ret = 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	switch (state) {
10762306a36Sopenharmony_ci	case PM_SUSPEND_MEM:
10862306a36Sopenharmony_ci		davinci_pm_suspend();
10962306a36Sopenharmony_ci		break;
11062306a36Sopenharmony_ci	default:
11162306a36Sopenharmony_ci		ret = -EINVAL;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return ret;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct platform_suspend_ops davinci_pm_ops = {
11862306a36Sopenharmony_ci	.enter		= davinci_pm_enter,
11962306a36Sopenharmony_ci	.valid		= suspend_valid_only_mem,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint __init davinci_pm_init(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	ret = davinci_cfg_reg(DA850_RTC_ALARM);
12762306a36Sopenharmony_ci	if (ret)
12862306a36Sopenharmony_ci		return ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	pm_config.ddr2_ctlr_base = da8xx_get_mem_ctlr();
13162306a36Sopenharmony_ci	pm_config.deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	pm_config.cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K);
13462306a36Sopenharmony_ci	if (!pm_config.cpupll_reg_base)
13562306a36Sopenharmony_ci		return -ENOMEM;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	pm_config.ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K);
13862306a36Sopenharmony_ci	if (!pm_config.ddrpll_reg_base) {
13962306a36Sopenharmony_ci		ret = -ENOMEM;
14062306a36Sopenharmony_ci		goto no_ddrpll_mem;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pm_config.ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K);
14462306a36Sopenharmony_ci	if (!pm_config.ddrpsc_reg_base) {
14562306a36Sopenharmony_ci		ret = -ENOMEM;
14662306a36Sopenharmony_ci		goto no_ddrpsc_mem;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
15062306a36Sopenharmony_ci	if (!davinci_sram_suspend) {
15162306a36Sopenharmony_ci		pr_err("PM: cannot allocate SRAM memory\n");
15262306a36Sopenharmony_ci		ret = -ENOMEM;
15362306a36Sopenharmony_ci		goto no_sram_mem;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
15762306a36Sopenharmony_ci						davinci_cpu_suspend_sz);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	suspend_set_ops(&davinci_pm_ops);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cino_sram_mem:
16462306a36Sopenharmony_ci	iounmap(pm_config.ddrpsc_reg_base);
16562306a36Sopenharmony_cino_ddrpsc_mem:
16662306a36Sopenharmony_ci	iounmap(pm_config.ddrpll_reg_base);
16762306a36Sopenharmony_cino_ddrpll_mem:
16862306a36Sopenharmony_ci	iounmap(pm_config.cpupll_reg_base);
16962306a36Sopenharmony_ci	return ret;
17062306a36Sopenharmony_ci}
171