/* $NetBSD: $ */
/*-
  * Copyright (c) 2009 UPMC/LIP6
  * All rights reserved.
  * This software is distributed under the following condiions
  * compliant with the NetBSD foundation policy.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */

/* version 17 flattened device tree structure */

#ifdef _KERNEL
#include <machine/bus.h>
#endif

struct boot_fdt_header {
	uint32_t fdt_magic;		/* magic word OF_DT_HEADER */
#define FDT_MAGIC	0xd00dfeed	/* version */
	uint32_t fdt_totalsize;		/* total size of DT block */
	uint32_t fdt_struct_off;	/* offset to structure */
	uint32_t fdt_strings_off;	/* offset to strings */
	uint32_t fdt_memrsvmap_off;	/* offset to memory reserve map */
	uint32_t fdt_version;		/* format version */
	uint32_t fdt_last_comp_version;	/* last compatible version */
	uint32_t fdt_boot_cpuid;	/* physical CPU id we're booting on */
	uint32_t fdt_size_strings;	/* size of the strings block */
	uint32_t fdt_size_struct;	/* size of the DT structure block */
};

/* properties have the following fields, followed by variable-sized value */
struct boot_fdt_prop {
	uint32_t prop_start;	/* FDT_PROP below */
	uint32_t prop_value_size; /* size of variable-sized value */
	uint32_t prop_name_off; /* offset of propertie name in string table */
	/* followed by 0 or more 32bit words of values */
	uint32_t prop_value[0];
};
#define FDT_BEGIN_NODE	0x1	/* Start node: full name */
#define FDT_END_NODE	0x2	/* End node */
#define FDT_PROP	0x3	/* Property: name off, size, content */
#define FDT_END		0x9


/* structure to store properties of a node */
struct fdt_node {
	const uint32_t *fdt_node_p; /* pointer to the node in the fdt */
	const char *fdt_node_name; /* pointer to the node name in the fdt */
	const uint32_t *fdt_node_data; /* pointer to node datas */
	int fdt_node_addr_cells;
	int fdt_node_size_cells;
#ifdef _KERNEL
	device_t fdt_node_dev; /* pointer to driver's data */
#endif

};

/*
 * structure describing a small cache of node references.
 * we just record device ref coockie and pointer to node, in an array
 */
struct fdt_refs {
	uint32_t fdtref_cookie;
	struct fdt_node fdtref_node;
};
/*
 * structure pointing to the fdt in memory. Used by most functions
 * as some parameters may change while parsing the tree, some functions
 * may create a local copy of this strucure to update parameters.
 */

struct fdt {
	const struct boot_fdt_header *fdt_header;
	const uint32_t *fdt_struct; /* pointer to fdt nodes */
	const char *fdt_strings; /* pointer to strings table */
	struct fdt_node fdt_root_node;
	/* cache for references in properties */
	struct fdt_refs *fdt_refs_cache;
	int fdt_refs_cache_size;
	int fdt_numnodes;
};

/*
 * parse fdt header, return pointer to properties and strings,
 * parse root node and build the reference cache
 */
int fdt_parse_header(const uint32_t *, struct fdt *);
int fdt_parse_tree(void);
/* look for next node of the same level */
int fdt_next_node(const struct fdt_node *, const uint32_t *, struct fdt_node *);
/* find first subnode in properties of node */
int fdt_find_subnode(const struct fdt_node *, struct fdt_node *);
/* look for a named subnode of current node */
int fdt_find_node(const struct fdt_node *, const char *, struct fdt_node *);
/* look for a node from its ref cookie */
struct fdt_node *fdt_find_node_from_ref(uint32_t);

/* look for named property , return pointer to struct boot_fdt_prop */
const struct boot_fdt_prop * fdt_find_prop(const uint32_t *, const char *);
/*
 * walk the tree looking for a named property, callback with the node and prop
 * pointers 
 */
int fdt_walk(const struct fdt_node *, const char *,
    int (*callback)(const struct fdt_node *,
	const struct boot_fdt_prop *, void *), void *);

/*
 * read a "reg" property and properly convert based on current
 * #address-cells/#size-cells
 */
struct fdt_reg {
#ifdef _KERNEL
	bus_addr_t reg_addr;
	bus_size_t reg_size;
#else
	paddr_t reg_addr;
	paddr_t reg_size;
#endif
};
int fdt_read_node_reg(const struct fdt_node *, struct fdt_reg *reg, int);

extern struct fdt *fdt; /* pointer to ROM fdt mapped in our VA */

#ifdef _KERNEL
bool fdt_get_clock_freq(const struct fdt_node *, uint32_t *);
#endif
