Subject: Re: [PATCH 07/11] of/flattree: Merge unflatten_dt_node

From: Wolfram Sang <[email-masked]>
Date: Thu, 5 Nov 2009 09:59:59 +0100

On Thu, Nov 05, 2009 at 12:46:38AM -0700, Grant Likely wrote:
> Merge common code between PowerPC and MicroBlaze
> 
> Signed-off-by: Grant Likely <[email-masked]>

[...]

> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index 01a7f5f..df1ac8d 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -167,3 +167,203 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
>  	return 0;
>  }
>  
> +static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
> +				       unsigned long align)
> +{
> +	void *res;
> +
> +	*mem = _ALIGN(*mem, align);
> +	res = (void *)*mem;
> +	*mem += size;
> +
> +	return res;
> +}
> +
> +/**
> + * unflatten_dt_node - Alloc and populate a device_node from the flat tree
> + * @p: pointer to node in flat tree
> + * @dad: Parent struct device_node
> + * @allnextpp: pointer to ->allnext from last allocated device_node
> + * @fpsize: Size of the node path up at the current depth.
> + */
> +unsigned long __init unflatten_dt_node(unsigned long mem,
> +					unsigned long *p,
> +					struct device_node *dad,
> +					struct device_node ***allnextpp,
> +					unsigned long fpsize)
> +{
> +	struct device_node *np;
> +	struct property *pp, **prev_pp = NULL;
> +	char *pathp;
> +	u32 tag;
> +	unsigned int l, allocl;
> +	int has_name = 0;
> +	int new_format = 0;
> +
> +	tag = *((u32 *)(*p));
> +	if (tag != OF_DT_BEGIN_NODE) {
> +		printk("Weird tag at start of node: %x\n", tag);

Loglevel missing -> pr_info?

> +		return mem;
> +	}
> +	*p += 4;
> +	pathp = (char *)*p;
> +	l = allocl = strlen(pathp) + 1;
> +	*p = _ALIGN(*p + l, 4);
> +
> +	/* 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;
> +		} 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 (allnextpp) {
> +		memset(np, 0, sizeof(*np));
> +		np->full_name = ((char *)np) + sizeof(struct device_node);
> +		if (new_format) {
> +			char *fn = np->full_name;
> +			/* 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);
> +		} else
> +			memcpy(np->full_name, pathp, l);
> +		prev_pp = &np->properties;
> +		**allnextpp = np;
> +		*allnextpp = &np->allnext;
> +		if (dad != NULL) {
> +			np->parent = dad;
> +			/* we temporarily use the next field as `last_child'*/
> +			if (dad->next == NULL)
> +				dad->child = np;
> +			else
> +				dad->next->sibling = np;
> +			dad->next = np;
> +		}
> +		kref_init(&np->kref);
> +	}
> +	while (1) {
> +		u32 sz, noff;
> +		char *pname;
> +
> +		tag = *((u32 *)(*p));
> +		if (tag == OF_DT_NOP) {
> +			*p += 4;
> +			continue;
> +		}
> +		if (tag != OF_DT_PROP)
> +			break;
> +		*p += 4;
> +		sz = *((u32 *)(*p));
> +		noff = *((u32 *)((*p) + 4));
> +		*p += 8;
> +		if (initial_boot_params->version < 0x10)
> +			*p = _ALIGN(*p, sz >= 8 ? 8 : 4);
> +
> +		pname = find_flat_dt_string(noff);
> +		if (pname == NULL) {
> +			pr_info("Can't find property name in list !\n");
> +			break;
> +		}
> +		if (strcmp(pname, "name") == 0)
> +			has_name = 1;
> +		l = strlen(pname) + 1;
> +		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
> +					__alignof__(struct property));
> +		if (allnextpp) {
> +			if (strcmp(pname, "linux,phandle") == 0) {
> +				np->node = *((u32 *)*p);
> +				if (np->linux_phandle == 0)
> +					np->linux_phandle = np->node;
> +			}
> +			if (strcmp(pname, "ibm,phandle") == 0)
> +				np->linux_phandle = *((u32 *)*p);
> +			pp->name = pname;
> +			pp->length = sz;
> +			pp->value = (void *)*p;
> +			*prev_pp = pp;
> +			prev_pp = &pp->next;
> +		}
> +		*p = _ALIGN((*p) + sz, 4);
> +	}
> +	/* with version 0x10 we may not have the name property, recreate
> +	 * it here from the unit name if absent
> +	 */
> +	if (!has_name) {
> +		char *p1 = pathp, *ps = pathp, *pa = NULL;
> +		int sz;
> +
> +		while (*p1) {
> +			if ((*p1) == '@')
> +				pa = p1;
> +			if ((*p1) == '/')
> +				ps = p1 + 1;
> +			p1++;
> +		}
> +		if (pa < ps)
> +			pa = p1;
> +		sz = (pa - ps) + 1;
> +		pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
> +					__alignof__(struct property));
> +		if (allnextpp) {
> +			pp->name = "name";
> +			pp->length = sz;
> +			pp->value = pp + 1;
> +			*prev_pp = pp;
> +			prev_pp = &pp->next;
> +			memcpy(pp->value, ps, sz - 1);
> +			((char *)pp->value)[sz - 1] = 0;
> +			pr_debug("fixed up name for %s -> %s\n", pathp,
> +				(char *)pp->value);
> +		}
> +	}
> +	if (allnextpp) {
> +		*prev_pp = NULL;
> +		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>";
> +	}
> +	while (tag == OF_DT_BEGIN_NODE) {
> +		mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
> +		tag = *((u32 *)(*p));
> +	}
> +	if (tag != OF_DT_END_NODE) {
> +		printk("Weird tag at end of node: %x\n", tag);

Ditto (microblaze had KERN_INFO, too).

> +		return mem;
> +	}
> +	*p += 4;
> +	return mem;
> +}
> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
> index 81231e0..ace9068 100644
> --- a/include/linux/of_fdt.h
> +++ b/include/linux/of_fdt.h
> @@ -69,6 +69,10 @@ extern void *of_get_flat_dt_prop(unsigned long node, const char *name,
>  				 unsigned long *size);
>  extern int of_flat_dt_is_compatible(unsigned long node, const char *name);
>  extern unsigned long of_get_flat_dt_root(void);
> +extern unsigned long unflatten_dt_node(unsigned long mem, unsigned long *p,
> +					struct device_node *dad,
> +					struct device_node ***allnextpp,
> +					unsigned long fpsize);
>  
>  /* Other Prototypes */
>  extern void finish_device_tree(void);
> 
> _______________________________________________
> Linuxppc-dev mailing list
> [email-masked]
> https://lists.ozlabs.org/listinfo/linuxppc-dev

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |