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