]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[dt] Provide dt_ioremap() to map device registers
authorMichael Brown <mcb30@ipxe.org>
Tue, 15 Apr 2025 19:19:17 +0000 (20:19 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 15 Apr 2025 19:39:28 +0000 (20:39 +0100)
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 <mcb30@ipxe.org>
src/drivers/bus/devtree.c
src/include/ipxe/devtree.h

index 35bd35c523120c98e2250d20f19a4a69fd8641f1..cbd8ea8fa8c59163f6317890c98e762076f98c5f 100644 (file)
@@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <errno.h>
 #include <ipxe/device.h>
 #include <ipxe/fdt.h>
+#include <ipxe/iomap.h>
 #include <ipxe/devtree.h>
 
 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;
index f31ddbd5b19d62df4f98fa16e01035298e70ad35..911e7ad1515bcdf111c341dfaa1b33e444b1eb05 100644 (file)
@@ -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 */