]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fdt] Generalise access to "reg" property
authorMichael Brown <mcb30@ipxe.org>
Fri, 9 May 2025 15:26:41 +0000 (16:26 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 9 May 2025 18:09:57 +0000 (19:09 +0100)
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>
src/core/fdt.c
src/drivers/bus/devtree.c
src/include/ipxe/devtree.h
src/include/ipxe/fdt.h

index 744e0e7054fc93decbd66efe63712b980c1febb9..eee5b47b57186d8617c16048c6a8f2ca57d44619 100644 (file)
@@ -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",
+                             &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
  *
index 654f8d14fffeed3ebcbd2177810b4eb5f5657b73..55f68ee33cfdb12a034e392dfb0f97d1286c6b6c 100644 (file)
@@ -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 )
index 04414f370280181e3494bf77bd8b02015b34e256..6e9286af44d33a76e5bd5277a6bf2888658dc423 100644 (file)
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <ipxe/device.h>
 #include <ipxe/dma.h>
+#include <ipxe/fdt.h>
 
 /** 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 */
index fb0ae775229c53e3a7436ee172a64bfecf966bbb..6c58f568728b4f62e26057fe9f14630f53801e66 100644 (file)
@@ -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,