1 /*
2  * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /*
8  * Driver for GIC600-specific features. This driver only overrides APIs that are
9  * different to those generic ones in GICv3 driver.
10  *
11  * GIC600 supports independently power-gating redistributor interface.
12  */
13 
14 #include <arch_helpers.h>
15 #include <assert.h>
16 #include <gicv3.h>
17 
18 #include "gicv3_private.h"
19 
20 /* GIC600-specific register offsets */
21 #define GICR_PWRR	0x24
22 
23 /* GICR_PWRR fields */
24 #define PWRR_RDPD_SHIFT		0
25 #define PWRR_RDGPD_SHIFT	2
26 #define PWRR_RDGPO_SHIFT	3
27 
28 #define PWRR_RDGPD	(1 << PWRR_RDGPD_SHIFT)
29 #define PWRR_RDGPO	(1 << PWRR_RDGPO_SHIFT)
30 
31 /* Values to write to GICR_PWRR register to power redistributor */
32 #define PWRR_ON		(0 << PWRR_RDPD_SHIFT)
33 #define PWRR_OFF	(1 << PWRR_RDPD_SHIFT)
34 
35 /* GIC600-specific accessor functions */
gicr_write_pwrr(uintptr_t base,unsigned int val)36 static void gicr_write_pwrr(uintptr_t base, unsigned int val)
37 {
38 	mmio_write_32(base + GICR_PWRR, val);
39 }
40 
gicr_read_pwrr(uintptr_t base)41 static uint32_t gicr_read_pwrr(uintptr_t base)
42 {
43 	return mmio_read_32(base + GICR_PWRR);
44 }
45 
gicr_group_powering_down(uint32_t pwrr)46 static int gicr_group_powering_down(uint32_t pwrr)
47 {
48 	/*
49 	 * Whether the redistributor group power down operation is in transit:
50 	 * i.e. it's intending to, but not finished yet.
51 	 */
52 	return ((pwrr & PWRR_RDGPD) && !(pwrr & PWRR_RDGPO));
53 }
54 
gic600_pwr_on(uintptr_t base)55 static void gic600_pwr_on(uintptr_t base)
56 {
57 	/* Power on redistributor */
58 	gicr_write_pwrr(base, PWRR_ON);
59 
60 	/* Wait until the power on state is reflected */
61 	while (gicr_read_pwrr(base) & PWRR_RDGPO)
62 		;
63 }
64 
gic600_pwr_off(uintptr_t base)65 static void gic600_pwr_off(uintptr_t base)
66 {
67 	/* Power off redistributor */
68 	gicr_write_pwrr(base, PWRR_OFF);
69 
70 	/*
71 	 * If this is the last man, turning this redistributor frame off will
72 	 * result in the group itself being powered off. In that case, wait as
73 	 * long as it's in transition, or has aborted the transition altogether
74 	 * for any reason.
75 	 */
76 	if (gicr_read_pwrr(base) & PWRR_RDGPD) {
77 		while (gicr_group_powering_down(gicr_read_pwrr(base)))
78 			;
79 	}
80 }
81 
gicv3_distif_pre_save(unsigned int proc_num)82 void gicv3_distif_pre_save(unsigned int proc_num)
83 {
84 	arm_gicv3_distif_pre_save(proc_num);
85 }
86 
gicv3_distif_post_restore(unsigned int proc_num)87 void gicv3_distif_post_restore(unsigned int proc_num)
88 {
89 	arm_gicv3_distif_post_restore(proc_num);
90 }
91 
92 /*
93  * Power off GIC600 redistributor
94  */
gicv3_rdistif_off(unsigned int proc_num)95 void gicv3_rdistif_off(unsigned int proc_num)
96 {
97 	uintptr_t gicr_base;
98 
99 	assert(gicv3_driver_data);
100 	assert(proc_num < gicv3_driver_data->rdistif_num);
101 	assert(gicv3_driver_data->rdistif_base_addrs);
102 
103 	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
104 	assert(gicr_base);
105 
106 	/* Attempt to power redistributor off */
107 	gic600_pwr_off(gicr_base);
108 }
109 
110 /*
111  * Power on GIC600 redistributor
112  */
gicv3_rdistif_on(unsigned int proc_num)113 void gicv3_rdistif_on(unsigned int proc_num)
114 {
115 	uintptr_t gicr_base;
116 
117 	assert(gicv3_driver_data);
118 	assert(proc_num < gicv3_driver_data->rdistif_num);
119 	assert(gicv3_driver_data->rdistif_base_addrs);
120 
121 	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
122 	assert(gicr_base);
123 
124 	/* Power redistributor on */
125 	gic600_pwr_on(gicr_base);
126 }
127