ARM: rpi_b: detect board revision

Detect the board revision early during boot, and print the decoded
model name.

Eventually, this information can be used for tasks such as:
- Allowing/preventing USB device mode; some models have a USB device on-
  board so only host mode makes sense. Others connect the SoC directly
  to the USB connector, so device-mode might make sense.
- The on-board USB hub/Ethernet requires different GPIOs to enable it,
  although luckily the default appears to be fine so far.
- The compute module contains an on-board eMMC device, so we could store
  the environment there. Other models use an SD card and so don't support
  saving the environment (unless we store it in a file on the FAT boot
  partition...)

Set $fdtfile based on this information. At present, the mainline Linux
kernel doesn't contain a separate DTB for most models, but I hope that
will change soon.

Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
diff --git a/arch/arm/include/asm/arch-bcm2835/mbox.h b/arch/arm/include/asm/arch-bcm2835/mbox.h
index 61f427d..0289ba6 100644
--- a/arch/arm/include/asm/arch-bcm2835/mbox.h
+++ b/arch/arm/include/asm/arch-bcm2835/mbox.h
@@ -119,6 +119,39 @@
  * };
  */
 
+#define BCM2835_MBOX_TAG_GET_BOARD_REV	0x00010002
+
+/*
+ * 0x2..0xf from:
+ * http://raspberryalphaomega.org.uk/2013/02/06/automatic-raspberry-pi-board-revision-detection-model-a-b1-and-b2/
+ * http://www.raspberrypi.org/forums/viewtopic.php?f=63&t=32733
+ * 0x10, 0x11 from swarren's testing
+ */
+#define BCM2835_BOARD_REV_B_I2C0_2	0x2
+#define BCM2835_BOARD_REV_B_I2C0_3	0x3
+#define BCM2835_BOARD_REV_B_I2C1_4	0x4
+#define BCM2835_BOARD_REV_B_I2C1_5	0x5
+#define BCM2835_BOARD_REV_B_I2C1_6	0x6
+#define BCM2835_BOARD_REV_A_7		0x7
+#define BCM2835_BOARD_REV_A_8		0x8
+#define BCM2835_BOARD_REV_A_9		0x9
+#define BCM2835_BOARD_REV_B_REV2_d	0xd
+#define BCM2835_BOARD_REV_B_REV2_e	0xe
+#define BCM2835_BOARD_REV_B_REV2_f	0xf
+#define BCM2835_BOARD_REV_B_PLUS	0x10
+#define BCM2835_BOARD_REV_CM		0x11
+
+struct bcm2835_mbox_tag_get_board_rev {
+	struct bcm2835_mbox_tag_hdr tag_hdr;
+	union {
+		struct {
+		} req;
+		struct {
+			u32 rev;
+		} resp;
+	} body;
+};
+
 #define BCM2835_MBOX_TAG_GET_MAC_ADDRESS	0x00010003
 
 struct bcm2835_mbox_tag_get_mac_address {
diff --git a/board/raspberrypi/rpi_b/rpi_b.c b/board/raspberrypi/rpi_b/rpi_b.c
index db904a4..51a4fa1 100644
--- a/board/raspberrypi/rpi_b/rpi_b.c
+++ b/board/raspberrypi/rpi_b/rpi_b.c
@@ -42,6 +42,12 @@
 	u32 end_tag;
 };
 
+struct msg_get_board_rev {
+	struct bcm2835_mbox_hdr hdr;
+	struct bcm2835_mbox_tag_get_board_rev get_board_rev;
+	u32 end_tag;
+};
+
 struct msg_get_mac_address {
 	struct bcm2835_mbox_hdr hdr;
 	struct bcm2835_mbox_tag_get_mac_address get_mac_address;
@@ -60,6 +66,67 @@
 	u32 end_tag;
 };
 
