mirror of
https://github.com/ipxe/ipxe
synced 2026-02-14 02:31:26 +03:00
[dt] Allow for creation of standalone devices
We will want to be able to create the console device as early as possible. Refactor devicetree probing to remove the assumption that a devicetree device must have a devicetree parent, and expose functions to allow a standalone device to be created given only the offset of a node within the tree. The full device path is no longer trivial to construct with this assumption removed. The full path is currently used only for debug messages. Remove the stored full path, use just the node name for debug messages, and ensure that the topology information previously visible in the full path is reconstructible from the combined debug output if needed. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -39,7 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||||||
#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;
|
||||||
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 );
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
/* Get parent node */
|
/* Get parent node */
|
||||||
if ( ( rc = fdt_parent ( &sysfdt, offset, &parent ) ) != 0 ) {
|
if ( ( rc = fdt_parent ( &sysfdt, offset, &parent ) ) != 0 ) {
|
||||||
DBGC ( dt, "DT %s could not locate parent: %s\n",
|
DBGC ( dt, "DT %s could not locate parent: %s\n",
|
||||||
dt->path, strerror ( rc ) );
|
dt->name, strerror ( rc ) );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
if ( ( rc = fdt_reg_address ( &sysfdt, offset, ®s, index,
|
if ( ( rc = fdt_reg_address ( &sysfdt, offset, ®s, index,
|
||||||
&address ) ) != 0 ) {
|
&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->name, index, strerror ( rc ) );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +84,7 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
( ( rc = fdt_reg_size ( &sysfdt, offset, ®s, index,
|
( ( rc = fdt_reg_size ( &sysfdt, offset, ®s, index,
|
||||||
&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->name, index, strerror ( rc ) );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,13 +92,13 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
if ( ! len )
|
if ( ! len )
|
||||||
len = size;
|
len = size;
|
||||||
DBGC ( dt, "DT %s region %d at %#08llx+%#04llx\n",
|
DBGC ( dt, "DT %s region %d at %#08llx+%#04llx\n",
|
||||||
dt->path, index, ( ( unsigned long long ) address ),
|
dt->name, index, ( ( unsigned long long ) address ),
|
||||||
( ( unsigned long long ) size ) );
|
( ( unsigned long long ) size ) );
|
||||||
|
|
||||||
/* Verify size */
|
/* Verify size */
|
||||||
if ( len > size ) {
|
if ( len > size ) {
|
||||||
DBGC ( dt, "DT %s region %d is too small (%#llx/%#zx bytes)\n",
|
DBGC ( dt, "DT %s region %d is too small (%#llx/%#zx bytes)\n",
|
||||||
dt->path, index, ( ( unsigned long long ) size ), len );
|
dt->name, index, ( ( unsigned long long ) size ), len );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
io_addr = ioremap ( address, len );
|
io_addr = ioremap ( address, len );
|
||||||
if ( ! io_addr ) {
|
if ( ! io_addr ) {
|
||||||
DBGC ( dt, "DT %s could not map region %d\n",
|
DBGC ( dt, "DT %s could not map region %d\n",
|
||||||
dt->path, index );
|
dt->name, index );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
|||||||
*
|
*
|
||||||
* @v dt Devicetree device
|
* @v dt Devicetree device
|
||||||
* @v offset Starting node offset
|
* @v offset Starting node offset
|
||||||
* @ret driver Driver, or NULL
|
* @ret driver Driver
|
||||||
*/
|
*/
|
||||||
static struct dt_driver * dt_find_driver ( struct dt_device *dt,
|
static struct dt_driver * dt_find_driver ( struct dt_device *dt,
|
||||||
unsigned int offset ) {
|
unsigned int offset ) {
|
||||||
@@ -135,19 +134,20 @@ static struct dt_driver * dt_find_driver ( struct dt_device *dt,
|
|||||||
/* Look for a compatible driver */
|
/* Look for a compatible driver */
|
||||||
for ( id = ids ; count-- ; id += ( strlen ( id ) + 1 ) ) {
|
for ( id = ids ; count-- ; id += ( strlen ( id ) + 1 ) ) {
|
||||||
DBGC2 ( &sysfdt, "DT %s is compatible with %s\n",
|
DBGC2 ( &sysfdt, "DT %s is compatible with %s\n",
|
||||||
dt->path, id );
|
dt->name, id );
|
||||||
for_each_table_entry ( driver, DT_DRIVERS ) {
|
for_each_table_entry ( driver, DT_DRIVERS ) {
|
||||||
for ( i = 0 ; i < driver->id_count ; i++ ) {
|
for ( i = 0 ; i < driver->id_count ; i++ ) {
|
||||||
if ( strcmp ( id, driver->ids[i] ) == 0 ) {
|
if ( strcmp ( id, driver->ids[i] ) == 0 ) {
|
||||||
DBGC ( dt, "DT %s has %s driver %s\n",
|
DBGC ( dt, "DT %s has %s driver %s\n",
|
||||||
dt->path, id, driver->name );
|
dt->name, id, driver->name );
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
/* Use generic node driver if no other driver matches */
|
||||||
|
return &dt_node_driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,19 +158,11 @@ static struct dt_driver * dt_find_driver ( struct dt_device *dt,
|
|||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int dt_probe ( struct dt_device *dt, unsigned int offset ) {
|
static int dt_probe ( struct dt_device *dt, unsigned int offset ) {
|
||||||
struct dt_driver *driver = NULL;
|
struct dt_driver *driver;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Identify driver. Use the generic node driver if no other
|
/* Identify driver */
|
||||||
* driver matches, or if this is the root device (which has no
|
driver = dt_find_driver ( dt, offset );
|
||||||
* valid devicetree parent).
|
|
||||||
*/
|
|
||||||
if ( offset > 0 )
|
|
||||||
driver = dt_find_driver ( dt, offset );
|
|
||||||
if ( ! driver )
|
|
||||||
driver = &dt_node_driver;
|
|
||||||
|
|
||||||
/* Record driver */
|
|
||||||
dt->driver = driver;
|
dt->driver = driver;
|
||||||
dt->dev.driver_name = driver->name;
|
dt->dev.driver_name = driver->name;
|
||||||
|
|
||||||
@@ -178,7 +170,7 @@ static int dt_probe ( struct dt_device *dt, unsigned int offset ) {
|
|||||||
if ( ( rc = driver->probe ( dt, offset ) ) != 0 ) {
|
if ( ( rc = driver->probe ( dt, offset ) ) != 0 ) {
|
||||||
if ( driver != &dt_node_driver ) {
|
if ( driver != &dt_node_driver ) {
|
||||||
DBGC ( dt, "DT %s could not probe: %s\n",
|
DBGC ( dt, "DT %s could not probe: %s\n",
|
||||||
dt->path, strerror ( rc ) );
|
dt->name, strerror ( rc ) );
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -192,44 +184,44 @@ static int dt_probe ( struct dt_device *dt, unsigned int offset ) {
|
|||||||
* @v dt Devicetree device
|
* @v dt Devicetree device
|
||||||
*/
|
*/
|
||||||
static void dt_remove ( struct dt_device *dt ) {
|
static void dt_remove ( struct dt_device *dt ) {
|
||||||
|
struct dt_driver *driver = dt->driver;
|
||||||
|
|
||||||
/* Remove device */
|
/* Remove device */
|
||||||
dt->driver->remove ( dt );
|
driver->remove ( dt );
|
||||||
|
if ( driver != &dt_node_driver )
|
||||||
|
DBGC ( dt, "DT %s removed\n", dt->name );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Probe devicetree node
|
* Probe devicetree node
|
||||||
*
|
*
|
||||||
* @v parent Parent device, or NULL for root of tree
|
* @v parent Parent generic device
|
||||||
* @v offset Starting node offset
|
* @v offset Starting node offset
|
||||||
* @v name Node name
|
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int dt_probe_node ( struct dt_device *parent, unsigned int offset,
|
int dt_probe_node ( struct device *parent, unsigned int offset ) {
|
||||||
const char *name ) {
|
struct fdt_descriptor desc;
|
||||||
struct dt_device *dt;
|
struct dt_device *dt;
|
||||||
const char *ppath;
|
const char *name;
|
||||||
size_t path_len;
|
|
||||||
void *path;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/* Describe token */
|
||||||
|
if ( ( rc = fdt_describe ( &sysfdt, offset, &desc ) ) != 0 )
|
||||||
|
goto err_describe;
|
||||||
|
|
||||||
/* Allocate and initialise device */
|
/* Allocate and initialise device */
|
||||||
ppath = ( ( parent && parent->path[1] ) ? parent->path : "" );
|
dt = zalloc ( sizeof ( *dt ) );
|
||||||
path_len = ( strlen ( ppath ) + 1 /* "/" */ +
|
|
||||||
strlen ( name ) + 1 /* NUL */ );
|
|
||||||
dt = zalloc ( sizeof ( *dt ) + path_len );
|
|
||||||
if ( ! dt ) {
|
if ( ! dt ) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
}
|
}
|
||||||
path = ( ( ( void * ) dt ) + sizeof ( *dt ) );
|
name = ( offset ? desc.name : "root node" );
|
||||||
sprintf ( path, "%s/%s", ppath, name );
|
dt->name = dt->dev.name;
|
||||||
dt->path = path;
|
|
||||||
snprintf ( dt->dev.name, sizeof ( dt->dev.name ), "%s", name );
|
snprintf ( dt->dev.name, sizeof ( dt->dev.name ), "%s", name );
|
||||||
dt->dev.desc.bus_type = BUS_TYPE_DT;
|
dt->dev.desc.bus_type = BUS_TYPE_DT;
|
||||||
dt->dev.parent = ( parent ? &parent->dev : &dt_root_device.dev );
|
dt->dev.parent = parent;
|
||||||
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, &parent->children );
|
||||||
|
|
||||||
/* Probe device */
|
/* Probe device */
|
||||||
if ( ( rc = dt_probe ( dt, offset ) ) != 0 )
|
if ( ( rc = dt_probe ( dt, offset ) ) != 0 )
|
||||||
@@ -242,15 +234,22 @@ static int dt_probe_node ( struct dt_device *parent, unsigned int offset,
|
|||||||
list_del ( &dt->dev.siblings );
|
list_del ( &dt->dev.siblings );
|
||||||
free ( dt );
|
free ( dt );
|
||||||
err_alloc:
|
err_alloc:
|
||||||
|
err_describe:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove devicetree node
|
* Remove devicetree node
|
||||||
*
|
*
|
||||||
* @v dt Devicetree device
|
* @v parent Parent generic device
|
||||||
*/
|
*/
|
||||||
static void dt_remove_node ( struct dt_device *dt ) {
|
void dt_remove_node ( struct device *parent ) {
|
||||||
|
struct dt_device *dt;
|
||||||
|
|
||||||
|
/* Identify most recently added child */
|
||||||
|
dt = list_last_entry ( &parent->children, struct dt_device,
|
||||||
|
dev.siblings );
|
||||||
|
assert ( dt != NULL );
|
||||||
|
|
||||||
/* Remove driver */
|
/* Remove driver */
|
||||||
dt_remove ( dt );
|
dt_remove ( dt );
|
||||||
@@ -279,7 +278,7 @@ static int dt_probe_children ( struct dt_device *parent,
|
|||||||
/* Describe token */
|
/* Describe token */
|
||||||
if ( ( rc = fdt_describe ( &sysfdt, offset, &desc ) ) != 0 ) {
|
if ( ( rc = fdt_describe ( &sysfdt, offset, &desc ) ) != 0 ) {
|
||||||
DBGC ( &sysfdt, "DT %s has malformed node: %s\n",
|
DBGC ( &sysfdt, "DT %s has malformed node: %s\n",
|
||||||
parent->path, strerror ( rc ) );
|
parent->name, strerror ( rc ) );
|
||||||
goto err_describe;
|
goto err_describe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,8 +287,11 @@ static int dt_probe_children ( struct dt_device *parent,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* Probe child node, if applicable */
|
/* Probe child node, if applicable */
|
||||||
if ( ( depth == 0 ) && desc.name && ( ! desc.data ) )
|
if ( ( depth == 0 ) && desc.name && ( ! desc.data ) ) {
|
||||||
dt_probe_node ( parent, desc.offset, desc.name );
|
DBGC2 ( &sysfdt, "DT %s is child of %s\n",
|
||||||
|
desc.name, parent->name );
|
||||||
|
dt_probe_node ( &parent->dev, desc.offset );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fail if we have no children (so that this device will be freed) */
|
/* Fail if we have no children (so that this device will be freed) */
|
||||||
@@ -312,15 +314,10 @@ static int dt_probe_children ( struct dt_device *parent,
|
|||||||
* @v parent Parent device
|
* @v parent Parent device
|
||||||
*/
|
*/
|
||||||
static void dt_remove_children ( struct dt_device *parent ) {
|
static void dt_remove_children ( struct dt_device *parent ) {
|
||||||
struct dt_device *dt;
|
|
||||||
struct dt_device *tmp;
|
|
||||||
|
|
||||||
/* Remove all child nodes */
|
/* Remove all child nodes */
|
||||||
list_for_each_entry_safe ( dt, tmp, &parent->dev.children,
|
while ( ! list_empty ( &parent->dev.children ) )
|
||||||
dev.siblings ) {
|
dt_remove_node ( &parent->dev );
|
||||||
dt_remove_node ( dt );
|
|
||||||
}
|
|
||||||
assert ( list_empty ( &parent->dev.children ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generic node driver */
|
/** Generic node driver */
|
||||||
@@ -336,10 +333,10 @@ static struct dt_driver dt_node_driver __dt_driver = {
|
|||||||
* @v rootdev Devicetree root device
|
* @v rootdev Devicetree root device
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int dt_probe_all ( struct root_device *rootdev __unused ) {
|
static int dt_probe_all ( struct root_device *rootdev ) {
|
||||||
|
|
||||||
/* Probe root node */
|
/* Probe root node */
|
||||||
return dt_probe_node ( NULL, 0, "" );
|
return dt_probe_node ( &rootdev->dev, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,13 +345,9 @@ static int dt_probe_all ( struct root_device *rootdev __unused ) {
|
|||||||
* @v rootdev Devicetree root device
|
* @v rootdev Devicetree root device
|
||||||
*/
|
*/
|
||||||
static void dt_remove_all ( struct root_device *rootdev ) {
|
static void dt_remove_all ( struct root_device *rootdev ) {
|
||||||
struct dt_device *dt;
|
|
||||||
|
|
||||||
/* Remove root node */
|
/* Remove root node */
|
||||||
dt = list_first_entry ( &rootdev->dev.children, struct dt_device,
|
dt_remove_node ( &rootdev->dev );
|
||||||
dev.siblings );
|
|
||||||
assert ( dt != NULL );
|
|
||||||
dt_remove_node ( dt );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Devicetree bus root device driver */
|
/** Devicetree bus root device driver */
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||||||
|
|
||||||
/** A devicetree device */
|
/** A devicetree device */
|
||||||
struct dt_device {
|
struct dt_device {
|
||||||
|
/** Device name */
|
||||||
|
const char *name;
|
||||||
/** Generic device */
|
/** Generic device */
|
||||||
struct device dev;
|
struct device dev;
|
||||||
/** DMA device */
|
/** DMA device */
|
||||||
struct dma_device dma;
|
struct dma_device dma;
|
||||||
/** Device path */
|
|
||||||
const char *path;
|
|
||||||
/** Driver for this device */
|
/** Driver for this device */
|
||||||
struct dt_driver *driver;
|
struct dt_driver *driver;
|
||||||
/** Driver-private data */
|
/** Driver-private data */
|
||||||
@@ -79,5 +79,7 @@ static inline void * dt_get_drvdata ( struct dt_device *dt ) {
|
|||||||
|
|
||||||
extern void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
extern void * dt_ioremap ( struct dt_device *dt, unsigned int offset,
|
||||||
unsigned int index, size_t len );
|
unsigned int index, size_t len );
|
||||||
|
extern int dt_probe_node ( struct device *parent, unsigned int offset );
|
||||||
|
extern void dt_remove_node ( struct device *parent );
|
||||||
|
|
||||||
#endif /* _IPXE_DEVTREE_H */
|
#endif /* _IPXE_DEVTREE_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user