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