arm: fconf: Fix GICv3 dynamic configuration

At the moment the fconf_populate_gicv3_config() implementation is
somewhat incomplete: First it actually fails to store the retrieved
information (the local addr[] array is going nowhere), but also it makes
quite some assumptions about the device tree passed to it: it needs to
use two address-cells and two size-cells, and also requires all five
register regions to be specified, where actually only the first two
are mandatory according to the binding (and needed by our code).

Fix this by introducing a proper generic function to retrieve "reg"
property information from a DT node:
We retrieve the #address-cells and #size-cells properties from the
parent node, then use those to extract the right values from the "reg"
property. The function takes an index to select one region of a reg
property.

This is loosely based on the STM32 implementation using "reg-names",
which we will subsume in a follow-up patch.

Change-Id: Ia59bfdf80aea4e36876c7b6ed4d153e303f482e8
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
Andre Przywara 2020-03-26 11:57:43 +00:00
parent be858cffa9
commit 364ad245a2
6 changed files with 75 additions and 14 deletions

View File

@ -226,3 +226,53 @@ int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop,
return err;
}
static uint64_t fdt_read_prop_cells(const fdt32_t *prop, int nr_cells)
{
uint64_t reg = fdt32_to_cpu(prop[0]);
if (nr_cells > 1) {
reg = (reg << 32) | fdt32_to_cpu(prop[1]);
}
return reg;
}
int fdt_get_reg_props_by_index(const void *dtb, int node, int index,
uintptr_t *base, size_t *size)
{
const fdt32_t *prop;
int parent, len;
int ac, sc;
int cell;
parent = fdt_parent_offset(dtb, node);
if (parent < 0) {
return -FDT_ERR_BADOFFSET;
}
ac = fdt_address_cells(dtb, parent);
sc = fdt_size_cells(dtb, parent);
cell = index * (ac + sc);
prop = fdt_getprop(dtb, node, "reg", &len);
if (prop == NULL) {
WARN("Couldn't find \"reg\" property in dtb\n");
return -FDT_ERR_NOTFOUND;
}
if (((cell + ac + sc) * (int)sizeof(uint32_t)) > len) {
return -FDT_ERR_BADVALUE;
}
if (base != NULL) {
*base = (uintptr_t)fdt_read_prop_cells(&prop[cell], ac);
}
if (size != NULL) {
*size = (size_t)fdt_read_prop_cells(&prop[cell + ac], sc);
}
return 0;
}

View File

@ -28,5 +28,7 @@ int fdtw_read_bytes(const void *dtb, int node, const char *prop,
unsigned int length, void *value);
int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop,
unsigned int length, const void *data);
int fdt_get_reg_props_by_index(const void *dtb, int node, int index,
uintptr_t *base, size_t *size);
#endif /* FDT_WRAPPERS_H */

View File

@ -18,7 +18,7 @@ int fconf_populate_gicv3_config(uintptr_t config)
{
int err;
int node;
uint32_t addr[20];
uintptr_t addr;
/* Necessary to work with libfdt APIs */
const void *hw_config_dtb = (const void *)config;
@ -33,19 +33,24 @@ int fconf_populate_gicv3_config(uintptr_t config)
WARN("FCONF: Unable to locate node with arm,gic-v3 compatible property\n");
return 0;
}
/* Read the reg cell holding base address of GIC controller modules
A sample reg cell array is shown here:
reg = <0x0 0x2f000000 0 0x10000>, // GICD
<0x0 0x2f100000 0 0x200000>, // GICR
<0x0 0x2c000000 0 0x2000>, // GICC
<0x0 0x2c010000 0 0x2000>, // GICH
<0x0 0x2c02f000 0 0x2000>; // GICV
*/
err = fdt_read_uint32_array(hw_config_dtb, node, "reg", 20, addr);
/* The GICv3 DT binding holds at least two address/size pairs,
* the first describing the distributor, the second the redistributors.
* See: bindings/interrupt-controller/arm,gic-v3.yaml
*/
err = fdt_get_reg_props_by_index(hw_config_dtb, node, 0, &addr, NULL);
if (err < 0) {
ERROR("FCONF: Failed to read reg property of GIC node\n");
ERROR("FCONF: Failed to read GICD reg property of GIC node\n");
return err;
}
gicv3_config.gicd_base = addr;
err = fdt_get_reg_props_by_index(hw_config_dtb, node, 1, &addr, NULL);
if (err < 0) {
ERROR("FCONF: Failed to read GICR reg property of GIC node\n");
} else {
gicv3_config.gicr_base = addr;
}
return err;
}

View File

@ -15,8 +15,8 @@
#define hw_config__topology_getter(prop) soc_topology.prop
struct gicv3_config_t {
void *gicd_base;
void *gicr_base;
uintptr_t gicd_base;
uintptr_t gicr_base;
};
struct hw_topology_t {

View File

@ -25,6 +25,9 @@ fdt fdt_first_subnode
fdt fdt_next_subnode
fdt fdt_path_offset
fdt fdt_subnode_offset
fdt fdt_address_cells
fdt fdt_size_cells
fdt fdt_parent_offset
mbedtls mbedtls_asn1_get_alg
mbedtls mbedtls_asn1_get_alg_null
mbedtls mbedtls_asn1_get_bitstring_null

View File

@ -23,6 +23,7 @@ fdt fdt_node_offset_by_compatible
fdt fdt_setprop_inplace_namelen_partial
fdt fdt_first_subnode
fdt fdt_next_subnode
fdt fdt_parent_offset
mbedtls mbedtls_asn1_get_alg
mbedtls mbedtls_asn1_get_alg_null
mbedtls mbedtls_asn1_get_bitstring_null