Browse Source
1. add driver/of/* 2. add driver/inc/of.h 3. add board.unflatten(addr) to `board' lua object. Signed-off-by: surenyi <surenyi82@163.com>master
surenyi
6 years ago
9 changed files with 848 additions and 0 deletions
@ -0,0 +1,258 @@ |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include <stdint.h> |
|||
#include <ctype.h> |
|||
#include "utils.h" |
|||
#include "libfdt.h" |
|||
#include "of.h" |
|||
#include "of_private.h" |
|||
|
|||
LIST_HEAD(aliases_lookup); |
|||
|
|||
struct device_node *of_aliases; |
|||
struct device_node *of_root; |
|||
struct device_node *of_chosen; |
|||
struct device_node *of_stdout; |
|||
static const char *of_stdout_options; |
|||
|
|||
static struct device_node *__of_get_next_child(const struct device_node *node, |
|||
struct device_node *prev) |
|||
{ |
|||
struct device_node *next; |
|||
|
|||
if (!node) |
|||
return NULL; |
|||
|
|||
next = prev ? prev->sibling : node->child; |
|||
for (; next; next = next->sibling) |
|||
if (next) |
|||
break; |
|||
return next; |
|||
} |
|||
|
|||
#define __for_each_child_of_node(parent, child) \ |
|||
for (child = __of_get_next_child(parent, NULL); child != NULL; \ |
|||
child = __of_get_next_child(parent, child)) |
|||
|
|||
static inline struct device_node *of_find_node_by_path(const char *path) |
|||
{ |
|||
return of_find_node_opts_by_path(path, NULL); |
|||
} |
|||
|
|||
static struct device_node *__of_find_node_by_path(struct device_node *parent, |
|||
const char *path) |
|||
{ |
|||
struct device_node *child; |
|||
int len; |
|||
|
|||
len = strcspn(path, "/:"); |
|||
if (!len) |
|||
return NULL; |
|||
|
|||
__for_each_child_of_node(parent, child) { |
|||
const char *name = strrchr(child->full_name, '/'); |
|||
if (!name) { |
|||
printf("malformed device_node %s\n", child->full_name); |
|||
continue; |
|||
} |
|||
name++; |
|||
if (strncmp(path, name, len) == 0 && (strlen(name) == len)) |
|||
return child; |
|||
} |
|||
return NULL; |
|||
} |
|||
|
|||
/**
|
|||
* of_find_node_opts_by_path - Find a node matching a full OF path |
|||
* @path: Either the full path to match, or if the path does not |
|||
* start with '/', the name of a property of the /aliases |
|||
* node (an alias). In the case of an alias, the node |
|||
* matching the alias' value will be returned. |
|||
* @opts: Address of a pointer into which to store the start of |
|||
* an options string appended to the end of the path with |
|||
* a ':' separator. |
|||
* |
|||
* Valid paths: |
|||
* /foo/bar Full path |
|||
* foo Valid alias |
|||
* foo/bar Valid alias + relative path |
|||
* |
|||
* Returns a node pointer with refcount incremented, use |
|||
* of_node_put() on it when done. |
|||
*/ |
|||
struct device_node *of_find_node_opts_by_path(const char *path, const char **opts) |
|||
{ |
|||
struct device_node *np = NULL; |
|||
struct property *pp; |
|||
const char *separator = strchr(path, ':'); |
|||
|
|||
if (opts) |
|||
*opts = separator ? separator + 1 : NULL; |
|||
|
|||
if (strcmp(path, "/") == 0) |
|||
return of_root; |
|||
|
|||
/* The path could begin with an alias */ |
|||
if (*path != '/') { |
|||
int len; |
|||
const char *p = separator; |
|||
|
|||
if (!p) |
|||
p = strchrnul(path, '/'); |
|||
len = p - path; |
|||
|
|||
/* of_aliases must not be NULL */ |
|||
if (!of_aliases) |
|||
return NULL; |
|||
|
|||
for_each_property_of_node(of_aliases, pp) { |
|||
if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) { |
|||
np = of_find_node_by_path(pp->value); |
|||
break; |
|||
} |
|||
} |
|||
if (!np) |
|||
return NULL; |
|||
path = p; |
|||
} |
|||
|
|||
/* Step down the tree matching path components */ |
|||
if (!np) |
|||
np = of_root; |
|||
while (np && *path == '/') { |
|||
path++; /* Increment past '/' delimiter */ |
|||
np = __of_find_node_by_path(np, path); |
|||
path = strchrnul(path, '/'); |
|||
if (separator && separator < path) |
|||
break; |
|||
} |
|||
return np; |
|||
} |
|||
|
|||
static struct property *__of_find_property(const struct device_node *np, |
|||
const char *name, int *lenp) |
|||
{ |
|||
struct property *pp; |
|||
|
|||
if (!np) |
|||
return NULL; |
|||
|
|||
for (pp = np->properties; pp; pp = pp->next) { |
|||
if (strcmp(pp->name, name) == 0) { |
|||
if (lenp) |
|||
*lenp = pp->length; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return pp; |
|||
} |
|||
|
|||
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp) |
|||
{ |
|||
struct property *pp; |
|||
|
|||
pp = __of_find_property(np, name, lenp); |
|||
|
|||
return pp; |
|||
} |
|||
|
|||
const void *of_get_property(const struct device_node *np, const char *name, |
|||
int *lenp) |
|||
{ |
|||
struct property *pp = of_find_property(np, name, lenp); |
|||
|
|||
return pp ? pp->value : NULL; |
|||
} |
|||
|
|||
static int kstrtoint(const char *s, unsigned int base, int *res) |
|||
{ |
|||
long long tmp; |
|||
|
|||
tmp = strtoll(s, NULL, base); |
|||
if (tmp != (long long)(int)tmp) |
|||
return -1; |
|||
*res = tmp; |
|||
return 0; |
|||
} |
|||
|
|||
static void of_alias_add(struct alias_prop *ap, struct device_node *np, |
|||
int id, const char *stem, int stem_len) |
|||
{ |
|||
ap->np = np; |
|||
ap->id = id; |
|||
strncpy(ap->stem, stem, stem_len); |
|||
ap->stem[stem_len] = 0; |
|||
list_add_tail(&ap->link, &aliases_lookup); |
|||
printf("adding DT alias:%s: stem=%s id=%i node=%s\n", |
|||
ap->alias, ap->stem, ap->id, of_node_full_name(np)); |
|||
} |
|||
|
|||
/**
|
|||
* of_alias_scan - Scan all properties of the 'aliases' node |
|||
* |
|||
* The function scans all the properties of the 'aliases' node and populates |
|||
* the global lookup table with the properties. It returns the |
|||
* number of alias properties found, or an error code in case of failure. |
|||
* |
|||
* @dt_alloc: An allocator that provides a virtual address to memory |
|||
* for storing the resulting tree |
|||
*/ |
|||
void of_alias_scan(void * (*dt_alloc)(uint32_t size, uint32_t align)) |
|||
{ |
|||
struct property *pp; |
|||
|
|||
of_aliases = of_find_node_by_path("/aliases"); |
|||
of_chosen = of_find_node_by_path("/chosen"); |
|||
if (of_chosen == NULL) |
|||
of_chosen = of_find_node_by_path("/chosen@0"); |
|||
|
|||
if (of_chosen) { |
|||
/* linux,stdout-path and /aliases/stdout are for legacy compatibility */ |
|||
const char *name = of_get_property(of_chosen, "stdout-path", NULL); |
|||
if (!name) |
|||
name = of_get_property(of_chosen, "linux,stdout-path", NULL); |
|||
if (!name) |
|||
name = of_get_property(of_aliases, "stdout", NULL); |
|||
if (name) |
|||
of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); |
|||
} |
|||
|
|||
if (!of_aliases) |
|||
return; |
|||
|
|||
for_each_property_of_node(of_aliases, pp) { |
|||
const char *start = pp->name; |
|||
const char *end = start + strlen(start); |
|||
struct device_node *np; |
|||
struct alias_prop *ap; |
|||
int id, len; |
|||
|
|||
/* Skip those we do not want to proceed */ |
|||
if (!strcmp(pp->name, "name") || |
|||
!strcmp(pp->name, "phandle") || |
|||
!strcmp(pp->name, "linux,phandle")) |
|||
continue; |
|||
|
|||
np = of_find_node_by_path(pp->value); |
|||
if (!np) |
|||
continue; |
|||
|
|||
/* walk the alias backwards to extract the id and work out
|
|||
* the 'stem' string */ |
|||
while (isdigit(*(end-1)) && end > start) |
|||
end--; |
|||
len = end - start; |
|||
|
|||
if (kstrtoint(end, 10, &id) < 0) |
|||
continue; |
|||
|
|||
/* Allocate an alias_prop with enough space for the stem */ |
|||
ap = dt_alloc(sizeof(*ap) + len + 1, 4); |
|||
if (!ap) |
|||
continue; |
|||
memset(ap, 0, sizeof(*ap) + len + 1); |
|||
ap->alias = start; |
|||
of_alias_add(ap, np, id, start, len); |
|||
} |
|||
} |
@ -0,0 +1,415 @@ |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include <stdint.h> |
|||
#include <stdlib.h> |
|||
#include "libfdt.h" |
|||
#include "of.h" |
|||
#include "of_private.h" |
|||
|
|||
static void * __alloc(uint32_t size, uint32_t align) |
|||
{ |
|||
return malloc(size); |
|||
} |
|||
|
|||
static inline void of_node_set_flag(struct device_node *n, unsigned long flag) |
|||
{ |
|||
n->_flags |= (flag << 1); |
|||
} |
|||
|
|||
static void reverse_nodes(struct device_node *parent) |
|||
{ |
|||
struct device_node *child, *next; |
|||
|
|||
/* In-depth first */ |
|||
child = parent->child; |
|||
while (child) { |
|||
reverse_nodes(child); |
|||
|
|||
child = child->sibling; |
|||
} |
|||
|
|||
/* Reverse the nodes in the child list */ |
|||
child = parent->child; |
|||
parent->child = NULL; |
|||
while (child) { |
|||
next = child->sibling; |
|||
|
|||
child->sibling = parent->child; |
|||
parent->child = child; |
|||
child = next; |
|||
} |
|||
} |
|||
|
|||
static void *unflatten_dt_alloc(void **mem, unsigned long size, |
|||
unsigned long align) |
|||
{ |
|||
unsigned char *dp; |
|||
|
|||
dp = *mem; |
|||
dp += size; |
|||
*mem = dp; |
|||
//*mem += size;
|
|||
|
|||
return dp; |
|||
} |
|||
|
|||
static void populate_properties(const void *blob, |
|||
int offset, |
|||
void **mem, |
|||
struct device_node *np, |
|||
const char *nodename, |
|||
int dryrun) |
|||
{ |
|||
struct property *pp, **pprev = NULL; |
|||
int cur; |
|||
int has_name = 0; |
|||
|
|||
pprev = &np->properties; |
|||
for (cur = fdt_first_property_offset(blob, offset); |
|||
cur >= 0; |
|||
cur = fdt_next_property_offset(blob, cur)) { |
|||
const uint32_t *val; |
|||
const char *pname; |
|||
int sz; |
|||
|
|||
val = fdt_getprop_by_offset(blob, cur, &pname, &sz); |
|||
if (!val) { |
|||
printf("Cannot locate property at 0x%x\n", cur); |
|||
continue; |
|||
} |
|||
|
|||
if (!pname) { |
|||
printf("Cannot find property name at 0x%x\n", cur); |
|||
continue; |
|||
} |
|||
|
|||
if (!strcmp(pname, "name")) |
|||
has_name = 1; |
|||
|
|||
pp = unflatten_dt_alloc(mem, sizeof(struct property), |
|||
__alignof__(struct property)); |
|||
if (dryrun) |
|||
continue; |
|||
|
|||
/* We accept flattened tree phandles either in
|
|||
* ePAPR-style "phandle" properties, or the |
|||
* legacy "linux,phandle" properties. If both |
|||
* appear and have different values, things |
|||
* will get weird. Don't do that. |
|||
*/ |
|||
if (!strcmp(pname, "phandle") || |
|||
!strcmp(pname, "linux,phandle")) { |
|||
if (!np->phandle) { |
|||
np->phandle = *val; //be32_to_cpup(val);
|
|||
} |
|||
} |
|||
|
|||
/* And we process the "ibm,phandle" property
|
|||
* used in pSeries dynamic device tree |
|||
* stuff |
|||
*/ |
|||
if (!strcmp(pname, "ibm,phandle")) { |
|||
np->phandle = *val; //be32_to_cpup(val);
|
|||
} |
|||
|
|||
pp->name = (char *)pname; |
|||
pp->length = sz; |
|||
pp->value = (void *)val; |
|||
*pprev = pp; |
|||
pprev = &pp->next; |
|||
} |
|||
|
|||
/* With version 0x10 we may not have the name property,
|
|||
* recreate it here from the unit name if absent |
|||
*/ |
|||
if (!has_name) { |
|||
const char *p = nodename, *ps = p, *pa = NULL; |
|||
int len; |
|||
|
|||
while (*p) { |
|||
if ((*p) == '@') |
|||
pa = p; |
|||
else if ((*p) == '/') |
|||
ps = p + 1; |
|||
p++; |
|||
} |
|||
|
|||
if (pa < ps) |
|||
pa = p; |
|||
len = (pa - ps) + 1; |
|||
pp = unflatten_dt_alloc(mem, sizeof(struct property) + len, |
|||
__alignof__(struct property)); |
|||
if (!dryrun) { |
|||
pp->name = "name"; |
|||
pp->length = len; |
|||
pp->value = pp + 1; |
|||
*pprev = pp; |
|||
pprev = &pp->next; |
|||
memcpy(pp->value, ps, len - 1); |
|||
((char *)pp->value)[len - 1] = 0; |
|||
printf("fixed up name for %s -> %s\n", |
|||
nodename, (char *)pp->value); |
|||
} |
|||
} |
|||
|
|||
if (!dryrun) |
|||
*pprev = NULL; |
|||
} |
|||
static unsigned int populate_node(const void *blob, |
|||
int offset, |
|||
void **mem, |
|||
struct device_node *dad, |
|||
unsigned int fpsize, |
|||
struct device_node **pnp, |
|||
int dryrun) |
|||
{ |
|||
struct device_node *np; |
|||
const char *pathp; |
|||
unsigned int l, allocl; |
|||
int new_format = 0; |
|||
|
|||
pathp = fdt_get_name(blob, offset, (int *)&l); |
|||
if (!pathp) { |
|||
*pnp = NULL; |
|||
return 0; |
|||
} |
|||
|
|||
allocl = ++l; |
|||
|
|||
/* version 0x10 has a more compact unit name here instead of the full
|
|||
* path. we accumulate the full path size using "fpsize", we'll rebuild |
|||
* it later. We detect this because the first character of the name is |
|||
* not '/'. |
|||
*/ |
|||
if ((*pathp) != '/') { |
|||
new_format = 1; |
|||
if (fpsize == 0) { |
|||
/* root node: special case. fpsize accounts for path
|
|||
* plus terminating zero. root node only has '/', so |
|||
* fpsize should be 2, but we want to avoid the first |
|||
* level nodes to have two '/' so we use fpsize 1 here |
|||
*/ |
|||
fpsize = 1; |
|||
allocl = 2; |
|||
l = 1; |
|||
pathp = ""; |
|||
} else { |
|||
/* account for '/' and path size minus terminal 0
|
|||
* already in 'l' |
|||
*/ |
|||
fpsize += l; |
|||
allocl = fpsize; |
|||
} |
|||
} |
|||
|
|||
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, |
|||
__alignof__(struct device_node)); |
|||
if (!dryrun) { |
|||
char *fn; |
|||
of_node_init(np); |
|||
np->full_name = fn = ((char *)np) + sizeof(*np); |
|||
if (new_format) { |
|||
/* rebuild full path for new format */ |
|||
if (dad && dad->parent) { |
|||
strcpy(fn, dad->full_name); |
|||
#ifdef DEBUG |
|||
if ((strlen(fn) + l + 1) != allocl) { |
|||
pr_debug("%s: p: %d, l: %d, a: %d\n", |
|||
pathp, (int)strlen(fn), |
|||
l, allocl); |
|||
} |
|||
#endif |
|||
fn += strlen(fn); |
|||
} |
|||
*(fn++) = '/'; |
|||
} |
|||
memcpy(fn, pathp, l); |
|||
|
|||
if (dad != NULL) { |
|||
np->parent = dad; |
|||
np->sibling = dad->child; |
|||
dad->child = np; |
|||
} |
|||
} |
|||
|
|||
populate_properties(blob, offset, mem, np, pathp, dryrun); |
|||
if (!dryrun) { |
|||
np->name = of_get_property(np, "name", NULL); |
|||
np->type = of_get_property(np, "device_type", NULL); |
|||
|
|||
if (!np->name) |
|||
np->name = "<NULL>"; |
|||
if (!np->type) |
|||
np->type = "<NULL>"; |
|||
} |
|||
|
|||
*pnp = np; |
|||
return fpsize; |
|||
} |
|||
/**
|
|||
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree |
|||
* @blob: The parent device tree blob |
|||
* @mem: Memory chunk to use for allocating device nodes and properties |
|||
* @dad: Parent struct device_node |
|||
* @nodepp: The device_node tree created by the call |
|||
* |
|||
* It returns the size of unflattened device tree or error code |
|||
*/ |
|||
static int unflatten_dt_nodes(const void *blob, |
|||
void *mem, |
|||
struct device_node *dad, |
|||
struct device_node **nodepp) |
|||
{ |
|||
struct device_node *root; |
|||
int offset = 0, depth = 0, initial_depth = 0; |
|||
#define FDT_MAX_DEPTH 64 |
|||
unsigned int fpsizes[FDT_MAX_DEPTH]; |
|||
struct device_node *nps[FDT_MAX_DEPTH]; |
|||
void *base = mem; |
|||
int dryrun = !base; |
|||
|
|||
if (nodepp) |
|||
*nodepp = NULL; |
|||
|
|||
/*
|
|||
* We're unflattening device sub-tree if @dad is valid. There are |
|||
* possibly multiple nodes in the first level of depth. We need |
|||
* set @depth to 1 to make fdt_next_node() happy as it bails |
|||
* immediately when negative @depth is found. Otherwise, the device |
|||
* nodes except the first one won't be unflattened successfully. |
|||
*/ |
|||
if (dad) |
|||
depth = initial_depth = 1; |
|||
|
|||
root = dad; |
|||
fpsizes[depth] = dad ? strlen(of_node_full_name(dad)) : 0; |
|||
nps[depth] = dad; |
|||
|
|||
for (offset = 0; |
|||
offset >= 0 && depth >= initial_depth; |
|||
offset = fdt_next_node(blob, offset, &depth)) { |
|||
if (depth >= FDT_MAX_DEPTH) { |
|||
printf("depth overflow.\n"); |
|||
continue; |
|||
} |
|||
|
|||
fpsizes[depth+1] = populate_node(blob, offset, &mem, |
|||
nps[depth], |
|||
fpsizes[depth], |
|||
&nps[depth+1], dryrun); |
|||
if (!fpsizes[depth+1]) |
|||
return (unsigned char *)mem - (unsigned char *)base; |
|||
|
|||
if (!dryrun && nodepp && !*nodepp) |
|||
*nodepp = nps[depth+1]; |
|||
if (!dryrun && !root) |
|||
root = nps[depth+1]; |
|||
} |
|||
|
|||
if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { |
|||
printf("Error %d processing FDT\n", offset); |
|||
return -22; |
|||
} |
|||
|
|||
/*
|
|||
* Reverse the child list. Some drivers assumes node order matches .dts |
|||
* node order |
|||
*/ |
|||
if (!dryrun) |
|||
reverse_nodes(root); |
|||
|
|||
return (unsigned char *)mem - (unsigned char *)base; |
|||
} |
|||
|
|||
/**
|
|||
* __unflatten_device_tree - create tree of device_nodes from flat blob |
|||
* |
|||
* unflattens a device-tree, creating the |
|||
* tree of struct device_node. It also fills the "name" and "type" |
|||
* pointers of the nodes so the normal device-tree walking functions |
|||
* can be used. |
|||
* @blob: The blob to expand |
|||
* @dad: Parent device node |
|||
* @mynodes: The device_node tree created by the call |
|||
* @dt_alloc: An allocator that provides a virtual address to memory |
|||
* for the resulting tree |
|||
* |
|||
* Returns NULL on failure or the memory chunk containing the unflattened |
|||
* device tree on success. |
|||
*/ |
|||
static void *__unflatten_device_tree(const void *blob, |
|||
struct device_node *dad, |
|||
struct device_node **mynodes, |
|||
void *(*dt_alloc)(uint32_t size, uint32_t align), |
|||
int detached) |
|||
{ |
|||
int size; |
|||
void *mem; |
|||
|
|||
printf(" -> unflatten_device_tree()\n"); |
|||
|
|||
if (!blob) { |
|||
printf("No device tree pointer\n"); |
|||
return NULL; |
|||
} |
|||
|
|||
printf("Unflattening device tree:\n"); |
|||
printf("magic: %08x\n", fdt_magic(blob)); |
|||
printf("size: %08x\n", fdt_totalsize(blob)); |
|||
printf("version: %08x\n", fdt_version(blob)); |
|||
|
|||
if (fdt_check_header(blob)) { |
|||
printf("Invalid device tree blob header\n"); |
|||
return NULL; |
|||
} |
|||
|
|||
/* First pass, scan for size */ |
|||
size = unflatten_dt_nodes(blob, NULL, dad, NULL); |
|||
if (size < 0) |
|||
return NULL; |
|||
|
|||
size = ((size + 3) & (~3)); //(ALIGN(size, 4);
|
|||
|
|||
printf(" size is %d, allocating...\n", size); |
|||
|
|||
/* Allocate memory for the expanded device tree */ |
|||
mem = dt_alloc(size + 4, __alignof__(struct device_node)); |
|||
memset(mem, 0, size); |
|||
|
|||
*(uint32_t *)((unsigned char *)mem + size) = 0xefbeadde; |
|||
|
|||
printf(" unflattening %p...\n", mem); |
|||
|
|||
/* Second pass, do actual unflattening */ |
|||
unflatten_dt_nodes(blob, mem, dad, mynodes); |
|||
if (*(uint32_t *)((unsigned char *)mem + size) != 0xefbeadde) |
|||
printf("End of tree marker overwritten: %08x\n", |
|||
*(uint32_t *)((unsigned char *)mem + size)); |
|||
|
|||
if (detached && mynodes) { |
|||
of_node_set_flag(*mynodes, OF_DETACHED); |
|||
printf("unflattened tree is detached\n"); |
|||
} |
|||
|
|||
printf(" <- unflatten_device_tree()\n"); |
|||
return mem; |
|||
} |
|||
|
|||
/**
|
|||
* unflatten_device_tree - create tree of device_nodes from flat blob |
|||
* |
|||
* unflattens the device-tree passed by the firmware, creating the |
|||
* tree of struct device_node. It also fills the "name" and "type" |
|||
* pointers of the nodes so the normal device-tree walking functions |
|||
* can be used. |
|||
*/ |
|||
int unflatten_device_tree(void *mempos) |
|||
{ |
|||
__unflatten_device_tree(mempos, NULL, &of_root, |
|||
__alloc, 0); |
|||
|
|||
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ |
|||
of_alias_scan(__alloc); |
|||
return of_root ? 0 : -1; |
|||
} |
|||
|
@ -0,0 +1,19 @@ |
|||
#ifndef __OF_PRIVATE_H____ |
|||
#define __OF_PRIVATE_H____ |
|||
#include "list.h" |
|||
|
|||
struct alias_prop { |
|||
struct list_head link; |
|||
const char *alias; |
|||
struct device_node *np; |
|||
int id; |
|||
char stem[0]; |
|||
}; |
|||
|
|||
extern struct device_node *of_root; |
|||
extern struct device_node *of_aliases; |
|||
extern struct device_node *of_chosen; |
|||
extern struct device_node *of_stdout; |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,46 @@ |
|||
#ifndef __LIST_H____ |
|||
#define __LIST_H____ |
|||
|
|||
struct list_head { |
|||
struct list_head *next, *prev; |
|||
}; |
|||
|
|||
/*
|
|||
* Simple doubly linked list implementation. |
|||
* |
|||
* Some of the internal functions ("__xxx") are useful when |
|||
* manipulating whole lists rather than single entries, as |
|||
* sometimes we already know the next/prev entries and we can |
|||
* generate better code by using them directly rather than |
|||
* using the generic single-entry routines. |
|||
*/ |
|||
|
|||
#define LIST_HEAD_INIT(name) { &(name), &(name) } |
|||
|
|||
#define LIST_HEAD(name) \ |
|||
struct list_head name = LIST_HEAD_INIT(name) |
|||
|
|||
static inline void INIT_LIST_HEAD(struct list_head *list) |
|||
{ |
|||
list->next = list; |
|||
list->prev = list; |
|||
} |
|||
|
|||
|
|||
static inline void __list_add(struct list_head *_new, |
|||
struct list_head *prev, |
|||
struct list_head *next) |
|||
{ |
|||
next->prev = _new; |
|||
_new->next = next; |
|||
_new->prev = prev; |
|||
prev->next = _new; |
|||
} |
|||
|
|||
static inline void list_add_tail(struct list_head *new, struct list_head *head) |
|||
{ |
|||
__list_add(new, head->prev, head); |
|||
} |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,74 @@ |
|||
#ifndef __DRIVER_OF_H__ |
|||
#define __DRIVER_OF_H__ |
|||
|
|||
struct property { |
|||
char *name; |
|||
int length; |
|||
void *value; |
|||
struct property *next; |
|||
unsigned long _flags; |
|||
unsigned int unique_id; |
|||
}; |
|||
|
|||
enum fwnode_type { |
|||
FWNODE_INVALID = 0, |
|||
FWNODE_OF, |
|||
FWNODE_ACPI, |
|||
FWNODE_ACPI_DATA, |
|||
FWNODE_PDATA, |
|||
FWNODE_IRQCHIP, |
|||
}; |
|||
|
|||
struct fwnode_handle { |
|||
enum fwnode_type type; |
|||
struct fwnode_handle *secondary; |
|||
}; |
|||
|
|||
/* flag descriptions (need to be visible even when !CONFIG_OF) */ |
|||
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ |
|||
#define OF_DETACHED 2 /* node has been detached from the device tree */ |
|||
#define OF_POPULATED 3 /* device already created for the node */ |
|||
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */ |
|||
|
|||
#define OF_BAD_ADDR ((u64)-1) |
|||
struct device_node { |
|||
struct device_node *parent; |
|||
struct device_node *child; |
|||
struct device_node *sibling; |
|||
|
|||
unsigned int phandle; |
|||
|
|||
struct fwnode_handle fwnode; |
|||
|
|||
const char *name; |
|||
const char *type; |
|||
const char *full_name; |
|||
|
|||
unsigned long _flags; |
|||
|
|||
struct property *properties; |
|||
struct property *deadprops; /* removed properties */ |
|||
}; |
|||
|
|||
static inline void of_node_init(struct device_node *node) |
|||
{ |
|||
node->fwnode.type = FWNODE_OF; |
|||
} |
|||
|
|||
#define for_each_property_of_node(dn, pp) \ |
|||
for (pp = dn->properties; pp != NULL; pp = pp->next) |
|||
|
|||
static inline const char *of_node_full_name(const struct device_node *np) |
|||
{ |
|||
return np ? np->full_name : "<no-node>"; |
|||
} |
|||
|
|||
|
|||
const void *of_get_property(const struct device_node *np, const char *name, int *lenp); |
|||
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); |
|||
struct device_node *of_find_node_opts_by_path(const char *path, const char **opts); |
|||
void of_alias_scan(void * (*dt_alloc)(uint32_t size, uint32_t align)); |
|||
int unflatten_device_tree(void *mempos); |
|||
|
|||
#endif |
|||
|
Loading…
Reference in new issue