aboutsummaryrefslogtreecommitdiff
path: root/plat/socionext/uniphier/uniphier_psci.c
blob: 82081a05165cc654069f630a0a3239ea8c619b6e (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
/*
 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <debug.h>
#include <mmio.h>
#include <psci.h>

#include "uniphier.h"

#define UNIPHIER_ROM_RSV0		0x59801200

#define UNIPHIER_SLFRSTSEL		0x61843010
#define   UNIPHIER_SLFRSTSEL_MASK		(0x3 << 0)
#define UNIPHIER_SLFRSTCTL		0x61843014
#define   UNIPHIER_SLFRSTCTL_RST		(1 << 0)

#define MPIDR_AFFINITY_INVALID		((u_register_t)-1)

uintptr_t uniphier_sec_entrypoint;

void uniphier_warmboot_entrypoint(void);
void __dead2 uniphier_fake_pwr_down(void);
u_register_t uniphier_holding_pen_release;
static int uniphier_psci_scp_mode;

static int uniphier_psci_pwr_domain_on(u_register_t mpidr)
{
	uniphier_holding_pen_release = mpidr;
	flush_dcache_range((uint64_t)&uniphier_holding_pen_release,
			   sizeof(uniphier_holding_pen_release));

	mmio_write_64(UNIPHIER_ROM_RSV0,
		      (uint64_t)&uniphier_warmboot_entrypoint);
	sev();

	return PSCI_E_SUCCESS;
}

static void uniphier_psci_pwr_domain_off(const psci_power_state_t *target_state)
{
	uniphier_gic_cpuif_disable();
}

static void uniphier_psci_pwr_domain_on_finish(
					const psci_power_state_t *target_state)
{
	uniphier_gic_pcpu_init();
	uniphier_gic_cpuif_enable();

	uniphier_cci_enable();
}

static void __dead2 uniphier_psci_pwr_domain_pwr_down_wfi(
					const psci_power_state_t *target_state)
{
	/*
	 * The Boot ROM cannot distinguish warn and cold resets.
	 * Instead of the CPU reset, fake it.
	 */
	uniphier_holding_pen_release = MPIDR_AFFINITY_INVALID;
	flush_dcache_range((uint64_t)&uniphier_holding_pen_release,
			   sizeof(uniphier_holding_pen_release));

	uniphier_fake_pwr_down();
}

static void uniphier_self_system_reset(void)
{
	mmio_clrbits_32(UNIPHIER_SLFRSTSEL, UNIPHIER_SLFRSTSEL_MASK);
	mmio_setbits_32(UNIPHIER_SLFRSTCTL, UNIPHIER_SLFRSTCTL_RST);
}

static void __dead2 uniphier_psci_system_off(void)
{
	if (uniphier_psci_scp_mode) {
		uniphier_scp_system_off();
	} else {
		NOTICE("SCP is disabled; can't shutdown the system.\n");
		NOTICE("Resetting the system instead.\n");
		uniphier_self_system_reset();
	}

	wfi();
	ERROR("UniPhier System Off: operation not handled.\n");
	panic();
}

static void __dead2 uniphier_psci_system_reset(void)
{
	if (uniphier_psci_scp_mode)
		uniphier_scp_system_reset();
	else
		uniphier_self_system_reset();

	wfi();
	ERROR("UniPhier System Reset: operation not handled.\n");
	panic();
}

static const struct plat_psci_ops uniphier_psci_ops = {
	.pwr_domain_on = uniphier_psci_pwr_domain_on,
	.pwr_domain_off = uniphier_psci_pwr_domain_off,
	.pwr_domain_on_finish = uniphier_psci_pwr_domain_on_finish,
	.pwr_domain_pwr_down_wfi = uniphier_psci_pwr_domain_pwr_down_wfi,
	.system_off = uniphier_psci_system_off,
	.system_reset = uniphier_psci_system_reset,
};

int plat_setup_psci_ops(uintptr_t sec_entrypoint,
			const struct plat_psci_ops **psci_ops)
{
	uniphier_sec_entrypoint = sec_entrypoint;
	flush_dcache_range((uint64_t)&uniphier_sec_entrypoint,
			   sizeof(uniphier_sec_entrypoint));

	uniphier_psci_scp_mode = uniphier_scp_is_running();
	flush_dcache_range((uint64_t)&uniphier_psci_scp_mode,
			   sizeof(uniphier_psci_scp_mode));

	if (uniphier_psci_scp_mode)
		uniphier_scp_open_com();

	*psci_ops = &uniphier_psci_ops;

	return 0;
}