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