@@ -22,6 +22,13 @@ cflatobjs := \
lib/abort.o \
lib/report.o
+# libfdt paths
+LIBFDT_objdir = lib/libfdt
+LIBFDT_srcdir = lib/libfdt
+LIBFDT_archive = $(LIBFDT_objdir)/libfdt.a
+LIBFDT_include = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_INCLUDES))
+LIBFDT_version = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_VERSION))
+
#include architecure specific make rules
include config/config-$(ARCH).mak
@@ -47,6 +54,11 @@ LDFLAGS += -pthread -lrt
$(libcflat): $(cflatobjs)
$(AR) rcs $@ $^
+include $(LIBFDT_srcdir)/Makefile.libfdt
+$(LIBFDT_archive): CFLAGS += -ffreestanding -I lib -I lib/libfdt -Wno-sign-compare
+$(LIBFDT_archive): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS))
+ $(AR) rcs $@ $^
+
%.o: %.S
$(CC) $(CFLAGS) -c -nostdlib -o $@ $<
@@ -59,10 +71,15 @@ install:
clean: arch_clean
$(RM) lib/.*.d $(libcflat) $(cflatobjs)
-distclean: clean
+libfdt_clean:
+ $(RM) $(LIBFDT_archive) \
+ $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS)) \
+ $(LIBFDT_objdir)/.*.d
+
+distclean: clean libfdt_clean
$(RM) config.mak $(TEST_DIR)-run test.log msr.out cscope.*
-cscope: common_dirs = lib
+cscope: common_dirs = lib lib/libfdt
cscope:
$(RM) ./cscope.*
find $(TEST_DIR) lib/$(TEST_DIR) $(common_dirs) -maxdepth 1 \
new file mode 100644
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "libfdt/libfdt.h"
+#include "devicetree.h"
+
+static const void *fdt;
+static u32 root_nr_address_cells, root_nr_size_cells;
+
+const void *dt_fdt(void)
+{
+ return fdt;
+}
+
+bool dt_available(void)
+{
+ return fdt_check_header(fdt) == 0;
+}
+
+int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
+{
+ const struct fdt_property *prop;
+ u32 *nr_cells;
+ int len;
+
+ prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
+ if (prop == NULL)
+ return len;
+
+ nr_cells = (u32 *)prop->data;
+ *nr_address_cells = fdt32_to_cpu(*nr_cells);
+
+ prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
+ if (prop == NULL)
+ return len;
+
+ nr_cells = (u32 *)prop->data;
+ *nr_size_cells = fdt32_to_cpu(*nr_cells);
+
+ return 0;
+}
+
+void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
+{
+ memset(reg, 0, sizeof(struct dt_reg));
+ reg->nr_address_cells = nr_address_cells;
+ reg->nr_size_cells = nr_size_cells;
+}
+
+int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
+{
+ const struct fdt_property *prop;
+ u32 *cells, i;
+ unsigned nr_tuple_cells;
+ int len;
+
+ prop = fdt_get_property(fdt, fdtnode, "reg", &len);
+ if (prop == NULL)
+ return len;
+
+ cells = (u32 *)prop->data;
+ nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
+ regidx *= nr_tuple_cells;
+
+ if (regidx + nr_tuple_cells > len/sizeof(u32))
+ return -FDT_ERR_NOTFOUND;
+
+ for (i = 0; i < reg->nr_address_cells; ++i)
+ reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
+
+ regidx += reg->nr_address_cells;
+ for (i = 0; i < reg->nr_size_cells; ++i)
+ reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
+
+ return 0;
+}
+
+int dt_pbus_translate_node(int fdtnode, int regidx,
+ struct dt_pbus_reg *pbus_reg)
+{
+ struct dt_reg raw_reg;
+ int ret;
+
+ dt_reg_init(&raw_reg, root_nr_address_cells, root_nr_size_cells);
+
+ ret = dt_get_reg(fdtnode, regidx, &raw_reg);
+ if (ret < 0)
+ return ret;
+
+ pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
+ raw_reg.address_cells);
+ pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
+ raw_reg.size_cells);
+
+ return 0;
+}
+
+int dt_pbus_translate(const struct dt_device *dev, int regidx,
+ void *reg)
+{
+ return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
+}
+
+int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
+{
+ /* matches any device with a valid node */
+ return fdtnode < 0 ? fdtnode : 1;
+}
+
+static const struct dt_bus dt_default_bus = {
+ .match = dt_bus_match_any,
+ .translate = dt_pbus_translate,
+};
+
+void dt_bus_init_defaults(struct dt_bus *bus)
+{
+ memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
+}
+
+void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
+ void *info)
+{
+ memset(dev, 0, sizeof(struct dt_device));
+ dev->bus = bus;
+ dev->info = info;
+}
+
+int dt_device_find_compatible(const struct dt_device *dev,
+ const char *compatible)
+{
+ int node, ret;
+
+ node = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ while (node >= 0) {
+ ret = dev->bus->match(dev, node);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ break;
+ node = fdt_node_offset_by_compatible(fdt, node, compatible);
+ }
+ return node;
+}
+
+int dt_pbus_get_base_compatible(const char *compatible,
+ struct dt_pbus_reg *base)
+{
+ struct dt_device dev;
+ int node;
+
+ dt_device_init(&dev, &dt_default_bus, NULL);
+
+ node = dt_device_find_compatible(&dev, compatible);
+ if (node < 0)
+ return node;
+
+ dt_device_bind_node(&dev, node);
+
+ return dt_pbus_get_base(&dev, base);
+}
+
+int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
+{
+ const char *pn = "device_type", *pv = "memory";
+ int node, ret, pl = strlen(pv) + 1, nr = 0;
+ struct dt_pbus_reg reg;
+
+ node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
+
+ while (node >= 0) {
+
+ while (nr < nr_regs) {
+ ret = dt_pbus_translate_node(node, nr, ®);
+ if (ret == -FDT_ERR_NOTFOUND)
+ break;
+ if (ret < 0)
+ return ret;
+ regs[nr].addr = reg.addr;
+ regs[nr].size = reg.size;
+ ++nr;
+ }
+
+ node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
+ }
+
+ return node != -FDT_ERR_NOTFOUND ? node : nr;
+}
+
+int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval, void *info),
+ void *info)
+{
+ const struct fdt_property *prop;
+ int cpus, cpu, ret, len;
+ struct dt_reg raw_reg;
+ u32 nac, nsc;
+
+ cpus = fdt_path_offset(fdt, "/cpus");
+ if (cpus < 0)
+ return cpus;
+
+ ret = dt_get_nr_cells(cpus, &nac, &nsc);
+ if (ret < 0)
+ return ret;
+
+ dt_reg_init(&raw_reg, nac, nsc);
+
+ dt_for_each_subnode(cpus, cpu) {
+
+ prop = fdt_get_property(fdt, cpu, "device_type", &len);
+ if (prop == NULL)
+ return len;
+
+ if (len != 4 || strcmp((char *)prop->data, "cpu"))
+ continue;
+
+ ret = dt_get_reg(cpu, 0, &raw_reg);
+ if (ret < 0)
+ return ret;
+
+ func(cpu, raw_reg.address_cells[0], info);
+ }
+
+ return 0;
+}
+
+int dt_get_bootargs(const char **bootargs)
+{
+ const struct fdt_property *prop;
+ int node, len;
+
+ *bootargs = NULL;
+
+ node = fdt_path_offset(fdt, "/chosen");
+ if (node < 0)
+ return node;
+
+ prop = fdt_get_property(fdt, node, "bootargs", &len);
+ if (prop)
+ *bootargs = prop->data;
+ else if (len < 0 && len != -FDT_ERR_NOTFOUND)
+ return len;
+
+ return 0;
+}
+
+int dt_init(const void *fdt_ptr)
+{
+ struct dt_bus *defbus = (struct dt_bus *)&dt_default_bus;
+ int root, ret;
+
+ ret = fdt_check_header(fdt_ptr);
+ if (ret < 0)
+ return ret;
+ fdt = fdt_ptr;
+
+ root = fdt_path_offset(fdt, "/");
+ if (root < 0)
+ return root;
+
+ ret = dt_get_nr_cells(root, &root_nr_address_cells,
+ &root_nr_size_cells);
+ if (ret < 0)
+ return ret;
+
+ defbus->nr_address_cells = root_nr_address_cells;
+ defbus->nr_size_cells = root_nr_size_cells;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,236 @@
+#ifndef _DEVICETREE_H_
+#define _DEVICETREE_H_
+/*
+ * devicetree builds on libfdt to implement abstractions and accessors
+ * for Linux required device tree content. The accessors provided are
+ * common across architectures. See section III of the kernel doc
+ * Documentation/devicetree/booting-without-of.txt
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "libfdt/libfdt.h"
+
+/**********************************************************************
+ * devicetree init and libfdt helpers
+ **********************************************************************/
+
+/* dt_init initializes devicetree with a pointer to an fdt, @fdt_ptr */
+extern int dt_init(const void *fdt_ptr);
+
+/* get the fdt pointer that devicetree is using */
+extern const void *dt_fdt(void);
+
+/* check for an initialized, valid devicetree */
+extern bool dt_available(void);
+
+/* traverse child nodes */
+#define dt_for_each_subnode(n, s) \
+ for (s = fdt_first_subnode(dt_fdt(), n); \
+ s != -FDT_ERR_NOTFOUND; \
+ s = fdt_next_subnode(dt_fdt(), s))
+
+/**********************************************************************
+ * Abstractions for required node types and properties
+ **********************************************************************/
+
+struct dt_device {
+ int fdtnode;
+ const struct dt_bus *bus;
+
+ /*
+ * info is a pointer to device specific data, which may be
+ * used by the bus match() and translate() functions
+ */
+ void *info;
+};
+
+struct dt_bus {
+ /*
+ * match a device @dev to an fdt node @fdtnode
+ * returns
+ * - a positive value on match
+ * - zero on no match
+ * - a negative FDT_ERR_* value on failure
+ */
+ int (*match)(const struct dt_device *dev, int fdtnode);
+
+ /*
+ * translate the @regidx'th "address size" tuple of
+ * @dev's fdt node's "reg" property, and store the result
+ * in @reg, a bus specific structure
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+ int (*translate)(const struct dt_device *dev, int regidx, void *reg);
+
+ /* the bus #address-cells and #size-cells properties */
+ u32 nr_address_cells, nr_size_cells;
+};
+
+/* dt_bus_match_any matches any fdt node, i.e. it always returns true */
+extern int dt_bus_match_any(const struct dt_device *dev, int fdtnode);
+
+/* the processor bus (pbus) address type and register tuple */
+typedef u64 dt_pbus_addr_t;
+struct dt_pbus_reg {
+ dt_pbus_addr_t addr;
+ dt_pbus_addr_t size;
+};
+
+static inline dt_pbus_addr_t dt_pbus_read_cells(u32 nr_cells, u32 *cells)
+{
+ switch (nr_cells) {
+ case 1: return cells[0];
+ case 2: return ((u64)cells[0] << 32) | cells[1];
+ }
+ return (~0ULL);
+}
+
+/*
+ * dt_pbus_translate translates device node regs for the
+ * processor bus using the root node's #address-cells and
+ * #size-cells and dt_pbus_read_cells()
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_pbus_translate(const struct dt_device *dev, int regidx,
+ void *reg);
+
+/*
+ * dt_pbus_translate_node is the same as dt_pbus_translate but
+ * operates on an fdt node instead of a dt_device
+ */
+extern int dt_pbus_translate_node(int fdtnode, int regidx,
+ struct dt_pbus_reg *reg);
+
+/*
+ * dt_pbus_get_base is an alias for
+ * dt_pbus_translate(dev, 0, base)
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+static inline int dt_pbus_get_base(const struct dt_device *dev,
+ struct dt_pbus_reg *base)
+{
+ return dt_pbus_translate(dev, 0, base);
+}
+
+/*
+ * dt_bus_init_defaults initializes @bus with
+ * match <- dt_bus_match_any
+ * translate <- dt_pbus_translate
+ * nr_address_cells <- #address-cells of the root node
+ * nr_size_cells <- #size-cells of the root node
+ */
+extern void dt_bus_init_defaults(struct dt_bus *bus);
+
+/*
+ * dt_device_init initializes a dt_device with the given parameters
+ */
+extern void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
+ void *info);
+
+static inline void dt_device_bind_node(struct dt_device *dev, int fdtnode)
+{
+ dev->fdtnode = fdtnode;
+}
+
+/*
+ * dt_device_find_compatible finds a @compatible node
+ * returns
+ * - node (>= 0) on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_device_find_compatible(const struct dt_device *dev,
+ const char *compatible);
+
+/*
+ * dt_pbus_get_base_compatible simply bundles many functions into one.
+ * It finds the first @compatible fdt node, then translates the 0th reg
+ * tuple (the base) using the processor bus translation, and finally it
+ * stores that result in @base.
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_pbus_get_base_compatible(const char *compatible,
+ struct dt_pbus_reg *base);
+
+/**********************************************************************
+ * Low-level accessors for required node types and properties
+ **********************************************************************/
+
+/*
+ * dt_get_nr_cells sets @nr_address_cells and @nr_size_cells to the
+ * #address-cells and #size-cells properties of @fdtnode
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells,
+ u32 *nr_size_cells);
+
+/* dt_reg is a structure for "raw" reg tuples */
+#define MAX_ADDRESS_CELLS 4
+#define MAX_SIZE_CELLS 4
+struct dt_reg {
+ u32 nr_address_cells, nr_size_cells;
+ u32 address_cells[MAX_ADDRESS_CELLS];
+ u32 size_cells[MAX_SIZE_CELLS];
+};
+
+/*
+ * dt_reg_init initialize a dt_reg struct to zero and sets
+ * nr_address_cells and nr_size_cells to @nr_address_cells and
+ * @nr_size_cells respectively.
+ */
+extern void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells,
+ u32 nr_size_cells);
+
+/*
+ * dt_get_reg gets the @regidx'th reg tuple of @fdtnode's reg property
+ * and stores it in @reg. @reg must be initialized.
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg);
+
+/**********************************************************************
+ * High-level accessors for required node types and properties
+ **********************************************************************/
+
+/*
+ * dt_get_bootargs gets a pointer to /chosen/bootargs
+ * returns
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_bootargs(const char **bootargs);
+
+/*
+ * dt_get_memory_params gets the memory parameters from the /memory node(s)
+ * storing each memory region ("address size" tuple) in consecutive entries
+ * of @regs, up to @nr_regs
+ * returns
+ * - number of memory regions found on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs);
+
+/*
+ * dt_for_each_cpu_node runs @func on each cpu node in the /cpus node
+ * passing it its fdt node, its reg property value, and @info
+ * - zero on success
+ * - a negative FDT_ERR_* value on failure
+ */
+extern int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval,
+ void *info), void *info);
+
+#endif /* _DEVICETREE_H_ */
@@ -25,6 +25,8 @@
#include <stdint.h>
#include <string.h>
+#define __unused __attribute__((__unused__))
+
#define xstr(s) xxstr(s)
#define xxstr(s) #s