1 /* 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 <common/debug.h> 11 #include <common/fdt_wrappers.h> 12 #include <lib/fconf/fconf.h> 13 #include <lib/fconf/fconf_amu_getter.h> 14 #include <libfdt.h> 15 16 #include <plat/common/platform.h> 17 18 struct fconf_amu_config fconf_amu_config; 19 static struct amu_topology fconf_amu_topology_; 20 21 /* 22 * Populate the core-specific AMU structure with information retrieved from a 23 * device tree. 24 * 25 * Returns `0` on success, or a negative integer representing an error code. 26 */ 27 static int fconf_populate_amu_cpu_amu(const void *fdt, int parent, 28 struct amu_core *amu) 29 { 30 int ret = 0; 31 int node = 0; 32 33 fdt_for_each_subnode(node, fdt, parent) { 34 const char *name; 35 const char *value; 36 int len; 37 38 uintptr_t idx = 0U; 39 40 name = fdt_get_name(fdt, node, &len); 41 if (strncmp(name, "counter@", 8) != 0) { 42 continue; 43 } 44 45 ret = fdt_get_reg_props_by_index(fdt, node, 0, &idx, NULL); 46 if (ret < 0) { 47 break; 48 } 49 50 value = fdt_getprop(fdt, node, "enable-at-el3", &len); 51 if ((value == NULL) && (len != -FDT_ERR_NOTFOUND)) { 52 break; 53 } 54 55 if (len != -FDT_ERR_NOTFOUND) { 56 amu->enable |= (1 << idx); 57 } 58 } 59 60 if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { 61 return node; 62 } 63 64 return ret; 65 } 66 67 /* 68 * Within a `cpu` node, attempt to dereference the `amu` property, and populate 69 * the AMU information for the core. 70 * 71 * Returns `0` on success, or a negative integer representing an error code. 72 */ 73 static int fconf_populate_amu_cpu(const void *fdt, int node, uintptr_t mpidr) 74 { 75 int ret; 76 int idx; 77 78 uint32_t amu_phandle; 79 struct amu_core *amu; 80 81 ret = fdt_read_uint32(fdt, node, "amu", &amu_phandle); 82 if (ret < 0) { 83 if (ret == -FDT_ERR_NOTFOUND) { 84 ret = 0; 85 } 86 87 return ret; 88 } 89 90 node = fdt_node_offset_by_phandle(fdt, amu_phandle); 91 if (node < 0) { 92 return node; 93 } 94 95 idx = plat_core_pos_by_mpidr(mpidr); 96 if (idx < 0) { 97 return -FDT_ERR_BADVALUE; 98 } 99 100 amu = &fconf_amu_topology_.cores[idx]; 101 102 return fconf_populate_amu_cpu_amu(fdt, node, amu); 103 } 104 105 /* 106 * Populates the global `amu_topology` structure based on what's described by 107 * the hardware configuration device tree blob. 108 * 109 * The device tree is expected to provide an `amu` property for each `cpu` node, 110 * like so: 111 * 112 * cpu@0 { 113 * amu = <&cpu0_amu>; 114 * }; 115 * 116 * amus { 117 * cpu0_amu: amu-0 { 118 * counters { 119 * #address-cells = <2>; 120 * #size-cells = <0>; 121 * 122 * counter@x,y { 123 * reg = <x y>; // Group x, counter y 124 * }; 125 * }; 126 * }; 127 * }; 128 */ 129 static int fconf_populate_amu(uintptr_t config) 130 { 131 int ret = fdtw_for_each_cpu( 132 (const void *)config, fconf_populate_amu_cpu); 133 if (ret == 0) { 134 fconf_amu_config.topology = &fconf_amu_topology_; 135 } else { 136 ERROR("FCONF: failed to parse AMU information: %d\n", ret); 137 } 138 139 return ret; 140 } 141 142 FCONF_REGISTER_POPULATOR(HW_CONFIG, amu, fconf_populate_amu); 143