blob: 8b839d37a9cb887693ca5e373dcf454d88dfb809 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Peng Fanfa85b022017-08-17 17:48:50 +08002/*
3 * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
4 * Copyright 2017 NXP
Peng Fanfa85b022017-08-17 17:48:50 +08005 */
6
Peng Fan7de47032015-10-23 10:13:04 +08007#include <asm/io.h>
8#include <asm/psci.h>
Chen-Yu Tsaiafc1f652016-06-19 12:38:41 +08009#include <asm/secure.h>
Peng Fan7de47032015-10-23 10:13:04 +080010#include <asm/arch/imx-regs.h>
Stefan Agnera89eb892018-06-24 21:09:56 +020011#include <linux/bitops.h>
Peng Fan7de47032015-10-23 10:13:04 +080012#include <common.h>
Anson Huang169c20e2018-01-07 14:34:31 +080013#include <fsl_wdog.h>
Peng Fan7de47032015-10-23 10:13:04 +080014
15#define GPC_CPU_PGC_SW_PDN_REQ 0xfc
16#define GPC_CPU_PGC_SW_PUP_REQ 0xf0
17#define GPC_PGC_C1 0x840
18
19#define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2
20
21/* below is for i.MX7D */
22#define SRC_GPR1_MX7D 0x074
23#define SRC_A7RCR0 0x004
24#define SRC_A7RCR1 0x008
25
26#define BP_SRC_A7RCR0_A7_CORE_RESET0 0
27#define BP_SRC_A7RCR1_A7_CORE1_ENABLE 1
28
Anson Huang4f0cd032018-01-07 14:34:32 +080029#define SNVS_LPCR 0x38
30#define BP_SNVS_LPCR_DP_EN 0x20
31#define BP_SNVS_LPCR_TOP 0x40
32
33#define CCM_CCGR_SNVS 0x4250
34
Anson Huang169c20e2018-01-07 14:34:31 +080035#define CCM_ROOT_WDOG 0xbb80
36#define CCM_CCGR_WDOG1 0x49c0
37
Stefan Agnera89eb892018-06-24 21:09:56 +020038#define MPIDR_AFF0 GENMASK(7, 0)
39
40#define IMX7D_PSCI_NR_CPUS 2
41#if IMX7D_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS
42#error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS"
43#endif
44
45u8 psci_state[IMX7D_PSCI_NR_CPUS] __secure_data = {
46 PSCI_AFFINITY_LEVEL_ON,
47 PSCI_AFFINITY_LEVEL_OFF};
48
49static inline void psci_set_state(int cpu, u8 state)
50{
51 psci_state[cpu] = state;
52 dsb();
53 isb();
54}
55
Peng Fan7de47032015-10-23 10:13:04 +080056static inline void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset)
57{
58 writel(enable, GPC_IPS_BASE_ADDR + offset);
59}
60
61__secure void imx_gpcv2_set_core1_power(bool pdn)
62{
63 u32 reg = pdn ? GPC_CPU_PGC_SW_PUP_REQ : GPC_CPU_PGC_SW_PDN_REQ;
64 u32 val;
65
66 imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C1);
67
68 val = readl(GPC_IPS_BASE_ADDR + reg);
69 val |= BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
70 writel(val, GPC_IPS_BASE_ADDR + reg);
71
72 while ((readl(GPC_IPS_BASE_ADDR + reg) &
73 BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7) != 0)
74 ;
75
76 imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C1);
77}
78
79__secure void imx_enable_cpu_ca7(int cpu, bool enable)
80{
81 u32 mask, val;
82
83 mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1);
84 val = readl(SRC_BASE_ADDR + SRC_A7RCR1);
85 val = enable ? val | mask : val & ~mask;
86 writel(val, SRC_BASE_ADDR + SRC_A7RCR1);
87}
88
Stefan Agnera89eb892018-06-24 21:09:56 +020089__secure void psci_arch_cpu_entry(void)
90{
91 u32 cpu = psci_get_cpu_id();
92
93 psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON);
94}
95
Stefan Agnercff38c52018-06-24 21:09:55 +020096__secure s32 psci_cpu_on(u32 __always_unused function_id, u32 mpidr, u32 ep,
97 u32 context_id)
Peng Fan7de47032015-10-23 10:13:04 +080098{
Stefan Agnera89eb892018-06-24 21:09:56 +020099 u32 cpu = mpidr & MPIDR_AFF0;
100
101 if (mpidr & ~MPIDR_AFF0)
102 return ARM_PSCI_RET_INVAL;
103
104 if (cpu >= IMX7D_PSCI_NR_CPUS)
105 return ARM_PSCI_RET_INVAL;
106
107 if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON)
108 return ARM_PSCI_RET_ALREADY_ON;
109
110 if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON_PENDING)
111 return ARM_PSCI_RET_ON_PENDING;
Stefan Agnercff38c52018-06-24 21:09:55 +0200112
113 psci_save(cpu, ep, context_id);
114
115 writel((u32)psci_cpu_entry, SRC_BASE_ADDR + cpu * 8 + SRC_GPR1_MX7D);
Stefan Agnera89eb892018-06-24 21:09:56 +0200116
117 psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON_PENDING);
118
Peng Fan7de47032015-10-23 10:13:04 +0800119 imx_gpcv2_set_core1_power(true);
120 imx_enable_cpu_ca7(cpu, true);
Stefan Agnera89eb892018-06-24 21:09:56 +0200121
122 return ARM_PSCI_RET_SUCCESS;
Peng Fan7de47032015-10-23 10:13:04 +0800123}
124
Stefan Agnercff38c52018-06-24 21:09:55 +0200125__secure s32 psci_cpu_off(void)
Peng Fan7de47032015-10-23 10:13:04 +0800126{
Stefan Agnercff38c52018-06-24 21:09:55 +0200127 int cpu;
128
Stefan Agnercff38c52018-06-24 21:09:55 +0200129 cpu = psci_get_cpu_id();
Stefan Agnera89eb892018-06-24 21:09:56 +0200130
131 psci_cpu_off_common();
132 psci_set_state(cpu, PSCI_AFFINITY_LEVEL_OFF);
133
Peng Fan7de47032015-10-23 10:13:04 +0800134 imx_enable_cpu_ca7(cpu, false);
135 imx_gpcv2_set_core1_power(false);
136 writel(0, SRC_BASE_ADDR + cpu * 8 + SRC_GPR1_MX7D + 4);
Stefan Agnercff38c52018-06-24 21:09:55 +0200137
138 while (1)
139 wfi();
Peng Fan7de47032015-10-23 10:13:04 +0800140}
Anson Huang169c20e2018-01-07 14:34:31 +0800141
Stefan Agnercff38c52018-06-24 21:09:55 +0200142__secure void psci_system_reset(void)
Anson Huang169c20e2018-01-07 14:34:31 +0800143{
144 struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR;
145
146 /* make sure WDOG1 clock is enabled */
147 writel(0x1 << 28, CCM_BASE_ADDR + CCM_ROOT_WDOG);
148 writel(0x3, CCM_BASE_ADDR + CCM_CCGR_WDOG1);
149 writew(WCR_WDE, &wdog->wcr);
Stefan Agnercff38c52018-06-24 21:09:55 +0200150
151 while (1)
152 wfi();
Anson Huang169c20e2018-01-07 14:34:31 +0800153}
Anson Huang4f0cd032018-01-07 14:34:32 +0800154
Stefan Agnercff38c52018-06-24 21:09:55 +0200155__secure void psci_system_off(void)
Anson Huang4f0cd032018-01-07 14:34:32 +0800156{
157 u32 val;
158
159 /* make sure SNVS clock is enabled */
160 writel(0x3, CCM_BASE_ADDR + CCM_CCGR_SNVS);
161
162 val = readl(SNVS_BASE_ADDR + SNVS_LPCR);
163 val |= BP_SNVS_LPCR_DP_EN | BP_SNVS_LPCR_TOP;
164 writel(val, SNVS_BASE_ADDR + SNVS_LPCR);
Stefan Agnercff38c52018-06-24 21:09:55 +0200165
166 while (1)
167 wfi();
Anson Huang4f0cd032018-01-07 14:34:32 +0800168}
Stefan Agnera89eb892018-06-24 21:09:56 +0200169
170__secure u32 psci_version(void)
171{
172 return ARM_PSCI_VER_1_0;
173}
174
175__secure s32 psci_cpu_suspend(u32 __always_unused function_id, u32 power_state,
176 u32 entry_point_address,
177 u32 context_id)
178{
179 return ARM_PSCI_RET_INVAL;
180}
181
182__secure s32 psci_affinity_info(u32 __always_unused function_id,
183 u32 target_affinity,
184 u32 lowest_affinity_level)
185{
186 u32 cpu = target_affinity & MPIDR_AFF0;
187
188 if (lowest_affinity_level > 0)
189 return ARM_PSCI_RET_INVAL;
190
191 if (target_affinity & ~MPIDR_AFF0)
192 return ARM_PSCI_RET_INVAL;
193
194 if (cpu >= IMX7D_PSCI_NR_CPUS)
195 return ARM_PSCI_RET_INVAL;
196
197 return psci_state[cpu];
198}
199
200__secure s32 psci_features(u32 __always_unused function_id, u32 psci_fid)
201{
202 switch (psci_fid) {
203 case ARM_PSCI_0_2_FN_PSCI_VERSION:
204 case ARM_PSCI_0_2_FN_CPU_OFF:
205 case ARM_PSCI_0_2_FN_CPU_ON:
206 case ARM_PSCI_0_2_FN_AFFINITY_INFO:
207 case ARM_PSCI_0_2_FN_SYSTEM_OFF:
208 case ARM_PSCI_0_2_FN_SYSTEM_RESET:
209 case ARM_PSCI_1_0_FN_PSCI_FEATURES:
210 return 0x0;
211 }
212 return ARM_PSCI_RET_NI;
213}