summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Kleine-Budde <mkl@pengutronix.de>2015-05-13 12:12:31 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-05-26 14:21:48 +0200
commitacfe5a4fec06be1d67e01b07cdd7e703fe3a2278 (patch)
tree21d86ce20ba1759614d094740907f5af975ad0c8
parent145e65ab12e7a8d7a895b8a8a4631ae303ec734f (diff)
downloadbarebox-acfe5a4fec06be1d67e01b07cdd7e703fe3a2278.tar.gz
barebox-acfe5a4fec06be1d67e01b07cdd7e703fe3a2278.tar.xz
state: add fixup to copy state from barebox to kernel device tree
This patch registers a DT fixup, that copies the state nodes from the active to the kernel DT. The backend reference will be a phandles. Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--common/state.c107
1 files changed, 100 insertions, 7 deletions
diff --git a/common/state.c b/common/state.c
index d6060bb7df..655c9a63e3 100644
--- a/common/state.c
+++ b/common/state.c
@@ -435,13 +435,6 @@ static struct state *state_new(const char *name)
return state;
}
-void state_release(struct state *state)
-{
- list_del(&state->list);
- unregister_device(&state->dev);
- free(state);
-}
-
static struct state_variable *state_find_var(struct state *state,
const char *name)
{
@@ -676,6 +669,101 @@ static int state_from_node(struct state *state, struct device_node *node,
return ret;
}
+static int of_state_fixup(struct device_node *root, void *ctx)
+{
+ struct state *state = ctx;
+ const char *compatible = "barebox,state";
+ struct device_node *new_node, *node, *parent, *backend_node;
+ struct property *p;
+ int ret;
+ phandle phandle;
+
+ node = of_find_node_by_path_from(root, state->root->full_name);
+ if (node) {
+ /* replace existing node - it will be deleted later */
+ parent = node->parent;
+ } else {
+ char *of_path, *c;
+
+ /* look for parent, remove last '/' from path */
+ of_path = xstrdup(state->root->full_name);
+ c = strrchr(of_path, '/');
+ if (!c)
+ return -ENODEV;
+ *c = '0';
+ parent = of_find_node_by_path(of_path);
+ if (!parent)
+ parent = root;
+
+ free(of_path);
+ }
+
+ /* serialize variable definitions */
+ new_node = state_to_node(state, parent, STATE_CONVERT_FIXUP);
+ if (IS_ERR(new_node))
+ return PTR_ERR(new_node);
+
+ /* compatible */
+ p = of_new_property(new_node, "compatible", compatible,
+ strlen(compatible) + 1);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* backend-type */
+ if (!state->backend) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ p = of_new_property(new_node, "backend-type", state->backend->name,
+ strlen(state->backend->name) + 1);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* backend phandle */
+ backend_node = of_find_node_by_path_from(root, state->backend->of_path);
+ if (!backend_node) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ phandle = of_node_create_phandle(backend_node);
+ ret = of_property_write_u32(new_node, "backend", phandle);
+ if (ret)
+ goto out;
+
+ /* address-cells + size-cells */
+ ret = of_property_write_u32(new_node, "#address-cells", 1);
+ if (ret)
+ goto out;
+
+ ret = of_property_write_u32(new_node, "#size-cells", 1);
+ if (ret)
+ goto out;
+
+ /* delete existing node */
+ if (node)
+ of_delete_node(node);
+
+ return 0;
+
+ out:
+ of_delete_node(new_node);
+ return ret;
+}
+
+void state_release(struct state *state)
+{
+ of_unregister_fixup(of_state_fixup, state);
+ list_del(&state->list);
+ unregister_device(&state->dev);
+ free(state);
+}
+
/*
* state_new_from_node - create a new state instance from a device_node
*
@@ -697,6 +785,11 @@ struct state *state_new_from_node(const char *name, struct device_node *node)
return ERR_PTR(ret);
}
+ ret = of_register_fixup(of_state_fixup, state);
+ if (ret) {
+ state_release(state);
+ return ERR_PTR(ret);
+ }
return state;
}