1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * MMP2 Power Management Routines
4 *
5 * (C) Copyright 2012 Marvell International Ltd.
6 * All Rights Reserved
7 */
8
9#include <linux/kernel.h>
10#include <linux/errno.h>
11#include <linux/err.h>
12#include <linux/time.h>
13#include <linux/delay.h>
14#include <linux/suspend.h>
15#include <linux/irq.h>
16#include <linux/io.h>
17#include <linux/interrupt.h>
18#include <asm/mach-types.h>
19
20#include <linux/soc/mmp/cputype.h>
21#include "addr-map.h"
22#include "pm-mmp2.h"
23#include "regs-icu.h"
24#include "irqs.h"
25
26int mmp2_set_wake(struct irq_data *d, unsigned int on)
27{
28	unsigned long data = 0;
29	int irq = d->irq;
30
31	/* enable wakeup sources */
32	switch (irq) {
33	case IRQ_MMP2_RTC:
34	case IRQ_MMP2_RTC_ALARM:
35		data = MPMU_WUCRM_PJ_WAKEUP(4) | MPMU_WUCRM_PJ_RTC_ALARM;
36		break;
37	case IRQ_MMP2_PMIC:
38		data = MPMU_WUCRM_PJ_WAKEUP(7);
39		break;
40	case IRQ_MMP2_MMC2:
41		/* mmc use WAKEUP2, same as GPIO wakeup source */
42		data = MPMU_WUCRM_PJ_WAKEUP(2);
43		break;
44	}
45	if (on) {
46		if (data) {
47			data |= __raw_readl(MPMU_WUCRM_PJ);
48			__raw_writel(data, MPMU_WUCRM_PJ);
49		}
50	} else {
51		if (data) {
52			data = ~data & __raw_readl(MPMU_WUCRM_PJ);
53			__raw_writel(data, MPMU_WUCRM_PJ);
54		}
55	}
56	return 0;
57}
58
59static void pm_scu_clk_disable(void)
60{
61	unsigned int val;
62
63	/* close AXI fabric clock gate */
64	__raw_writel(0x0, CIU_REG(0x64));
65	__raw_writel(0x0, CIU_REG(0x68));
66
67	/* close MCB master clock gate */
68	val = __raw_readl(CIU_REG(0x1c));
69	val |= 0xf0;
70	__raw_writel(val, CIU_REG(0x1c));
71
72	return ;
73}
74
75static void pm_scu_clk_enable(void)
76{
77	unsigned int val;
78
79	/* open AXI fabric clock gate */
80	__raw_writel(0x03003003, CIU_REG(0x64));
81	__raw_writel(0x00303030, CIU_REG(0x68));
82
83	/* open MCB master clock gate */
84	val = __raw_readl(CIU_REG(0x1c));
85	val &= ~(0xf0);
86	__raw_writel(val, CIU_REG(0x1c));
87
88	return ;
89}
90
91static void pm_mpmu_clk_disable(void)
92{
93	/*
94	 * disable clocks in MPMU_CGR_PJ register
95	 * except clock for APMU_PLL1, APMU_PLL1_2 and AP_26M
96	 */
97	__raw_writel(0x0000a010, MPMU_CGR_PJ);
98}
99
100static void pm_mpmu_clk_enable(void)
101{
102	unsigned int val;
103
104	__raw_writel(0xdffefffe, MPMU_CGR_PJ);
105	val = __raw_readl(MPMU_PLL2_CTRL1);
106	val |= (1 << 29);
107	__raw_writel(val, MPMU_PLL2_CTRL1);
108
109	return ;
110}
111
112void mmp2_pm_enter_lowpower_mode(int state)
113{
114	uint32_t idle_cfg, apcr;
115
116	idle_cfg = __raw_readl(APMU_PJ_IDLE_CFG);
117	apcr = __raw_readl(MPMU_PCR_PJ);
118	apcr &= ~(MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD | MPMU_PCR_PJ_APBSD
119		 | MPMU_PCR_PJ_AXISD | MPMU_PCR_PJ_VCTCXOSD | (1 << 13));
120	idle_cfg &= ~APMU_PJ_IDLE_CFG_PJ_IDLE;
121
122	switch (state) {
123	case POWER_MODE_SYS_SLEEP:
124		apcr |= MPMU_PCR_PJ_SLPEN;		/* set the SLPEN bit */
125		apcr |= MPMU_PCR_PJ_VCTCXOSD;		/* set VCTCXOSD */
126		fallthrough;
127	case POWER_MODE_CHIP_SLEEP:
128		apcr |= MPMU_PCR_PJ_SLPEN;
129		fallthrough;
130	case POWER_MODE_APPS_SLEEP:
131		apcr |= MPMU_PCR_PJ_APBSD;		/* set APBSD */
132		fallthrough;
133	case POWER_MODE_APPS_IDLE:
134		apcr |= MPMU_PCR_PJ_AXISD;		/* set AXISDD bit */
135		apcr |= MPMU_PCR_PJ_DDRCORSD;		/* set DDRCORSD bit */
136		idle_cfg |= APMU_PJ_IDLE_CFG_PJ_PWRDWN;	/* PJ power down */
137		apcr |= MPMU_PCR_PJ_SPSD;
138		fallthrough;
139	case POWER_MODE_CORE_EXTIDLE:
140		idle_cfg |= APMU_PJ_IDLE_CFG_PJ_IDLE;	/* set the IDLE bit */
141		idle_cfg &= ~APMU_PJ_IDLE_CFG_ISO_MODE_CNTRL_MASK;
142		idle_cfg |= APMU_PJ_IDLE_CFG_PWR_SW(3)
143			| APMU_PJ_IDLE_CFG_L2_PWR_SW;
144		break;
145	case POWER_MODE_CORE_INTIDLE:
146		apcr &= ~MPMU_PCR_PJ_SPSD;
147		break;
148	}
149
150	/* set reserve bits */
151	apcr |= (1 << 30) | (1 << 25);
152
153	/* finally write the registers back */
154	__raw_writel(idle_cfg, APMU_PJ_IDLE_CFG);
155	__raw_writel(apcr, MPMU_PCR_PJ);	/* 0xfe086000 */
156}
157
158static int mmp2_pm_enter(suspend_state_t state)
159{
160	int temp;
161
162	temp = __raw_readl(MMP2_ICU_INT4_MASK);
163	if (temp & (1 << 1)) {
164		printk(KERN_ERR "%s: PMIC interrupt is handling\n", __func__);
165		return -EAGAIN;
166	}
167
168	temp = __raw_readl(APMU_SRAM_PWR_DWN);
169	temp |= ((1 << 19) | (1 << 18));
170	__raw_writel(temp, APMU_SRAM_PWR_DWN);
171	pm_mpmu_clk_disable();
172	pm_scu_clk_disable();
173
174	printk(KERN_INFO "%s: before suspend\n", __func__);
175	cpu_do_idle();
176	printk(KERN_INFO "%s: after suspend\n", __func__);
177
178	pm_mpmu_clk_enable();		/* enable clocks in MPMU */
179	pm_scu_clk_enable();		/* enable clocks in SCU */
180
181	return 0;
182}
183
184/*
185 * Called after processes are frozen, but before we shut down devices.
186 */
187static int mmp2_pm_prepare(void)
188{
189	mmp2_pm_enter_lowpower_mode(POWER_MODE_SYS_SLEEP);
190
191	return 0;
192}
193
194/*
195 * Called after devices are re-setup, but before processes are thawed.
196 */
197static void mmp2_pm_finish(void)
198{
199	mmp2_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE);
200}
201
202static int mmp2_pm_valid(suspend_state_t state)
203{
204	return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM));
205}
206
207/*
208 * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
209 */
210static const struct platform_suspend_ops mmp2_pm_ops = {
211	.valid		= mmp2_pm_valid,
212	.prepare	= mmp2_pm_prepare,
213	.enter		= mmp2_pm_enter,
214	.finish		= mmp2_pm_finish,
215};
216
217static int __init mmp2_pm_init(void)
218{
219	uint32_t apcr;
220
221	if (!cpu_is_mmp2())
222		return -EIO;
223
224	suspend_set_ops(&mmp2_pm_ops);
225
226	/*
227	 * Set bit 0, Slow clock Select 32K clock input instead of VCXO
228	 * VCXO is chosen by default, which would be disabled in suspend
229	 */
230	__raw_writel(0x5, MPMU_SCCR);
231
232	/*
233	 * Clear bit 23 of CIU_CPU_CONF
234	 * direct PJ4 to DDR access through Memory Controller slow queue
235	 * fast queue has issue and cause lcd will flick
236	 */
237	__raw_writel(__raw_readl(CIU_REG(0x8)) & ~(0x1 << 23), CIU_REG(0x8));
238
239	/* Clear default low power control bit */
240	apcr = __raw_readl(MPMU_PCR_PJ);
241	apcr &= ~(MPMU_PCR_PJ_SLPEN | MPMU_PCR_PJ_DDRCORSD
242			| MPMU_PCR_PJ_APBSD | MPMU_PCR_PJ_AXISD | 1 << 13);
243	__raw_writel(apcr, MPMU_PCR_PJ);
244
245	return 0;
246}
247
248late_initcall(mmp2_pm_init);
249