@@ -25,8 +25,9 @@
/**
* struct of_overlay_info - Holds a single overlay info
- * @target: target of the overlay operation
- * @overlay: pointer to the overlay contents node
+ * @target: target of the overlay operation
+ * @overlay: pointer to the overlay contents node
+ * @plug_targets: pointer to array of plug target pointers
*
* Holds a single overlay state, including all the overlay logs &
* records.
@@ -34,6 +35,7 @@
struct of_overlay_info {
struct device_node *target;
struct device_node *overlay;
+ struct device_node **plug_targets;
};
/**
@@ -54,7 +56,8 @@ struct of_overlay {
};
static int of_overlay_apply_one(struct of_overlay *ov,
- struct device_node *target, const struct device_node *overlay);
+ struct device_node *target, const struct device_node *overlay,
+ bool plug_node, struct of_overlay_info *ovinfo);
static int of_overlay_apply_single_property(struct of_overlay *ov,
struct device_node *target, struct property *prop)
@@ -97,7 +100,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov,
tchild = of_get_child_by_name(target, cname);
if (tchild != NULL) {
/* apply overlay recursively */
- ret = of_overlay_apply_one(ov, tchild, child);
+ ret = of_overlay_apply_one(ov, tchild, child, false, NULL);
of_node_put(tchild);
} else {
/* create empty tree as a target */
@@ -112,7 +115,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov,
if (ret)
return ret;
- ret = of_overlay_apply_one(ov, tchild, child);
+ ret = of_overlay_apply_one(ov, tchild, child, false, NULL);
if (ret)
return ret;
}
@@ -128,33 +131,108 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov,
* by using the changeset.
*/
static int of_overlay_apply_one(struct of_overlay *ov,
- struct device_node *target, const struct device_node *overlay)
+ struct device_node *target, const struct device_node *overlay,
+ bool plug_node, struct of_overlay_info *ovinfo)
{
- struct device_node *child;
+ struct device_node *overlay_child;
+ struct device_node *plug_target;
struct property *prop;
int ret;
+ u32 val;
- for_each_property_of_node(overlay, prop) {
- ret = of_overlay_apply_single_property(ov, target, prop);
- if (ret) {
- pr_err("%s: Failed to apply prop @%s/%s\n",
- __func__, target->full_name, prop->name);
- return ret;
+ if (!plug_node) {
+ for_each_property_of_node(overlay, prop) {
+ ret = of_overlay_apply_single_property(ov, target, prop);
+ if (ret) {
+ pr_err("%s: Failed to apply prop @%s/%s\n",
+ __func__, target->full_name, prop->name);
+ return ret;
+ }
}
}
- for_each_child_of_node(overlay, child) {
- ret = of_overlay_apply_single_device_node(ov, target, child);
- if (ret != 0) {
- pr_err("%s: Failed to apply single node @%s/%s\n",
- __func__, target->full_name,
- child->name);
- of_node_put(child);
- return ret;
+ if (plug_node) {
+ struct device_node *socket_child;
+ int child_cnt = 0;
+
+ if (WARN_ON(!ovinfo))
+ return -EINVAL;
+
+ for_each_child_of_node(overlay->child, overlay_child) {
+ child_cnt++;
+ }
+ /* plug_targets[] is NULL terminated */
+ child_cnt++;
+ ovinfo->plug_targets = kcalloc(child_cnt,
+ sizeof(*ovinfo->plug_targets),
+ GFP_KERNEL);
+
+ child_cnt = 0;
+ for_each_child_of_node(overlay->child, overlay_child) {
+
+ socket_child = of_get_child_by_name(target,
+ overlay_child->name);
+ ret = of_property_read_u32(socket_child,
+ "target_phandle", &val);
+ of_node_put(socket_child);
+ if (ret != 0)
+ goto overlay_child;
+ plug_target = of_find_node_by_phandle(val);
+ if (!plug_target)
+ goto overlay_child;
+ /* save for of_node_put() in of_free_overlay_info() */
+ ovinfo->plug_targets[child_cnt++] = plug_target;
+
+ ret = of_overlay_apply_single_device_node(ov, plug_target,
+ overlay_child);
+ if (ret != 0)
+ goto plug_target;
+ }
+
+ } else {
+ for_each_child_of_node(overlay, overlay_child) {
+ ret = of_overlay_apply_single_device_node(ov, target,
+ overlay_child);
+ if (ret != 0)
+ goto overlay_child;
}
}
return 0;
+
+plug_target:
+ of_node_put(plug_target);
+overlay_child:
+ of_node_put(overlay_child);
+ return ret;
+}
+
+static bool plug_compatible(struct device_node *overlay,
+ struct device_node *target)
+{
+ struct property *prop_plug;
+ struct property *prop_socket;
+ const char *c_plug;
+ const char *c_socket;
+ bool socket_node;
+
+ socket_node = of_property_read_bool(target, "connector-socket");
+ if (!socket_node)
+ return false;
+
+ prop_plug = of_find_property(overlay, "compatible", NULL);
+ prop_socket = of_find_property(target, "compatible", NULL);
+
+ for (c_socket = of_prop_next_string(prop_plug, NULL); c_socket;
+ c_socket = of_prop_next_string(prop_plug, c_socket)) {
+ for (c_plug = of_prop_next_string(prop_plug, NULL); c_plug;
+ c_plug = of_prop_next_string(prop_plug, c_plug)) {
+ if (!of_compat_cmp(c_plug, c_socket, strlen(c_plug)))
+ return true;
+ }
+ }
+
+ return false;
}
/**
@@ -168,13 +246,24 @@ static int of_overlay_apply_one(struct of_overlay *ov,
*/
static int of_overlay_apply(struct of_overlay *ov)
{
- int i, err;
+ int i, err = 0;
+ bool plug_node = false;
/* first we apply the overlays atomically */
for (i = 0; i < ov->count; i++) {
struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i];
- err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay);
+ plug_node = of_property_read_bool(ovinfo->overlay,
+ "connector-plug");
+ if (plug_node) {
+ if (!plug_compatible(ovinfo->target, ovinfo->overlay))
+ err = -ENODEV;
+ }
+
+ if (err == 0)
+ err = of_overlay_apply_one(ov, ovinfo->target,
+ ovinfo->overlay, plug_node,
+ ovinfo);
if (err != 0) {
pr_err("%s: overlay failed '%s'\n",
__func__, ovinfo->target->full_name);
@@ -309,6 +398,7 @@ static int of_build_overlay_info(struct of_overlay *ov,
static int of_free_overlay_info(struct of_overlay *ov)
{
struct of_overlay_info *ovinfo;
+ struct device_node **plug_targets;
int i;
/* do it in reverse */
@@ -317,6 +407,11 @@ static int of_free_overlay_info(struct of_overlay *ov)
of_node_put(ovinfo->target);
of_node_put(ovinfo->overlay);
+ plug_targets = ovinfo->plug_targets;
+ while (*plug_targets != NULL) {
+ of_node_put(*plug_targets);
+ plug_targets++;
+ }
}
kfree(ov->ovinfo_tab);