mirror of
https://github.com/ipxe/ipxe
synced 2026-02-28 03:11:18 +03:00
[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 <mcb30@ipxe.org>
This commit is contained in:
@@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ipxe/device.h>
|
#include <ipxe/device.h>
|
||||||
#include <ipxe/fdt.h>
|
#include <ipxe/fdt.h>
|
||||||
|
#include <ipxe/iomap.h>
|
||||||
#include <ipxe/devtree.h>
|
#include <ipxe/devtree.h>
|
||||||
|
|
||||||
static struct dt_driver dt_node_driver __dt_driver;
|
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 );
|
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
|
* 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 );
|
INIT_LIST_HEAD ( &dt->dev.children );
|
||||||
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 */
|
||||||
|
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 */
|
/* Probe device */
|
||||||
if ( ( rc = dt_probe ( dt, offset ) ) != 0 )
|
if ( ( rc = dt_probe ( dt, offset ) ) != 0 )
|
||||||
goto err_probe;
|
goto err_probe;
|
||||||
|
|||||||
@@ -21,8 +21,19 @@ struct dt_device {
|
|||||||
struct dt_driver *driver;
|
struct dt_driver *driver;
|
||||||
/** Driver-private data */
|
/** Driver-private data */
|
||||||
void *priv;
|
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 */
|
/** A devicetree driver */
|
||||||
struct dt_driver {
|
struct dt_driver {
|
||||||
/** Driver name */
|
/** Driver name */
|
||||||
@@ -73,4 +84,7 @@ static inline void * dt_get_drvdata ( struct dt_device *dt ) {
|
|||||||
return dt->priv;
|
return dt->priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
||||||
|
unsigned int index, size_t len );
|
||||||
|
|
||||||
#endif /* _IPXE_DEVTREE_H */
|
#endif /* _IPXE_DEVTREE_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user