summaryrefslogtreecommitdiff
path: root/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c
blob: bcf68650cd37cd6e20bdfd2d72c219e197921dfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/*
 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>

#include <platform_def.h>

#include <arch_helpers.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>

#include <../hikey960_def.h>
#include <hisi_ipc.h>
#include "hisi_pwrc.h"


/* resource lock api */
#define RES0_LOCK_BASE		(SOC_PCTRL_RESOURCE0_LOCK_ADDR(PCTRL_BASE))
#define RES1_LOCK_BASE		(SOC_PCTRL_RESOURCE1_LOCK_ADDR(PCTRL_BASE))
#define RES2_LOCK_BASE		(SOC_PCTRL_RESOURCE2_LOCK_ADDR(PCTRL_BASE))

#define LOCK_BIT			(0x1 << 28)
#define LOCK_ID_MASK			(0x7 << 29)
#define CPUIDLE_LOCK_ID(core)		(0x6 - (core))
#define LOCK_UNLOCK_OFFSET		0x4
#define LOCK_STAT_OFFSET		0x8

#define CLUSTER0_CPUS_ONLINE_MASK	(0xF << 16)
#define	CLUSTER1_CPUS_ONLINE_MASK	(0xF << 20)

/* cpu hotplug flag api */
#define SCTRL_BASE			(SOC_ACPU_SCTRL_BASE_ADDR)
#define REG_SCBAKDATA3_OFFSET		(SOC_SCTRL_SCBAKDATA3_ADDR(SCTRL_BASE))
#define REG_SCBAKDATA8_OFFSET		(SOC_SCTRL_SCBAKDATA8_ADDR(SCTRL_BASE))
#define REG_SCBAKDATA9_OFFSET		(SOC_SCTRL_SCBAKDATA9_ADDR(SCTRL_BASE))

#define CPUIDLE_FLAG_REG(cluster) \
			((cluster == 0) ? REG_SCBAKDATA8_OFFSET : \
			 REG_SCBAKDATA9_OFFSET)
#define CLUSTER_IDLE_BIT				BIT(8)
#define CLUSTER_IDLE_MASK		(CLUSTER_IDLE_BIT | 0x0F)

#define AP_SUSPEND_FLAG			(1 << 16)

#define CLUSTER_PWDN_IDLE		(0<<28)
#define CLUSTER_PWDN_HOTPLUG		(1<<28)
#define CLUSTER_PWDN_SR			(2<<28)

#define CLUSTER0_PDC_OFFSET			0x260
#define CLUSTER1_PDC_OFFSET			0x300

#define PDC_EN_OFFSET				0x0
#define PDC_COREPWRINTEN_OFFSET		0x4
#define PDC_COREPWRINTSTAT_OFFSET	0x8
#define PDC_COREGICMASK_OFFSET		0xc
#define PDC_COREPOWERUP_OFFSET		0x10
#define PDC_COREPOWERDN_OFFSET		0x14
#define PDC_COREPOWERSTAT_OFFSET	0x18

#define PDC_COREPWRSTAT_MASK   (0XFFFF)

enum pdc_gic_mask {
	PDC_MASK_GIC_WAKE_IRQ,
	PDC_UNMASK_GIC_WAKE_IRQ
};

enum pdc_finish_int_mask {
	PDC_DISABLE_FINISH_INT,
	PDC_ENABLE_FINISH_INT
};

static void hisi_resource_lock(unsigned int lockid, unsigned int offset)
{
	unsigned int lock_id = (lockid << 29);
	unsigned int lock_val =  lock_id | LOCK_BIT;
	unsigned int lock_state;

	do {
		mmio_write_32(offset, lock_val);
		lock_state = mmio_read_32(LOCK_STAT_OFFSET + (uintptr_t)offset);
	} while ((lock_state & LOCK_ID_MASK) != lock_id);
}

static void hisi_resource_unlock(unsigned int lockid, unsigned int offset)
{
	unsigned int lock_val = (lockid << 29) | LOCK_BIT;

	mmio_write_32((LOCK_UNLOCK_OFFSET + (uintptr_t)offset), lock_val);
}


