dm: Allow device removal features to be dropped

For SPL we don't expect to need to remove a device. Save some code space
by dropping this feature. The board config can define
CONFIG_DM_DEVICE_REMOVE if this is in fact needed.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Tom Rini <trini@ti.com>
diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index 151c239..f14695b 100644
--- a/drivers/core/Makefile
+++ b/drivers/core/Makefile
@@ -4,5 +4,6 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-obj-y := device.o lists.o root.o uclass.o util.o
+obj-$(CONFIG_DM)	+= device.o lists.o root.o uclass.o util.o
 obj-$(CONFIG_OF_CONTROL) += simple-bus.o
+obj-$(CONFIG_DM_DEVICE_REMOVE)	+= device-remove.o
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
new file mode 100644
index 0000000..8fc6b71
--- /dev/null
+++ b/drivers/core/device-remove.c
@@ -0,0 +1,187 @@
+/*
+ * Device manager
+ *
+ * Copyright (c) 2014 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+
+/**
+ * device_chld_unbind() - Unbind all device's children from the device
+ *
+ * On error, the function continues to unbind all children, and reports the
+ * first error.
+ *
+ * @dev:	The device that is to be stripped of its children
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_unbind(struct udevice *dev)
+{
+	struct udevice *pos, *n;
+	int ret, saved_ret = 0;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_unbind(pos);
+		if (ret && !saved_ret)
+			saved_ret = ret;
+	}
+
+	return saved_ret;
+}
+
+/**
+ * device_chld_remove() - Stop all device's children
+ * @dev:	The device whose children are to be removed
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_remove(struct udevice *dev)
+{
+	struct udevice *pos, *n;
+	int ret;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_remove(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int device_unbind(struct udevice *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return -EINVAL;
+
+	drv = dev->driver;
+	assert(drv);
+
+	if (drv->unbind) {
+		ret = drv->unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = device_chld_unbind(dev);
+	if (ret)
+		return ret;
+
+	ret = uclass_unbind_device(dev);
+	if (ret)
+		return ret;
+
+	if (dev->parent)
+		list_del(&dev->sibling_node);
+	free(dev);
+
+	return 0;
+}
+
+/**
+ * device_free() - Free memory buffers allocated by a device
+ * @dev:	Device that is to be started
+ */
+void device_free(struct udevice *dev)
+{
+	int size;
+
+	if (dev->driver->priv_auto_alloc_size) {
+		free(dev->priv);
+		dev->priv = NULL;
+	}
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		free(dev->platdata);
+		dev->platdata = NULL;
+	}
+	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
+	if (size) {
+		free(dev->uclass_priv);
+		dev->uclass_priv = NULL;
+	}
+	if (dev->parent) {
+		size = dev->parent->driver->per_child_auto_alloc_size;
+		if (size) {
+			free(dev->parent_priv);
+			dev->parent_priv = NULL;
+		}
+	}
+}
+
+int device_remove(struct udevice *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!(dev->flags & DM_FLAG_ACTIVATED))
+		return 0;
+
+	drv = dev->driver;
+	assert(drv);
+
+	ret = uclass_pre_remove_device(dev);
+	if (ret)
+		return ret;
+
+	ret = device_chld_remove(dev);
+	if (ret)
+		goto err;
+
+	if (drv->remove) {
+		ret = drv->remove(dev);
+		if (ret)
+			goto err_remove;
+	}
+
+	if (dev->parent && dev->parent->driver->child_post_remove) {
+		ret = dev->parent->driver->child_post_remove(dev);
+		if (ret) {
+			dm_warn("%s: Device '%s' failed child_post_remove()",
+				__func__, dev->name);
+		}
+	}
+
+	device_free(dev);
+
+	dev->seq = -1;
+	dev->flags &= ~DM_FLAG_ACTIVATED;
+
+	return ret;
+
+err_remove:
+	/* We can't put the children back */
+	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
+		__func__, dev->name);
+err:
+	ret = uclass_post_probe_device(dev);
+	if (ret) {
+		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
+			__func__, dev->name);
+	}
+
+	return ret;
+}
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 49faa29..576a4f2 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -24,52 +24,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/**
- * device_chld_unbind() - Unbind all device's children from the device
- *
- * On error, the function continues to unbind all children, and reports the
- * first error.
- *
- * @dev:	The device that is to be stripped of its children
- * @return 0 on success, -ve on error
- */
-static int device_chld_unbind(struct udevice *dev)
-{
-	struct udevice *pos, *n;
-	int ret, saved_ret = 0;
-
-	assert(dev);
-
-	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
-		ret = device_unbind(pos);
-		if (ret && !saved_ret)
-			saved_ret = ret;
-	}
-
-	return saved_ret;
-}
-
-/**
- * device_chld_remove() - Stop all device's children
- * @dev:	The device whose children are to be removed
- * @return 0 on success, -ve on error
- */
-static int device_chld_remove(struct udevice *dev)
-{
-	struct udevice *pos, *n;
-	int ret;
-
-	assert(dev);
-
-	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
-		ret = device_remove(pos);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
 int device_bind(struct udevice *parent, struct driver *drv, const char *name,
 		void *platdata, int of_offset, struct udevice **devp)
 {
@@ -167,71 +121,6 @@
 			   -1, devp);
 }
 
