[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-05-09 16:26:41 +01:00
parent 3027864f13
commit f988ec09e0
4 changed files with 141 additions and 26 deletions

View File

@@ -563,6 +563,111 @@ int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name,
return 0; 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",
&regs->address_cells ) ) != 0 ) {
regs->address_cells = FDT_DEFAULT_ADDRESS_CELLS;
}
/* Read #size-cells, if present */
if ( ( rc = fdt_u32 ( fdt, offset, "#size-cells",
&regs->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 * Get MAC address from property
* *

View File

@@ -56,27 +56,25 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
unsigned int index, size_t len ) { unsigned int index, size_t len ) {
struct dt_device *parent = struct dt_device *parent =
container_of ( dt->dev.parent, struct dt_device, dev ); container_of ( dt->dev.parent, struct dt_device, dev );
struct fdt_reg_cells *regs = &parent->regs;
uint64_t address; uint64_t address;
uint64_t size; uint64_t size;
unsigned int cell;
void *io_addr; void *io_addr;
int rc; int rc;
/* Read address */ /* Read address */
cell = ( index * ( parent->address_cells + parent->size_cells ) ); if ( ( rc = fdt_reg_address ( &sysfdt, offset, regs, index,
if ( ( rc = fdt_cells ( &sysfdt, offset, "reg", cell, &address ) ) != 0 ) {
parent->address_cells, &address ) ) != 0 ) {
DBGC ( dt, "DT %s could not read region %d address: %s\n", DBGC ( dt, "DT %s could not read region %d address: %s\n",
dt->path, index, strerror ( rc ) ); dt->path, index, strerror ( rc ) );
return NULL; return NULL;
} }
cell += parent->address_cells;
/* Read size (or assume sufficient, if tree specifies no sizes) */ /* Read size (or assume sufficient, if tree specifies no sizes) */
size = len; size = len;
if ( parent->size_cells && if ( regs->size_cells &&
( rc = fdt_cells ( &sysfdt, offset, "reg", cell, ( ( rc = fdt_reg_size ( &sysfdt, offset, regs, index,
parent->size_cells, &size ) ) != 0 ) { &size ) ) != 0 ) ) {
DBGC ( dt, "DT %s could not read region %d size: %s\n", DBGC ( dt, "DT %s could not read region %d size: %s\n",
dt->path, index, strerror ( rc ) ); dt->path, index, strerror ( rc ) );
return NULL; 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 ); list_add_tail ( &dt->dev.siblings, &dt->dev.parent->children );
/* Read #address-cells and #size-cells, if present */ /* Read #address-cells and #size-cells, if present */
if ( ( rc = fdt_u32 ( &sysfdt, offset, "#address-cells", fdt_reg_cells ( &sysfdt, offset, &dt->regs );
&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;
}
/* Probe device */ /* Probe device */
if ( ( rc = dt_probe ( dt, offset ) ) != 0 ) if ( ( rc = dt_probe ( dt, offset ) ) != 0 )

View File

@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/device.h> #include <ipxe/device.h>
#include <ipxe/dma.h> #include <ipxe/dma.h>
#include <ipxe/fdt.h>
/** A devicetree device */ /** A devicetree device */
struct dt_device { struct dt_device {
@@ -25,18 +26,10 @@ struct dt_device {
/** Driver-private data */ /** Driver-private data */
void *priv; void *priv;
/** Number of address cells for child devices */ /** Register cell size specification */
uint32_t address_cells; struct fdt_reg_cells regs;
/** Number of size cells for child devices */
uint32_t size_cells;
}; };
/** 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 */ /** A devicetree driver */
struct dt_driver { struct dt_driver {
/** Driver name */ /** Driver name */

View File

@@ -124,6 +124,22 @@ struct fdt_descriptor {
int depth; 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 image_tag fdt_image __image_tag;
extern struct fdt sysfdt; extern struct fdt sysfdt;
@@ -144,6 +160,16 @@ extern int fdt_u64 ( struct fdt *fdt, unsigned int offset, const char *name,
uint64_t *value ); uint64_t *value );
extern int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name, extern int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name,
uint32_t *value ); 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, extern int fdt_mac ( struct fdt *fdt, unsigned int offset,
struct net_device *netdev ); struct net_device *netdev );
extern int fdt_parse ( struct fdt *fdt, struct fdt_header *hdr, extern int fdt_parse ( struct fdt *fdt, struct fdt_header *hdr,