dm: Introduce device sequence numbering

In U-Boot it is pretty common to number devices from 0 and access them
on the command line using this numbering. While it may come to pass that
we will move away from this numbering, the possibility seems remote at
present.

Given that devices within a uclass will have an implied numbering, it
makes sense to build this into driver model as a core feature. The cost
is fairly small in terms of code and data space.

With each uclass having numbered devices we can ask for SPI port 0 or
serial port 1 and receive a single device.

Devices typically request a sequence number using aliases in the device
tree. These are resolved when the device is probed, to deal with conflicts.
Sequence numbers need not be sequential and holes are permitted.

At present there is no support for sequence numbers using static platform
data. It could easily be added to 'struct driver_info' if needed, but it
seems better to add features as we find a use for them, and the use of -1
to mean 'no sequence' makes the default value somewhat painful.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 86b9ff8..848ce3b 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -10,6 +10,7 @@
  */
 
 #include <common.h>
+#include <fdtdec.h>
 #include <malloc.h>
 #include <dm/device.h>
 #include <dm/device-internal.h>
@@ -21,6 +22,8 @@
 #include <linux/err.h>
 #include <linux/list.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /**
  * device_chld_unbind() - Unbind all device's children from the device
  *
@@ -95,6 +98,21 @@
 	dev->parent = parent;
 	dev->driver = drv;
 	dev->uclass = uc;
+
+	/*
+	 * For some devices, such as a SPI or I2C bus, the 'reg' property
+	 * is a reasonable indicator of the sequence number. But if there is
+	 * an alias, we use that in preference. In any case, this is just
+	 * a 'requested' sequence, and will be resolved (and ->seq updated)
+	 * when the device is probed.
+	 */
+	dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1);
+	dev->seq = -1;
+	if (uc->uc_drv->name && of_offset != -1) {
+		fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset,
+				     &dev->req_seq);
+	}
+
 	if (!dev->platdata && drv->platdata_auto_alloc_size)
 		dev->flags |= DM_FLAG_ALLOC_PDATA;
 
@@ -207,6 +225,7 @@
 	struct driver *drv;
 	int size = 0;
 	int ret;
+	int seq;
 
 	if (!dev)
 		return -EINVAL;
@@ -249,6 +268,13 @@
 			goto fail;
 	}
 
+	seq = uclass_resolve_seq(dev);
+	if (seq < 0) {
+		ret = seq;
+		goto fail;
+	}
+	dev->seq = seq;
+
 	if (drv->ofdata_to_platdata && dev->of_offset >= 0) {
 		ret = drv->ofdata_to_platdata(dev);
 		if (ret)
@@ -276,6 +302,7 @@
 			__func__, dev->name);
 	}
 fail:
+	dev->seq = -1;
 	device_free(dev);
 
 	return ret;
@@ -311,6 +338,7 @@
 
 	device_free(dev);
 
+	dev->seq = -1;
 	dev->flags &= ~DM_FLAG_ACTIVATED;
 
 	return 0;