-int device_unbind(struct udevice *dev)
-{
-	struct driver *drv;
-	int ret;
-
-	if (!dev)
-		return -EINVAL;
-
-	if (dev->flags & DM_FLAG_ACTIVATED)
-		return -EINVAL;
-
-	drv = dev->driver;
-	assert(drv);
-
-	if (drv->unbind) {
-		ret = drv->unbind(dev);
-		if (ret)
-			return ret;
-	}
-
-	ret = device_chld_unbind(dev);
-	if (ret)
-		return ret;
-
-	ret = uclass_unbind_device(dev);
-	if (ret)
-		return ret;
-
-	if (dev->parent)
-		list_del(&dev->sibling_node);
-	free(dev);
-
-	return 0;
-}
-
-/**
- * device_free() - Free memory buffers allocated by a device
- * @dev:	Device that is to be started
- */
-static void device_free(struct udevice *dev)
-{
-	int size;
-
-	if (dev->driver->priv_auto_alloc_size) {
-		free(dev->priv);
-		dev->priv = NULL;
-	}
-	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
-		free(dev->platdata);
-		dev->platdata = NULL;
-	}
-	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
-	if (size) {
-		free(dev->uclass_priv);
-		dev->uclass_priv = NULL;
-	}
-	if (dev->parent) {
-		size = dev->parent->driver->per_child_auto_alloc_size;
-		if (size) {
-			free(dev->parent_priv);
-			dev->parent_priv = NULL;
-		}
-	}
-}
-
 int device_probe_child(struct udevice *dev, void *parent_priv)
 {
 	struct driver *drv;
@@ -342,63 +231,6 @@
 	return device_probe_child(dev, NULL);
 }
 
-int device_remove(struct udevice *dev)
-{
-	struct driver *drv;
-	int ret;
-
-	if (!dev)
-		return -EINVAL;
-
-	if (!(dev->flags & DM_FLAG_ACTIVATED))
-		return 0;
-
-	drv = dev->driver;
-	assert(drv);
-
-	ret = uclass_pre_remove_device(dev);
-	if (ret)
-		return ret;
-
-	ret = device_chld_remove(dev);
-	if (ret)
-		goto err;
-
-	if (drv->remove) {
-		ret = drv->remove(dev);
-		if (ret)
-			goto err_remove;
-	}
-
-	if (dev->parent && dev->parent->driver->child_post_remove) {
-		ret = dev->parent->driver->child_post_remove(dev);
-		if (ret) {
-			dm_warn("%s: Device '%s' failed child_post_remove()",
-				__func__, dev->name);
-		}
-	}
-
-	device_free(dev);
-
-	dev->seq = -1;
-	dev->flags &= ~DM_FLAG_ACTIVATED;
-
-	return ret;
-
-err_remove:
-	/* We can't put the children back */
-	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
-		__func__, dev->name);
-err:
-	ret = uclass_post_probe_device(dev);
-	if (ret) {
-		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
-			__func__, dev->name);
-	}
-
-	return ret;
-}
-
 void *dev_get_platdata(struct udevice *dev)
 {
 	if (!dev) {
diff --git a/include/config_defaults.h b/include/config_defaults.h
index ad08c1d..985f055 100644
--- a/include/config_defaults.h
+++ b/include/config_defaults.h
@@ -20,4 +20,8 @@
 #define CONFIG_ZLIB 1
 #define CONFIG_PARTITIONS 1
 
+#ifndef CONFIG_SPL_BUILD
+#define CONFIG_DM_DEVICE_REMOVE
+#endif
+
 #endif
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 44cb7ef..f0cc794 100644
--- a/include/dm/device-internal.h
+++ b/include/dm/device-internal.h
@@ -87,7 +87,11 @@
  * @dev: Pointer to device to remove
  * @return 0 if OK, -ve on error (an error here is normally a very bad thing)
  */
+#ifdef CONFIG_DM_DEVICE_REMOVE
 int device_remove(struct udevice *dev);
+#else
+static inline int device_remove(struct udevice *dev) { return 0; }
+#endif
 
 /**
  * device_unbind() - Unbind a device, destroying it
@@ -99,6 +103,12 @@
  */
 int device_unbind(struct udevice *dev);
 
+#ifdef CONFIG_DM_DEVICE_REMOVE
+void device_free(struct udevice *dev);
+#else
+static inline void device_free(struct udevice *dev) {}
+#endif
+
 /* Cast away any volatile pointer */
 #define DM_ROOT_NON_CONST		(((gd_t *)gd)->dm_root)
 #define DM_UCLASS_ROOT_NON_CONST	(((gd_t *)gd)->uclass_root)