1 /*
2  * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <common/debug.h>
8 #include <drivers/delay_timer.h>
9 #include <lib/mmio.h>
10 #include <sspm_reg.h>
11 #include <mtk_mcdi.h>
12 
mcdi_mbox_read(uint32_t id)13 static inline uint32_t mcdi_mbox_read(uint32_t id)
14 {
15 	return mmio_read_32(SSPM_MBOX_3_BASE + (id << 2));
16 }
17 
mcdi_mbox_write(uint32_t id,uint32_t val)18 static inline void mcdi_mbox_write(uint32_t id, uint32_t val)
19 {
20 	mmio_write_32(SSPM_MBOX_3_BASE + (id << 2), val);
21 }
22 
sspm_set_bootaddr(uint32_t bootaddr)23 void sspm_set_bootaddr(uint32_t bootaddr)
24 {
25 	mcdi_mbox_write(MCDI_MBOX_BOOTADDR, bootaddr);
26 }
27 
sspm_cluster_pwr_off_notify(uint32_t cluster)28 void sspm_cluster_pwr_off_notify(uint32_t cluster)
29 {
30 	mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 1);
31 }
32 
sspm_cluster_pwr_on_notify(uint32_t cluster)33 void sspm_cluster_pwr_on_notify(uint32_t cluster)
34 {
35 	mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 0);
36 }
37 
sspm_standbywfi_irq_enable(uint32_t cpu_idx)38 void sspm_standbywfi_irq_enable(uint32_t cpu_idx)
39 {
40 	mmio_write_32(SSPM_CFGREG_ACAO_INT_SET, STANDBYWFI_EN(cpu_idx));
41 }
42 
mcdi_avail_cpu_mask_read(void)43 uint32_t mcdi_avail_cpu_mask_read(void)
44 {
45 	return mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK);
46 }
47 
mcdi_avail_cpu_mask_write(uint32_t mask)48 uint32_t mcdi_avail_cpu_mask_write(uint32_t mask)
49 {
50 	mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, mask);
51 
52 	return mask;
53 }
54 
mcdi_avail_cpu_mask_set(uint32_t mask)55 uint32_t mcdi_avail_cpu_mask_set(uint32_t mask)
56 {
57 	uint32_t m;
58 
59 	m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK);
60 	m |= mask;
61 	mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m);
62 
63 	return m;
64 }
65 
mcdi_avail_cpu_mask_clr(uint32_t mask)66 uint32_t mcdi_avail_cpu_mask_clr(uint32_t mask)
67 {
68 	uint32_t m;
69 
70 	m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK);
71 	m &= ~mask;
72 	mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m);
73 
74 	return m;
75 }
76 
mcdi_cpu_cluster_pwr_stat_read(void)77 uint32_t mcdi_cpu_cluster_pwr_stat_read(void)
78 {
79 	return mcdi_mbox_read(MCDI_MBOX_CPU_CLUSTER_PWR_STAT);
80 }
81 
82 #define PAUSE_BIT		1
83 #define CLUSTER_OFF_OFS		20
84 #define CPU_OFF_OFS		24
85 #define CLUSTER_ON_OFS		4
86 #define CPU_ON_OFS		8
87 
target_mask(int cluster,int cpu_idx,bool on)88 static uint32_t target_mask(int cluster, int cpu_idx, bool on)
89 {
90 	uint32_t t = 0;
91 
92 	if (on) {
93 		if (cluster >= 0)
94 			t |= BIT(cluster + CLUSTER_ON_OFS);
95 
96 		if (cpu_idx >= 0)
97 			t |= BIT(cpu_idx + CPU_ON_OFS);
98 	} else {
99 		if (cluster >= 0)
100 			t |= BIT(cluster + CLUSTER_OFF_OFS);
101 
102 		if (cpu_idx >= 0)
103 			t |= BIT(cpu_idx + CPU_OFF_OFS);
104 	}
105 
106 	return t;
107 }
108 
mcdi_pause_clr(int cluster,int cpu_idx,bool on)109 void mcdi_pause_clr(int cluster, int cpu_idx, bool on)
110 {
111 	uint32_t tgt = target_mask(cluster, cpu_idx, on);
112 	uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION);
113 
114 	m &= ~tgt;
115 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
116 }
117 
mcdi_pause_set(int cluster,int cpu_idx,bool on)118 void mcdi_pause_set(int cluster, int cpu_idx, bool on)
119 {
120 	uint32_t tgt = target_mask(cluster, cpu_idx, on);
121 	uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION);
122 	uint32_t tgtn = target_mask(-1, cpu_idx, !on);
123 
124 	/* request on and off at the same time to ensure it can be paused */
125 	m |= tgt | tgtn;
126 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
127 
128 	/* wait pause_ack */
129 	while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK))
130 		;
131 
132 	/* clear non-requested operation */
133 	m &= ~tgtn;
134 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
135 }
136 
mcdi_pause(void)137 void mcdi_pause(void)
138 {
139 	uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT);
140 
141 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
142 
143 	/* wait pause_ack */
144 	while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK))
145 		;
146 }
147 
mcdi_unpause(void)148 void mcdi_unpause(void)
149 {
150 	uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT);
151 
152 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
153 }
154 
mcdi_hotplug_wait_ack(int cluster,int cpu_idx,bool on)155 void mcdi_hotplug_wait_ack(int cluster, int cpu_idx, bool on)
156 {
157 	uint32_t tgt = target_mask(cluster, cpu_idx, on);
158 	uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
159 
160 	/* wait until ack */
161 	while (!(ack & tgt))
162 		ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
163 }
164 
mcdi_hotplug_clr(int cluster,int cpu_idx,bool on)165 void mcdi_hotplug_clr(int cluster, int cpu_idx, bool on)
166 {
167 	uint32_t tgt = target_mask(cluster, cpu_idx, on);
168 	uint32_t tgt_cpu = target_mask(-1, cpu_idx, on);
169 	uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD);
170 	uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
171 
172 	if (!(cmd & tgt))
173 		return;
174 
175 	/* wait until ack */
176 	while (!(ack & tgt_cpu))
177 		ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
178 
179 	cmd &= ~tgt;
180 	mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd);
181 }
182 
mcdi_hotplug_set(int cluster,int cpu_idx,bool on)183 void mcdi_hotplug_set(int cluster, int cpu_idx, bool on)
184 {
185 	uint32_t tgt = target_mask(cluster, cpu_idx, on);
186 	uint32_t tgt_cpu = target_mask(-1, cpu_idx, on);
187 	uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD);
188 	uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
189 
190 	if ((cmd & tgt) == tgt)
191 		return;
192 
193 	/* wait until ack clear */
194 	while (ack & tgt_cpu)
195 		ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK);
196 
197 	cmd |= tgt;
198 	mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd);
199 }
200 
check_mcdi_ctl_stat(void)201 bool check_mcdi_ctl_stat(void)
202 {
203 	uint32_t clk_regs[] = {0x100010ac, 0x100010c8};
204 	uint32_t clk_mask[] = {0x00028000, 0x00000018};
205 	uint32_t tgt = target_mask(0, 0, true);
206 	uint32_t m;
207 	int i;
208 
209 	/* check clk status */
210 	for (i = 0; i < ARRAY_SIZE(clk_regs); i++) {
211 		if (mmio_read_32(clk_regs[i]) & clk_mask[i]) {
212 			WARN("mcdi: clk check fail.\n");
213 			return false;
214 		}
215 	}
216 
217 	/* check mcdi cmd handling */
218 	m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT);
219 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
220 
221 	i = 500;
222 	while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK) && --i > 0)
223 		udelay(10);
224 
225 	m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT);
226 	mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m);
227 
228 	if (i == 0) {
229 		WARN("mcdi: pause_action fail.\n");
230 		return false;
231 	}
232 
233 	/* check mcdi cmd handling */
234 	if (mcdi_mbox_read(MCDI_MBOX_HP_CMD) ||
235 			mcdi_mbox_read(MCDI_MBOX_HP_ACK)) {
236 		WARN("mcdi: hp_cmd fail.\n");
237 		return false;
238 	}
239 
240 	mcdi_mbox_write(MCDI_MBOX_HP_CMD, tgt);
241 
242 	i = 500;
243 	while ((mcdi_mbox_read(MCDI_MBOX_HP_ACK) & tgt) != tgt && --i > 0)
244 		udelay(10);
245 
246 	mcdi_mbox_write(MCDI_MBOX_HP_CMD, 0);
247 
248 	if (i == 0) {
249 		WARN("mcdi: hp_ack fail.\n");
250 		return false;
251 	}
252 
253 	return true;
254 }
255 
mcdi_init(void)256 void mcdi_init(void)
257 {
258 	mcdi_avail_cpu_mask_write(0x01); /* cpu0 default on */
259 }
260