+/* See comments in mbox.h for data source */
+static const struct {
+	const char *name;
+	const char *fdtfile;
+} models[] = {
+	[BCM2835_BOARD_REV_B_I2C0_2] = {
+		"Model B (no P5)",
+		"bcm2835-rpi-b-i2c0.dtb",
+	},
+	[BCM2835_BOARD_REV_B_I2C0_3] = {
+		"Model B (no P5)",
+		"bcm2835-rpi-b-i2c0.dtb",
+	},
+	[BCM2835_BOARD_REV_B_I2C1_4] = {
+		"Model B",
+		"bcm2835-rpi-b.dtb",
+	},
+	[BCM2835_BOARD_REV_B_I2C1_5] = {
+		"Model B",
+		"bcm2835-rpi-b.dtb",
+	},
+	[BCM2835_BOARD_REV_B_I2C1_6] = {
+		"Model B",
+		"bcm2835-rpi-b.dtb",
+	},
+	[BCM2835_BOARD_REV_A_7] = {
+		"Model A",
+		"bcm2835-rpi-a.dtb",
+	},
+	[BCM2835_BOARD_REV_A_8] = {
+		"Model A",
+		"bcm2835-rpi-a.dtb",
+	},
+	[BCM2835_BOARD_REV_A_9] = {
+		"Model A",
+		"bcm2835-rpi-a.dtb",
+	},
+	[BCM2835_BOARD_REV_B_REV2_d] = {
+		"Model B rev2",
+		"bcm2835-rpi-b-rev2.dtb",
+	},
+	[BCM2835_BOARD_REV_B_REV2_e] = {
+		"Model B rev2",
+		"bcm2835-rpi-b-rev2.dtb",
+	},
+	[BCM2835_BOARD_REV_B_REV2_f] = {
+		"Model B rev2",
+		"bcm2835-rpi-b-rev2.dtb",
+	},
+	[BCM2835_BOARD_REV_B_PLUS] = {
+		"Model B+",
+		"bcm2835-rpi-b-plus.dtb",
+	},
+	[BCM2835_BOARD_REV_CM] = {
+		"Compute Module",
+		"bcm2835-rpi-cm.dtb",
+	},
+};
+
+u32 rpi_board_rev = 0;
+
 int dram_init(void)
 {
 	ALLOC_ALIGN_BUFFER(struct msg_get_arm_mem, msg, 1, 16);
@@ -79,13 +146,27 @@
 	return 0;
 }
 
-int misc_init_r(void)
+static void set_fdtfile(void)
+{
+	const char *fdtfile;
+
+	if (getenv("fdtfile"))
+		return;
+
+	fdtfile = models[rpi_board_rev].fdtfile;
+	if (!fdtfile)
+		fdtfile = "bcm2835-rpi-other.dtb";
+
+	setenv("fdtfile", fdtfile);
+}
+
+static void set_usbethaddr(void)
 {
 	ALLOC_ALIGN_BUFFER(struct msg_get_mac_address, msg, 1, 16);
 	int ret;
 
 	if (getenv("usbethaddr"))
-		return 0;
+		return;
 
 	BCM2835_MBOX_INIT_HDR(msg);
 	BCM2835_MBOX_INIT_TAG(&msg->get_mac_address, GET_MAC_ADDRESS);
@@ -94,11 +175,18 @@
 	if (ret) {
 		printf("bcm2835: Could not query MAC address\n");
 		/* Ignore error; not critical */
-		return 0;
+		return;
 	}
 
 	eth_setenv_enetaddr("usbethaddr", msg->get_mac_address.body.resp.mac);
 
+	return;
+}
+
+int misc_init_r(void)
+{
+	set_fdtfile();
+	set_usbethaddr();
 	return 0;
 }
 
@@ -126,8 +214,36 @@
 	return 0;
 }
 
+static void get_board_rev(void)
+{
+	ALLOC_ALIGN_BUFFER(struct msg_get_board_rev, msg, 1, 16);
+	int ret;
+	const char *name;
+
+	BCM2835_MBOX_INIT_HDR(msg);
+	BCM2835_MBOX_INIT_TAG(&msg->get_board_rev, GET_BOARD_REV);
+
+	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
+	if (ret) {
+		printf("bcm2835: Could not query board revision\n");
+		/* Ignore error; not critical */
+		return;
+	}
+
+	rpi_board_rev = msg->get_board_rev.body.resp.rev;
+	if (rpi_board_rev >= ARRAY_SIZE(models))
+		rpi_board_rev = 0;
+
+	name = models[rpi_board_rev].name;
+	if (!name)
+		name = "Unknown model";
+	printf("RPI model: %s\n", name);
+}
+
 int board_init(void)
 {
+	get_board_rev();
+
 	gd->bd->bi_boot_params = 0x100;
 
 	return power_on_module(BCM2835_MBOX_POWER_DEVID_USB_HCD);
diff --git a/include/configs/rpi_b.h b/include/configs/rpi_b.h
index 41e975f..4874c51 100644
--- a/include/configs/rpi_b.h
+++ b/include/configs/rpi_b.h
@@ -175,7 +175,6 @@
 	"pxefile_addr_r=0x00100000\0" \
 	"kernel_addr_r=0x01000000\0" \
 	"fdt_addr_r=0x02000000\0" \
-	"fdtfile=bcm2835-rpi-b.dtb\0" \
 	"ramdisk_addr_r=0x02100000\0" \
 
 #define BOOT_TARGET_DEVICES(func) \