static void hisi_cpuhotplug_lock(unsigned int cluster, unsigned int core)
{
	unsigned int lock_id;

	lock_id = (cluster << 2) + core;

	hisi_resource_lock(lock_id, RES2_LOCK_BASE);
}

static void hisi_cpuhotplug_unlock(unsigned int cluster, unsigned int core)
{
	unsigned int lock_id;

	lock_id = (cluster << 2) + core;

	hisi_resource_unlock(lock_id, RES2_LOCK_BASE);
}

/* get the resource lock */
void hisi_cpuidle_lock(unsigned int cluster, unsigned int core)
{
	unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE);

	hisi_resource_lock(CPUIDLE_LOCK_ID(core), offset);
}

/* release the resource lock */
void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core)
{
	unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE);

	hisi_resource_unlock(CPUIDLE_LOCK_ID(core), offset);
}

unsigned int hisi_get_cpuidle_flag(unsigned int cluster)
{
	unsigned int val;

	val = mmio_read_32(CPUIDLE_FLAG_REG(cluster));
	val &= 0xF;

	return val;
}

void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core)
{
	mmio_setbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core));
}

void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core)
{
	mmio_clrbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core));

}

int hisi_test_ap_suspend_flag(void)
{
	unsigned int val1;
	unsigned int val2;

	val1 = mmio_read_32(CPUIDLE_FLAG_REG(0));
	val1 &= AP_SUSPEND_FLAG;

	val2 = mmio_read_32(CPUIDLE_FLAG_REG(1));
	val2 &= AP_SUSPEND_FLAG;

	val1 |= val2;
	return (val1 != 0);
}

void hisi_set_cluster_pwdn_flag(unsigned int cluster,
				unsigned int core, unsigned int value)
{
	unsigned int val;

	hisi_cpuhotplug_lock(cluster, core);

	val = mmio_read_32(REG_SCBAKDATA3_OFFSET);
	val &= ~(0x3U << ((2 * cluster) + 28));
	val |= (value << (2 * cluster));
	mmio_write_32(REG_SCBAKDATA3_OFFSET, val);

	hisi_cpuhotplug_unlock(cluster, core);
}

unsigned int hisi_get_cpu_boot_flag(unsigned int cluster, unsigned int core)
{
	unsigned int val;

	hisi_cpuhotplug_lock(cluster, core);
	val = mmio_read_32(REG_SCBAKDATA3_OFFSET);
	val = val >> (16 + (cluster << 2));
	val &= 0xF;
	hisi_cpuhotplug_unlock(cluster, core);

	return val;
}

unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core)
{
	unsigned int val;

	hisi_cpuhotplug_lock(cluster, core);
	val = mmio_read_32(REG_SCBAKDATA3_OFFSET);
	val = val >> (16 + (cluster << 2));
	val &= 0xF;
	hisi_cpuhotplug_unlock(cluster, core);

	if (val)
		return 0;
	else
		return 1;
}

void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core)
{
	unsigned int flag = BIT((cluster<<2) + core + 16);

	hisi_cpuhotplug_lock(cluster, core);

	mmio_setbits_32(REG_SCBAKDATA3_OFFSET, flag);

	hisi_cpuhotplug_unlock(cluster, core);
}

void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core)
{
	unsigned int flag = BIT((cluster<<2) + core + 16);

	hisi_cpuhotplug_lock(cluster, core);

	mmio_clrbits_32(REG_SCBAKDATA3_OFFSET, flag);

	hisi_cpuhotplug_unlock(cluster, core);
}

int cluster_is_powered_on(unsigned int cluster)
{
	unsigned int val = mmio_read_32(REG_SCBAKDATA3_OFFSET);
	int ret;

	if (cluster == 0)
		ret = val & CLUSTER0_CPUS_ONLINE_MASK;
	else
		ret = val & CLUSTER1_CPUS_ONLINE_MASK;

	return !!ret;
}

static void *hisi_get_pdc_addr(unsigned int cluster)
{
	void *pdc_base_addr;
	uintptr_t addr;

	if (cluster == 0)
		addr = SOC_CRGPERIPH_A53_PDCEN_ADDR(CRG_BASE);
	else
		addr = SOC_CRGPERIPH_MAIA_PDCEN_ADDR(CRG_BASE);
	pdc_base_addr = (void *)addr;

	return pdc_base_addr;
}

