1 /*
2  * Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved.
3  * Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * Neither the name of ARM nor the names of its contributors may be used
16  * to endorse or promote products derived from this software without specific
17  * prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <assert.h>
33 #include <arch_helpers.h>
34 #include <arm_gic.h>
35 #include <debug.h>
36 #include <cci400.h>
37 #include <errno.h>
38 #include <gic_v2.h>
39 #include <gpio.h>
40 #include <hi6220.h>
41 #include <hisi_ipc.h>
42 #include <hisi_pwrc.h>
43 #include <mmio.h>
44 #include <platform.h>
45 #include <platform_def.h>
46 #include <psci.h>
47 #include <sp804_timer.h>
48 
49 #include "hikey_def.h"
50 #include "hikey_private.h"
51 
52 #define PLAT_SOC_SUSPEND_STATE	0x4
53 
hikey_do_plat_actions(uint32_t afflvl,uint32_t state)54 static int32_t hikey_do_plat_actions(uint32_t afflvl, uint32_t state)
55 {
56 	assert(afflvl <= MPIDR_AFFLVL1);
57 
58 	if (state != PSCI_STATE_OFF)
59 		return -EAGAIN;
60 
61 	return 0;
62 }
63 
hikey_affinst_on(uint64_t mpidr,uint64_t sec_entrypoint,uint32_t afflvl,uint32_t state)64 int32_t hikey_affinst_on(uint64_t mpidr,
65 			 uint64_t sec_entrypoint,
66 			 uint32_t afflvl,
67 			 uint32_t state)
68 {
69 	int cpu, cluster;
70 
71 	cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT;
72 	cpu = mpidr & MPIDR_CPU_MASK;
73 
74 	VERBOSE("#%s, mpidr:%llx, afflvl:%x, state:%x\n", __func__, mpidr, afflvl, state);
75 
76 	/* directly return for power on */
77 	if (state == PSCI_STATE_ON)
78 		return PSCI_E_SUCCESS;
79 
80 	switch (afflvl) {
81 	case MPIDR_AFFLVL0:
82 		hisi_pwrc_set_core_bx_addr(cpu, cluster, sec_entrypoint);
83 		hisi_ipc_cpu_on(cpu, cluster);
84 		break;
85 
86 	case MPIDR_AFFLVL1:
87 		hisi_ipc_cluster_on(cpu, cluster);
88 		break;
89 	}
90 
91 	return PSCI_E_SUCCESS;
92 }
93 
94 
hikey_affinst_off(uint32_t afflvl,uint32_t state)95 static void hikey_affinst_off(uint32_t afflvl, uint32_t state)
96 {
97 	unsigned int mpidr = read_mpidr_el1();
98 	int cpu, cluster;
99 
100 	cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT;
101 	cpu = mpidr & MPIDR_CPU_MASK;
102 
103 	if (hikey_do_plat_actions(afflvl, state) == -EAGAIN)
104 		return;
105 
106 	switch (afflvl) {
107 	case MPIDR_AFFLVL1:
108 		hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE);
109 		cci_disable_cluster_coherency(mpidr);
110 		hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE);
111 
112 		hisi_ipc_cluster_off(cpu, cluster);
113 		break;
114 
115 	case MPIDR_AFFLVL0:
116 		arm_gic_cpuif_deactivate();
117 		hisi_ipc_cpu_off(cpu, cluster);
118 		break;
119 	}
120 
121 	return;
122 }
123 
hikey_affinst_suspend(uint64_t sec_entrypoint,uint32_t afflvl,uint32_t state)124 static void hikey_affinst_suspend(uint64_t sec_entrypoint,
125 				  uint32_t afflvl,
126 				  uint32_t state)
127 {
128 	unsigned int mpidr = read_mpidr_el1();
129 	int cpu, cluster;
130 
131 	cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT;
132 	cpu = mpidr & MPIDR_CPU_MASK;
133 
134 	if (hikey_do_plat_actions(afflvl, state) == -EAGAIN)
135 		return;
136 
137 	switch (afflvl) {
138 	case MPIDR_AFFLVL1:
139 
140 		hisi_ipc_spin_lock(HISI_IPC_SEM_CPUIDLE);
141 		cci_disable_cluster_coherency(mpidr);
142 		hisi_ipc_spin_unlock(HISI_IPC_SEM_CPUIDLE);
143 
144 		if (psci_get_suspend_stateid() == PLAT_SOC_SUSPEND_STATE) {
145 			hisi_pwrc_set_cluster_wfi(1);
146 			hisi_pwrc_set_cluster_wfi(0);
147 			hisi_ipc_psci_system_off();
148 		} else
149 			hisi_ipc_cluster_suspend(cpu, cluster);
150 
151 		break;
152 
153 	case MPIDR_AFFLVL0:
154 
155 		/* Program the jump address for the target cpu */
156 		hisi_pwrc_set_core_bx_addr(cpu, cluster, sec_entrypoint);
157 
158 		arm_gic_cpuif_deactivate();
159 
160 		if (psci_get_suspend_stateid() != PLAT_SOC_SUSPEND_STATE)
161 			hisi_ipc_cpu_suspend(cpu, cluster);
162 		break;
163 	}
164 
165 	return;
166 }
167 
hikey_affinst_on_finish(uint32_t afflvl,uint32_t state)168 void hikey_affinst_on_finish(uint32_t afflvl, uint32_t state)
169 {
170 	unsigned long mpidr;
171 	int cpu, cluster;
172 
173 	if (hikey_do_plat_actions(afflvl, state) == -EAGAIN)
174 		return;
175 
176 	/* Get the mpidr for this cpu */
177 	mpidr = read_mpidr_el1();
178 	cluster = (mpidr & MPIDR_CLUSTER_MASK) >> MPIDR_AFF1_SHIFT;
179 	cpu = mpidr & MPIDR_CPU_MASK;
180 
181 	/* Perform the common cluster specific operations */
182 	if (afflvl != MPIDR_AFFLVL0)
183 		cci_enable_cluster_coherency(mpidr);
184 
185 	/* Zero the jump address in the mailbox for this cpu */
186 	hisi_pwrc_set_core_bx_addr(cpu, cluster, 0);
187 
188 	if (psci_get_suspend_stateid() == PLAT_SOC_SUSPEND_STATE) {
189 		arm_gic_setup();
190 	} else {
191 		/* Enable the gic cpu interface */
192 		arm_gic_cpuif_setup();
193 
194 		/* TODO: This setup is needed only after a cold boot */
195 		arm_gic_pcpu_distif_setup();
196 	}
197 
198 	return;
199 }
200 
hikey_affinst_suspend_finish(uint32_t afflvl,uint32_t state)201 static void hikey_affinst_suspend_finish(uint32_t afflvl,
202 					 uint32_t state)
203 {
204 	hikey_affinst_on_finish(afflvl, state);
205 	return;
206 }
207 
hikey_system_off(void)208 static void __dead2 hikey_system_off(void)
209 {
210 	unsigned int start, cnt, delta, delta_ms;
211 	unsigned int show = 1;
212 
213 	NOTICE("%s: off system\n", __func__);
214 
215 	/* pulling GPIO_0_0 low to trigger PMIC shutdown */
216 	/* setting pinmux */
217 	mmio_write_32(0xF8001810, 0x2);
218 	/* setting pin direction */
219 	mmio_write_8(0xF8011400, 1);
220 	/* setting pin output value */
221 	mmio_write_8(0xF8011004, 0);
222 
223 	/* PMIC shutdown depends on two conditions: GPIO_0_0 (PWR_HOLD) low,
224 	 * and VBUS_DET < 3.6V. For HiKey, VBUS_DET is connected to VDD_4V2
225 	 * through Jumper 1-2. So, to complete shutdown, user needs to manually
226 	 * remove Jumper 1-2.
227 	 */
228 	/* init timer00 */
229 	mmio_write_32(TIMER00_CONTROL, 0);
230 	mmio_write_32(TIMER00_LOAD, 0xffffffff);
231 	/* free running */
232 	mmio_write_32(TIMER00_CONTROL, 0x82);
233 
234 	/* adding delays */
235 	start = mmio_read_32(TIMER00_VALUE);
236 	do {
237 		cnt = mmio_read_32(TIMER00_VALUE);
238 		if (cnt > start) {
239 			delta = 0xffffffff - cnt;
240 			delta += start;
241 		} else
242 			delta = start - cnt;
243 		delta_ms = delta / 19200;
244 		if (delta_ms > 1000 && show) { /* after 1 second */
245 			/* if we are still alive, that means Jumper
246 			 * 1-2 is mounted. Need to warn and reboot
247 			 */
248 			NOTICE("..........................................\n");
249 			NOTICE(" IMPORTANT: Remove Jumper 1-2 to shutdown\n");
250 			NOTICE(" DANGER:    SoC is still burning. DANGER!\n");
251 			NOTICE(" Board will be reboot to avoid overheat\n");
252 			NOTICE("..........................................\n");
253 			show = 0;
254 		}
255 	} while (delta_ms < 5000); /* no. of delay in ms */
256 
257 	/* Send the system reset request */
258 	mmio_write_32(AO_SC_SYS_STAT0, 0x48698284);
259 
260 	wfi();
261 	panic();
262 }
263 
hikey_system_reset(void)264 static void __dead2 hikey_system_reset(void)
265 {
266 	VERBOSE("%s: reset system\n", __func__);
267 
268 	/* Send the system reset request */
269 	mmio_write_32(AO_SC_SYS_STAT0, 0x48698284);
270 
271 	wfi();
272 	panic();
273 }
274 
hikey_get_sys_suspend_power_state(void)275 unsigned int hikey_get_sys_suspend_power_state(void)
276 {
277 	unsigned int power_state;
278 
279 	power_state = psci_make_powerstate(PLAT_SOC_SUSPEND_STATE,
280 			PSTATE_TYPE_POWERDOWN, MPIDR_AFFLVL1);
281 
282 	return power_state;
283 }
284 
285 static const plat_pm_ops_t hikey_plat_pm_ops = {
286 	.affinst_on		     = hikey_affinst_on,
287 	.affinst_on_finish	     = hikey_affinst_on_finish,
288 	.affinst_off		     = hikey_affinst_off,
289 	.affinst_standby	     = NULL,
290 	.affinst_suspend	     = hikey_affinst_suspend,
291 	.affinst_suspend_finish	     = hikey_affinst_suspend_finish,
292 	.system_off		     = hikey_system_off,
293 	.system_reset		     = hikey_system_reset,
294 	.get_sys_suspend_power_state = hikey_get_sys_suspend_power_state,
295 };
296 
platform_setup_pm(const plat_pm_ops_t ** plat_ops)297 int platform_setup_pm(const plat_pm_ops_t **plat_ops)
298 {
299 	*plat_ops = &hikey_plat_pm_ops;
300 	return 0;
301 }
302