blob: c7fb80362f14807fe13296a88a8d8f89e1b4fe05 [file] [log] [blame]
Chris Kayf11909f2021-08-19 11:21:52 +01001/*
2 * Copyright (c) 2021, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <stddef.h>
8#include <stdint.h>
9
10#include "amu_private.h"
11#include <common/debug.h>
12#include <common/fdt_wrappers.h>
13#include <lib/extensions/amu.h>
14#include <lib/fconf/fconf.h>
15#include <libfdt.h>
16
17#include <plat/common/platform.h>
18
19static bool amu_topology_populated_ ; /* Whether the topology is valid */
20static struct amu_fconf_topology amu_topology_; /* Populated topology cache */
21
22const struct amu_fconf_topology *amu_topology(void)
23{
24 if (!amu_topology_populated_) {
25 return NULL;
26 }
27
28 return &amu_topology_;
29}
30
31/*
32 * Populate the core-specific AMU structure with information retrieved from a
33 * device tree.
34 *
35 * Returns `0` on success, or a negative integer representing an error code.
36 */
37static int amu_fconf_populate_cpu_amu(const void *fdt, int parent,
38 struct amu_fconf_core *amu)
39{
40 int ret = 0;
41 int node = 0;
42
43 fdt_for_each_subnode(node, fdt, parent) {
44 const char *name;
45 const char *value;
46 int len;
47
48 uintptr_t idx = 0U;
49
50 name = fdt_get_name(fdt, node, &len);
51 if (strncmp(name, "counter@", 8) != 0) {
52 continue;
53 }
54
55 ret = fdt_get_reg_props_by_index(fdt, node, 0, &idx, NULL);
56 if (ret < 0) {
57 break;
58 }
59
60 value = fdt_getprop(fdt, node, "enable-at-el3", &len);
61 if ((value == NULL) && (len != -FDT_ERR_NOTFOUND)) {
62 break;
63 }
64
65 if (len != -FDT_ERR_NOTFOUND) {
66 amu->enable |= (1 << idx);
67 }
68 }
69
70 if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
71 return node;
72 }
73
74 return ret;
75}
76
77/*
78 * Within a `cpu` node, attempt to dereference the `amu` property, and populate
79 * the AMU information for the core.
80 *
81 * Returns `0` on success, or a negative integer representing an error code.
82 */
83static int amu_fconf_populate_cpu(const void *fdt, int node, uintptr_t mpidr)
84{
85 int ret;
86 int idx;
87
88 uint32_t amu_phandle;
89 struct amu_fconf_core *amu;
90
91 ret = fdt_read_uint32(fdt, node, "amu", &amu_phandle);
92 if (ret < 0) {
93 if (ret == -FDT_ERR_NOTFOUND) {
94 ret = 0;
95 }
96
97 return ret;
98 }
99
100 node = fdt_node_offset_by_phandle(fdt, amu_phandle);
101 if (node < 0) {
102 return node;
103 }
104
105 idx = plat_core_pos_by_mpidr(mpidr);
106 amu = &amu_topology_.cores[idx];
107
108 return amu_fconf_populate_cpu_amu(fdt, node, amu);
109}
110
111/*
112 * For every CPU node (`/cpus/cpu@n`) in an FDT, executes a callback passing a
113 * pointer to the FDT and the offset of the CPU node. If the return value of the
114 * callback is negative, it is treated as an error and the loop is aborted. In
115 * this situation, the value of the callback is returned from the function.
116 *
117 * Returns `0` on success, or a negative integer representing an error code.
118 */
119static int amu_fconf_foreach_cpu(const void *fdt,
120 int (*callback)(const void *, int, uintptr_t))
121{
122 int ret = 0;
123 int parent, node = 0;
124
125 parent = fdt_path_offset(fdt, "/cpus");
126 if (parent < 0) {
127 if (parent == -FDT_ERR_NOTFOUND) {
128 parent = 0;
129 }
130
131 return parent;
132 }
133
134 fdt_for_each_subnode(node, fdt, parent) {
135 const char *name;
136 int len;
137
138 uintptr_t mpidr = 0U;
139
140 name = fdt_get_name(fdt, node, &len);
141 if (strncmp(name, "cpu@", 4) != 0) {
142 continue;
143 }
144
145 ret = fdt_get_reg_props_by_index(fdt, node, 0, &mpidr, NULL);
146 if (ret < 0) {
147 break;
148 }
149
150 ret = callback(fdt, node, mpidr);
151 if (ret < 0) {
152 break;
153 }
154 }
155
156 if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
157 return node;
158 }
159
160 return ret;
161}
162
163/*
164 * Populates the global `amu_topology` structure based on what's described by
165 * the hardware configuration device tree blob.
166 *
167 * The device tree is expected to provide an `amu` property for each `cpu` node,
168 * like so:
169 *
170 * cpu@0 {
171 * amu = <&cpu0_amu>;
172 * };
173 *
174 * amus {
175 * cpu0_amu: amu-0 {
176 * counters {
177 * #address-cells = <2>;
178 * #size-cells = <0>;
179 *
180 * counter@x,y {
181 * reg = <x y>; // Group x, counter y
182 * };
183 * };
184 * };
185 * };
186 */
187static int amu_fconf_populate(uintptr_t config)
188{
189 int ret = amu_fconf_foreach_cpu(
190 (const void *)config, amu_fconf_populate_cpu);
191 if (ret < 0) {
192 ERROR("AMU-FCONF: Failed to configure AMU: %d\n", ret);
193 } else {
194 amu_topology_populated_ = true;
195 }
196
197 return ret;
198}
199
200FCONF_REGISTER_POPULATOR(HW_CONFIG, amu, amu_fconf_populate);