aboutsummaryrefslogtreecommitdiff
path: root/drivers/arm/gic/v3/arm_gicv3_common.c
blob: c8097329517cfb03bdfc77cad9ea46e96ab0346b (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
/*
 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*
 * Driver for implementation defined features that are identical in ARM GICv3
* implementations (GIC-500 and GIC-600 for now). This driver only overrides
* APIs that are different to those generic ones in GICv3 driver.
 */

#include <arch_helpers.h>
#include <assert.h>
#include <gicv3.h>

#include "gicv3_private.h"
#include "arm_gicv3_common.h"

/*
 * Flush the internal GIC cache of the LPIs pending tables to memory before
 * saving the state of the Redistributor. This is required before powering off
 * the GIC when the pending status must be preserved.
 * `rdist_proc_num` is the processor number corresponding to the Redistributor of the
 * current CPU.
 */
void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)
{
	uintptr_t gicr_base = 0;

	assert(gicv3_driver_data);
	assert(gicv3_driver_data->rdistif_base_addrs);

	/*
	 * The GICR_WAKER.Sleep bit should be set only when both
	 * GICR_WAKER.ChildrenAsleep and GICR_WAKER.ProcessorSleep are set on
	 * all the Redistributors.
	 */
	for (unsigned int i = 0; i < gicv3_driver_data->rdistif_num; i++) {
		gicr_base = gicv3_driver_data->rdistif_base_addrs[i];
		assert(gicr_base);
		assert(gicr_read_waker(gicr_base) & WAKER_CA_BIT);
		assert(gicr_read_waker(gicr_base) & WAKER_PS_BIT);
	}

	gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num];
	/*
	 * According to the TRM, there is only one instance of the
	 * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed
	 * through any of the Redistributor.
	 */

	/*
	 * Set GICR_WAKER.Sleep
	 * After this point, the system must be configured so that the
	 * wake_request signals for the right cores are asserted when a wakeup
	 * interrupt is detected. The GIC will not be able to do that anymore
	 * when the GICR_WAKER.Sleep bit is set to 1.
	 */
	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_SL_BIT);

	/* Wait until the GICR_WAKER.Quiescent bit is set */
	while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT))
		;
}

/*
 * Allow the LPIs pending state to be read back from the tables in memory after
 * having restored the state of the GIC Redistributor.
 */
void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)
{
	uintptr_t gicr_base;

	assert(gicv3_driver_data);
	assert(gicv3_driver_data->rdistif_base_addrs);

	/*
	 * According to the TRM, there is only one instance of the
	 * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed
	 * through any of the Redistributor.
	 */
	gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num];
	assert(gicr_base);

	/*
	 * If the GIC had power removed, the GICR_WAKER state will be reset.
	 * Since the GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits are cleared,
	 * we can exit early. This also prevents the following assert from
	 * erroneously triggering.
	 */
	if (!(gicr_read_waker(gicr_base) & WAKER_SL_BIT))
		return;

	/*
	 * Writes to GICR_WAKER.Sleep bit are ignored if GICR_WAKER.Quiescent
	 * bit is not set. We should be alright on power on path, therefore
	 * coming out of sleep and Quiescent should be set, but we assert in
	 * case.
	 */
	assert(gicr_read_waker(gicr_base) & WAKER_QSC_BIT);

	/* Clear GICR_WAKER.Sleep */
	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_SL_BIT);

	/*
	 * We don't know if the effects of setting GICR_WAKER.Sleep bit is
	 * instantaneous, so we wait until the interface is not Quiescent
	 * anymore.
	 */
	while (gicr_read_waker(gicr_base) & WAKER_QSC_BIT)
		;
}