core: Add dev_{disable,enable}_by_path

We cannot use device structures to disable devices, since getting
them with the API functions would bind and activate the device, which
would fail if the underlying device does not exist.
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/core/device.c b/drivers/core/device.c
index fd59fe1..feed43c 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -516,6 +516,33 @@
 	return 0;
 }
 
+/**
+ * device_find_by_ofnode() - Return device associated with given ofnode
+ *
+ * The returned device is *not* activated.
+ *
+ * @node: The ofnode for which a associated device should be looked up
+ * @devp: Pointer to structure to hold the found device
+ * Return: 0 if OK, -ve on error
+ */
+static int device_find_by_ofnode(ofnode node, struct udevice **devp)
+{
+	struct uclass *uc;
+	struct udevice *dev;
+	int ret;
+
+	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
+		ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node,
+						   &dev);
+		if (!ret || dev) {
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
 int device_get_child(struct udevice *parent, int index, struct udevice **devp)
 {
 	struct udevice *dev;
@@ -739,3 +766,54 @@
 
 	return !fdt_node_check_compatible(fdt, 0, compat);
 }
+
+int dev_disable_by_path(const char *path)
+{
+	struct uclass *uc;
+	ofnode node = ofnode_path(path);
+	struct udevice *dev;
+	int ret = 1;
+
+	if (!of_live_active())
+		return -ENOSYS;
+
+	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
+		ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, &dev);
+		if (!ret)
+			break;
+	}
+
+	if (ret)
+		return ret;
+
+	ret = device_remove(dev, DM_REMOVE_NORMAL);
+	if (ret)
+		return ret;
+
+	ret = device_unbind(dev);
+	if (ret)
+		return ret;
+
+	return ofnode_set_enabled(node, false);
+}
+
+int dev_enable_by_path(const char *path)
+{
+	ofnode node = ofnode_path(path);
+	ofnode pnode = ofnode_get_parent(node);
+	struct udevice *parent;
+	int ret = 1;
+
+	if (!of_live_active())
+		return -ENOSYS;
+
+	ret = device_find_by_ofnode(pnode, &parent);
+	if (ret)
+		return ret;
+
+	ret = ofnode_set_enabled(node, true);
+	if (ret)
+		return ret;
+
+	return lists_bind_fdt(parent, node, NULL);
+}