From: Michael Brown Date: Fri, 9 May 2025 15:26:41 +0000 (+0100) Subject: [fdt] Generalise access to "reg" property X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=f988ec09e01b54d21d1b1fa0e2b3121d926ed7df;p=thirdparty%2Fipxe.git [fdt] Generalise access to "reg" property The "reg" property is also used by non-device nodes, such as the nodes describing the system memory map. Provide generalised functionality for parsing the "#address-cells", "#size-cells", and "reg" properties. Signed-off-by: Michael Brown --- diff --git a/src/core/fdt.c b/src/core/fdt.c index 744e0e705..eee5b47b5 100644 --- a/src/core/fdt.c +++ b/src/core/fdt.c @@ -563,6 +563,111 @@ int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name, return 0; } +/** + * Get region cell size specification + * + * @v fdt Device tree + * @v offset Starting (parent) node offset + * @v regs Region cell size specification to fill in + * + * Note that #address-cells and #size-cells are defined on the + * immediate parent node, rather than on the node with the "reg" + * property itself. + */ +void fdt_reg_cells ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs ) { + int rc; + + /* Read #address-cells, if present */ + if ( ( rc = fdt_u32 ( fdt, offset, "#address-cells", + ®s->address_cells ) ) != 0 ) { + regs->address_cells = FDT_DEFAULT_ADDRESS_CELLS; + } + + /* Read #size-cells, if present */ + if ( ( rc = fdt_u32 ( fdt, offset, "#size-cells", + ®s->size_cells ) ) != 0 ) { + regs->size_cells = FDT_DEFAULT_SIZE_CELLS; + } + + /* Calculate stride */ + regs->stride = ( regs->address_cells + regs->size_cells ); +} + +/** + * Get number of regions + * + * @v fdt Device tree + * @v offset Starting node offset + * @v regs Region cell size specification + * @ret count Number of regions, or negative error + */ +int fdt_reg_count ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs ) { + struct fdt_descriptor desc; + const uint32_t *cell; + unsigned int count; + int rc; + + /* Find property */ + if ( ( rc = fdt_property ( fdt, offset, "reg", &desc ) ) != 0 ) + return rc; + + /* Determine number of regions */ + count = ( desc.len / ( regs->stride * sizeof ( *cell ) ) ); + return count; +} + +/** + * Get region address + * + * @v fdt Device tree + * @v offset Starting node offset + * @v regs Region cell size specification + * @v index Region index + * @v address Region starting address to fill in + * @ret rc Return status code + */ +int fdt_reg_address ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs, unsigned int index, + uint64_t *address ) { + unsigned int cell = ( index * regs->stride ); + int rc; + + /* Read relevant portion of region array */ + if ( ( rc = fdt_cells ( fdt, offset, "reg", cell, regs->address_cells, + address ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** + * Get region size + * + * @v fdt Device tree + * @v offset Starting node offset + * @v regs Region cell size specification + * @v index Region index + * @v size Region size to fill in + * @ret rc Return status code + */ +int fdt_reg_size ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs, unsigned int index, + uint64_t *size ) { + unsigned int cell = ( ( index * regs->stride ) + regs->address_cells ); + int rc; + + /* Read relevant portion of region array */ + if ( ( rc = fdt_cells ( fdt, offset, "reg", cell, regs->size_cells, + size ) ) != 0 ) { + return rc; + } + + return 0; +} + /** * Get MAC address from property * diff --git a/src/drivers/bus/devtree.c b/src/drivers/bus/devtree.c index 654f8d14f..55f68ee33 100644 --- a/src/drivers/bus/devtree.c +++ b/src/drivers/bus/devtree.c @@ -56,27 +56,25 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset, unsigned int index, size_t len ) { struct dt_device *parent = container_of ( dt->dev.parent, struct dt_device, dev ); + struct fdt_reg_cells *regs = &parent->regs; uint64_t address; uint64_t size; - unsigned int cell; void *io_addr; int rc; /* Read address */ - cell = ( index * ( parent->address_cells + parent->size_cells ) ); - if ( ( rc = fdt_cells ( &sysfdt, offset, "reg", cell, - parent->address_cells, &address ) ) != 0 ) { + if ( ( rc = fdt_reg_address ( &sysfdt, offset, regs, index, + &address ) ) != 0 ) { DBGC ( dt, "DT %s could not read region %d address: %s\n", dt->path, index, strerror ( rc ) ); return NULL; } - cell += parent->address_cells; /* Read size (or assume sufficient, if tree specifies no sizes) */ size = len; - if ( parent->size_cells && - ( rc = fdt_cells ( &sysfdt, offset, "reg", cell, - parent->size_cells, &size ) ) != 0 ) { + if ( regs->size_cells && + ( ( rc = fdt_reg_size ( &sysfdt, offset, regs, index, + &size ) ) != 0 ) ) { DBGC ( dt, "DT %s could not read region %d size: %s\n", dt->path, index, strerror ( rc ) ); return NULL; @@ -225,14 +223,7 @@ static int dt_probe_node ( struct dt_device *parent, unsigned int offset, list_add_tail ( &dt->dev.siblings, &dt->dev.parent->children ); /* Read #address-cells and #size-cells, if present */ - if ( ( rc = fdt_u32 ( &sysfdt, offset, "#address-cells", - &dt->address_cells ) ) != 0 ) { - dt->address_cells = DT_DEFAULT_ADDRESS_CELLS; - } - if ( ( rc = fdt_u32 ( &sysfdt, offset, "#size-cells", - &dt->size_cells ) ) != 0 ) { - dt->size_cells = DT_DEFAULT_SIZE_CELLS; - } + fdt_reg_cells ( &sysfdt, offset, &dt->regs ); /* Probe device */ if ( ( rc = dt_probe ( dt, offset ) ) != 0 ) diff --git a/src/include/ipxe/devtree.h b/src/include/ipxe/devtree.h index 04414f370..6e9286af4 100644 --- a/src/include/ipxe/devtree.h +++ b/src/include/ipxe/devtree.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include /** A devicetree device */ struct dt_device { @@ -25,18 +26,10 @@ struct dt_device { /** Driver-private data */ void *priv; - /** Number of address cells for child devices */ - uint32_t address_cells; - /** Number of size cells for child devices */ - uint32_t size_cells; + /** Register cell size specification */ + struct fdt_reg_cells regs; }; -/** Default number of address cells, if not specified */ -#define DT_DEFAULT_ADDRESS_CELLS 2 - -/** Default number of size cells, if not specified */ -#define DT_DEFAULT_SIZE_CELLS 1 - /** A devicetree driver */ struct dt_driver { /** Driver name */ diff --git a/src/include/ipxe/fdt.h b/src/include/ipxe/fdt.h index fb0ae7752..6c58f5687 100644 --- a/src/include/ipxe/fdt.h +++ b/src/include/ipxe/fdt.h @@ -124,6 +124,22 @@ struct fdt_descriptor { int depth; }; +/** A device tree region cell size specification */ +struct fdt_reg_cells { + /** Number of address cells */ + uint32_t address_cells; + /** Number of size cells */ + uint32_t size_cells; + /** Number of address cells plus number of size cells */ + unsigned int stride; +}; + +/** Default number of address cells, if not specified */ +#define FDT_DEFAULT_ADDRESS_CELLS 2 + +/** Default number of size cells, if not specified */ +#define FDT_DEFAULT_SIZE_CELLS 1 + extern struct image_tag fdt_image __image_tag; extern struct fdt sysfdt; @@ -144,6 +160,16 @@ extern int fdt_u64 ( struct fdt *fdt, unsigned int offset, const char *name, uint64_t *value ); extern int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name, uint32_t *value ); +extern void fdt_reg_cells ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs ); +extern int fdt_reg_count ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs ); +extern int fdt_reg_address ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs, unsigned int index, + uint64_t *address ); +extern int fdt_reg_size ( struct fdt *fdt, unsigned int offset, + struct fdt_reg_cells *regs, unsigned int index, + uint64_t *size ); extern int fdt_mac ( struct fdt *fdt, unsigned int offset, struct net_device *netdev ); extern int fdt_parse ( struct fdt *fdt, struct fdt_header *hdr,