static unsigned int hisi_get_pdc_stat(unsigned int cluster)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);
	unsigned int val;

	val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPOWERSTAT_OFFSET);

	return val;
}

static int check_hotplug(unsigned int cluster, unsigned int boot_flag)
{
	unsigned int mask = 0xF;

	if (hisi_test_ap_suspend_flag() ||
	    ((boot_flag & mask) == mask))
		return 0;

	return 1;
}

int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core)
{
	unsigned int mask = 0xf << (core * 4);
	unsigned int pdc_stat = hisi_get_pdc_stat(cluster);
	unsigned int boot_flag = hisi_get_cpu_boot_flag(cluster, core);
	unsigned int cpuidle_flag = hisi_get_cpuidle_flag(cluster);

	mask = (PDC_COREPWRSTAT_MASK & (~mask));
	pdc_stat &= mask;

	if ((boot_flag ^ cpuidle_flag) || pdc_stat ||
	    check_hotplug(cluster, boot_flag))
		return 0;
	else
		return 1;
}

void hisi_disable_pdc(unsigned int cluster)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	mmio_write_32((uintptr_t)pdc_base_addr, 0x0);
}

void hisi_enable_pdc(unsigned int cluster)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	mmio_write_32((uintptr_t)pdc_base_addr, 0x1);
}

void hisi_pdc_set_intmask(void *pdc_base_addr,
			unsigned int core,
			enum pdc_finish_int_mask intmask)
{
	unsigned int val;

	val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET);
	if (intmask == PDC_ENABLE_FINISH_INT)
		val |= BIT(core);
	else
		val &= ~BIT(core);

	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, val);
}

static inline void hisi_pdc_set_gicmask(void *pdc_base_addr,
					unsigned int core,
					enum pdc_gic_mask gicmask)
{
	unsigned int val;

	val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET);
	if (gicmask == PDC_MASK_GIC_WAKE_IRQ)
		val |= BIT(core);
	else
		val &= ~BIT(core);

	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET, val);
}

void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster)
{
	int i;
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	for (i = 0; i < 4; i++)
		hisi_pdc_set_gicmask(pdc_base_addr, i, PDC_MASK_GIC_WAKE_IRQ);
}

static void hisi_pdc_powerup_core(unsigned int cluster, unsigned int core,
				  enum pdc_gic_mask gicmask,
				  enum pdc_finish_int_mask intmask)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERUP_OFFSET,
		      BIT(core));
}

static void hisi_pdc_powerdn_core(unsigned int cluster, unsigned int core,
				  enum pdc_gic_mask gicmask,
				  enum pdc_finish_int_mask intmask)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET,
		      BIT(core));
}

void hisi_powerup_core(unsigned int cluster, unsigned int core)
{
	hisi_pdc_powerup_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ,
			      PDC_DISABLE_FINISH_INT);
}

void hisi_powerdn_core(unsigned int cluster, unsigned int core)
{
	hisi_pdc_powerdn_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ,
			      PDC_DISABLE_FINISH_INT);
}

void hisi_powerup_cluster(unsigned int cluster, unsigned int core)
{
	hisi_ipc_pm_on_off(core, cluster, PM_ON);
}

void hisi_powerdn_cluster(unsigned int cluster, unsigned int core)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_HOTPLUG);
	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET,
		      (0x10001 << core));
	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET,
		      BIT(core));
}

void hisi_enter_core_idle(unsigned int cluster, unsigned int core)
{
	hisi_pdc_powerdn_core(cluster, core, PDC_UNMASK_GIC_WAKE_IRQ,
			      PDC_DISABLE_FINISH_INT);
}

void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core)
{
	void *pdc_base_addr = hisi_get_pdc_addr(cluster);

	hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_IDLE);
	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET,
		      (0x10001 << core));
	mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET,
		      BIT(core));
}

void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core)
{
	hisi_ipc_pm_suspend(core, cluster, 0x3);
}