From: Michael Brown Date: Tue, 15 Apr 2025 19:19:17 +0000 (+0100) Subject: [dt] Provide dt_ioremap() to map device registers X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eeec6442d9d422cbc7473f8dd9229c1acdc4084b;p=thirdparty%2Fipxe.git [dt] Provide dt_ioremap() to map device registers Devicetree devices encode register address ranges within the "reg" property, with the number of cells used for addresses and for sizes determined by the #address-cells and #size-cells properties of the immediate parent device. Record the number of address and size cells for each device, and provide a dt_ioremap() function to allow drivers to map a specified range without having to directly handle the "reg" property. Signed-off-by: Michael Brown --- diff --git a/src/drivers/bus/devtree.c b/src/drivers/bus/devtree.c index 35bd35c52..cbd8ea8fa 100644 --- a/src/drivers/bus/devtree.c +++ b/src/drivers/bus/devtree.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include static struct dt_driver dt_node_driver __dt_driver; @@ -41,6 +42,70 @@ struct root_device dt_root_device __root_device; static void dt_remove_children ( struct dt_device *parent ); +/** + * Map devicetree range + * + * @v dt Devicetree device + * @v offset Starting node offset + * @v index Region index + * @v len Length to map, or 0 to map whole region + * @ret io_addr I/O address, or NULL on error + */ +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 ); + 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 ) { + 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 ) { + DBGC ( dt, "DT %s could not read region %d size: %s\n", + dt->path, index, strerror ( rc ) ); + return NULL; + } + + /* Use region size as length if not specified */ + if ( ! len ) + len = size; + DBGC ( dt, "DT %s region %d at %#08llx+%#04llx\n", + dt->path, index, ( ( unsigned long long ) address ), + ( ( unsigned long long ) size ) ); + + /* Verify size */ + if ( len > size ) { + DBGC ( dt, "DT %s region %d is too small (%#llx/%#zx bytes)\n", + dt->path, index, ( ( unsigned long long ) size ), len ); + return NULL; + } + + /* Map region */ + io_addr = ioremap ( address, len ); + if ( ! io_addr ) { + DBGC ( dt, "DT %s could not map region %d\n", + dt->path, index ); + return NULL; + } + + return io_addr; +} + /** * Find devicetree driver * @@ -158,6 +223,16 @@ static int dt_probe_node ( struct dt_device *parent, unsigned int offset, INIT_LIST_HEAD ( &dt->dev.children ); 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; + } + /* Probe device */ if ( ( rc = dt_probe ( dt, offset ) ) != 0 ) goto err_probe; diff --git a/src/include/ipxe/devtree.h b/src/include/ipxe/devtree.h index f31ddbd5b..911e7ad15 100644 --- a/src/include/ipxe/devtree.h +++ b/src/include/ipxe/devtree.h @@ -21,8 +21,19 @@ struct dt_device { struct dt_driver *driver; /** 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; }; +/** 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 */ @@ -73,4 +84,7 @@ static inline void * dt_get_drvdata ( struct dt_device *dt ) { return dt->priv; } +extern void * dt_ioremap ( struct dt_device *dt, unsigned int offset, + unsigned int index, size_t len ); + #endif /* _IPXE_DEVTREE_H */