board: Add Qualcomm Dragonboard 410C support

This commit add support for 96Boards Dragonboard410C.
It is board based on APQ8016 Qualcomm SoC, complying with
96boards specification.
Features (present out of the box):
- 4x Cortex A53 (ARMv8)
- 2x USB Host port
- 1x USB Device port
- 4x LEDs
- 1x HDMI connector
- 1x uSD connector
- 3x buttons (Power, Vol+, Vol-/Reset)
- WIFI, Bluetooth with integrated antenna
- 8GiB eMMC

U-Boot boots chained with fastboot in 64-bit mode.
For detailed build instructions see readme.txt in board directory.

Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
Tested-by: Simon Glass <sjg@chromium.org>
diff --git a/board/qualcomm/dragonboard410c/Kconfig b/board/qualcomm/dragonboard410c/Kconfig
new file mode 100644
index 0000000..03bd7ae
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/Kconfig
@@ -0,0 +1,15 @@
+if TARGET_DRAGONBOARD410C
+
+config SYS_BOARD
+	default "dragonboard410c"
+
+config SYS_VENDOR
+	default "qualcomm"
+
+config SYS_SOC
+	default "apq8016"
+
+config SYS_CONFIG_NAME
+	default "dragonboard410c"
+
+endif
diff --git a/board/qualcomm/dragonboard410c/MAINTAINERS b/board/qualcomm/dragonboard410c/MAINTAINERS
new file mode 100644
index 0000000..65cb47c
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/MAINTAINERS
@@ -0,0 +1,6 @@
+DRAGONBOARD410C BOARD
+M:	Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+S:	Maintained
+F:	board/qualcomm/dragonboard410c/
+F:	include/configs/dragonboard410c.h
+F:	configs/dragonboard410c_defconfig
diff --git a/board/qualcomm/dragonboard410c/Makefile b/board/qualcomm/dragonboard410c/Makefile
new file mode 100644
index 0000000..cd67808
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/Makefile
@@ -0,0 +1,8 @@
+#
+# (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y	:= dragonboard410c.o
+extra-y += head.o
diff --git a/board/qualcomm/dragonboard410c/dragonboard410c.c b/board/qualcomm/dragonboard410c/dragonboard410c.c
new file mode 100644
index 0000000..1fa5664
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/dragonboard410c.c
@@ -0,0 +1,131 @@
+/*
+ * Board init file for Dragonboard 410C
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <usb.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int dram_init(void)
+{
+	gd->ram_size = PHYS_SDRAM_1_SIZE;
+	return 0;
+}
+
+void dram_init_banksize(void)
+{
+	gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
+	gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
+}
+
+
+int board_prepare_usb(enum usb_init_type type)
+{
+	static struct udevice *pmic_gpio;
+	static struct gpio_desc hub_reset, usb_sel;
+	int ret = 0, node;
+
+	if (!pmic_gpio) {
+		ret = uclass_get_device_by_name(UCLASS_GPIO,
+						"pm8916_gpios@c000",
+						&pmic_gpio);
+		if (ret < 0) {
+			printf("Failed to find pm8916_gpios@c000 node.\n");
+			return ret;
+		}
+	}
+
+	/* Try to request gpios needed to start usb host on dragonboard */
+	if (!dm_gpio_is_valid(&hub_reset)) {
+		node = fdt_subnode_offset(gd->fdt_blob, pmic_gpio->of_offset,
+					  "usb_hub_reset_pm");
+		if (node < 0) {
+			printf("Failed to find usb_hub_reset_pm dt node.\n");
+			return node;
+		}
+		ret = gpio_request_by_name_nodev(gd->fdt_blob, node, "gpios", 0,
+						 &hub_reset, 0);
+		if (ret < 0) {
+			printf("Failed to request usb_hub_reset_pm gpio.\n");
+			return ret;
+		}
+	}
+
+	if (!dm_gpio_is_valid(&usb_sel)) {
+		node = fdt_subnode_offset(gd->fdt_blob, pmic_gpio->of_offset,
+					  "usb_sw_sel_pm");
+		if (node < 0) {
+			printf("Failed to find usb_sw_sel_pm dt node.\n");
+			return 0;
+		}
+		ret = gpio_request_by_name_nodev(gd->fdt_blob, node, "gpios", 0,
+						 &usb_sel, 0);
+		if (ret < 0) {
+			printf("Failed to request usb_sw_sel_pm gpio.\n");
+			return ret;
+		}
+	}
+
+	if (type == USB_INIT_HOST) {
+		/* Start USB Hub */
+		dm_gpio_set_dir_flags(&hub_reset,
+				      GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+		mdelay(100);
+		/* Switch usb to host connectors */
+		dm_gpio_set_dir_flags(&usb_sel,
+				      GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+		mdelay(100);
+	} else { /* Device */
+		/* Disable hub */
+		dm_gpio_set_dir_flags(&hub_reset, GPIOD_IS_OUT);
+		/* Switch back to device connector */
+		dm_gpio_set_dir_flags(&usb_sel, GPIOD_IS_OUT);
+	}
+
+	return 0;
+}
+
+int board_init(void)
+{
+	return 0;
+}
+
+/* Check for vol- button - if pressed - stop autoboot */
+int misc_init_r(void)
+{
+	struct udevice *pon;
+	struct gpio_desc resin;
+	int node, ret;
+
+	ret = uclass_get_device_by_name(UCLASS_GPIO, "pm8916_pon@800", &pon);
+	if (ret < 0) {
+		printf("Failed to find PMIC pon node. Check device tree\n");
+		return 0;
+	}
+
+	node = fdt_subnode_offset(gd->fdt_blob, pon->of_offset, "key_vol_down");
+	if (node < 0) {
+		printf("Failed to find key_vol_down node. Check device tree\n");
+		return 0;
+	}
+
+	if (gpio_request_by_name_nodev(gd->fdt_blob, node, "gpios", 0, &resin,
+				       0)) {
+		printf("Failed to request key_vol_down button.\n");
+		return 0;
+	}
+
+	if (dm_gpio_get_value(&resin)) {
+		setenv("bootdelay", "-1");
+		printf("Power button pressed - dropping to console.\n");
+	}
+
+	return 0;
+}
diff --git a/board/qualcomm/dragonboard410c/head.S b/board/qualcomm/dragonboard410c/head.S
new file mode 100644
index 0000000..ba29b12
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/head.S
@@ -0,0 +1,34 @@
+/*
+ * ARM64 header for proper chain-loading with Little Kernel.
+ *
+ * Little Kernel shipped with Dragonboard410C boots standard Linux images for
+ * ARM64. This file adds header that is required to boot U-Boot properly.
+ *
+ * For details see:
+ * https://www.kernel.org/doc/Documentation/arm64/booting.txt
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+
+.global _arm64_header
+_arm64_header:
+	b _start
+	.word 0
+	/* Image load offset from start of RAM, little-endian */
+	.quad   CONFIG_SYS_TEXT_BASE-PHYS_SDRAM_1
+	/* Effective size of kernel image, little-endian */
+	.quad   0 /* 0x60000 - ignored */
+	/* Informative flags, little-endian */
+	.quad   0
+	.quad   0                               /* reserved */
+	.quad   0                               /* reserved */
+	.quad   0                               /* reserved */
+	.byte   0x41                            /* Magic number, "ARM\x64" */
+	.byte   0x52
+	.byte   0x4d
+	.byte   0x64
+	.word   0                               /* reserved */
diff --git a/board/qualcomm/dragonboard410c/readme.txt b/board/qualcomm/dragonboard410c/readme.txt
new file mode 100644
index 0000000..7fc7c7a
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/readme.txt
@@ -0,0 +1,71 @@
+#
+# (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+Build & Run instructions:
+
+1) Install mkbootimg and dtbTool from
+   git://codeaurora.org/quic/kernel/skales (15ece94f09 worked for me)
+2) Setup CROSS_COMPILE to aarch64 compiler
+3) make dragonboard410c_config
+4) make
+5) generate fake, empty ramdisk (can have 0 bytes)
+$ touch rd
+
+6) Generate qualcomm device tree table with dtbTool [1]
+$ dtbTool -o dt.img arch/arm/dts
+
+7) Generate Android boot image with mkbootimg [2]:
+$ mkbootimg --kernel=u-boot-dtb.bin --output=u-boot.img --dt=dt.img  \
+  --pagesize 2048 --base 0x80000000 --ramdisk=rd --cmdline=""
+
+8) Enter fastboot (reboot board with vol- button pressed)
+
+9) Boot it:
+$ fastboot boot u-boot.img
+or flash as kernel:
+$ fastboot flash boot u-boot.img
+$ fastboot reboot
+
+
+What is working:
+- UART
+- GPIO (SoC)
+- SD
+- eMMC
+- Reset
+- USB in EHCI mode (usb starts does switch device->host, usb stop does the opposite)
+- PMIC GPIOS (but not in generic subsystem)
+- PMIC "special" buttons (power, vol-)
+
+What is not working / known bugs:
+- SDHCI is slow (~2.5MiB/s for SD and eMMC)
+
+[1] To boot any kernel image, Little Kernel requires valid device tree for the
+platform it runs on. dtbTool creates device tree table that Little Kernel scans.
+Later on proper device tree is passed to next boot stage.
+Full device tree is not required to boot u-boot. Enough would be:
+/dts-v1/;
+
+/ {
+	model = "Qualcomm Technologies, Inc. Dragonboard 410c";
+	compatible = "qcom,dragonboard", "qcom,apq8016-sbc";
+	qcom,msm-id = <0xce 0x0 0xf8 0x0 0xf9 0x0 0xfa 0x0 0xf7 0x0>;
+	qcom,board-id = <0x10018 0x0>;
+	#address-cells = <0x2>;
+	#size-cells = <0x2>;
+	chosen { };
+	aliases { };
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000 0 0x3da00000>;
+	};
+};
+
+but for simplicity (and because size of image is not that critical) we use
+existing Qualcomm device trees.
+
+[2] Note that ramdisk is required, even if it is unused.
diff --git a/board/qualcomm/dragonboard410c/u-boot.lds b/board/qualcomm/dragonboard410c/u-boot.lds
new file mode 100644
index 0000000..6e1c5a8
--- /dev/null
+++ b/board/qualcomm/dragonboard410c/u-boot.lds
@@ -0,0 +1,90 @@
+/*
+ * Override linker script for fastboot-readable images
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * Based on arch/arm/cpu/armv8/u-boot.lds (Just add header)
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
+OUTPUT_ARCH(aarch64)
+ENTRY(_arm64_header)
+SECTIONS
+{
+	. = 0x00000000;
+
+	. = ALIGN(8);
+	.text :
+	{
+		*(.__image_copy_start)
+		board/qualcomm/dragonboard410c/head.o (.text*)
+		CPUDIR/start.o (.text*)
+		*(.text*)
+	}
+
+	. = ALIGN(8);
+	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
+
+	. = ALIGN(8);
+	.data : {
+		*(.data*)
+	}
+
+	. = ALIGN(8);
+
+	. = .;
+
+	. = ALIGN(8);
+	.u_boot_list : {
+		KEEP(*(SORT(.u_boot_list*)));
+	}
+
+	. = ALIGN(8);
+
+	.image_copy_end :
+	{
+		*(.__image_copy_end)
+	}
+
+	. = ALIGN(8);
+
+	.rel_dyn_start :
+	{
+		*(.__rel_dyn_start)
+	}
+
+	.rela.dyn : {
+		*(.rela*)
+	}
+
+	.rel_dyn_end :
+	{
+		*(.__rel_dyn_end)
+	}
+
+	_end = .;
+
+	. = ALIGN(8);
+
+	.bss_start : {
+		KEEP(*(.__bss_start));
+	}
+
+	.bss : {
+		*(.bss*)
+		 . = ALIGN(8);
+	}
+
+	.bss_end : {
+		KEEP(*(.__bss_end));
+	}
+
+	/DISCARD/ : { *(.dynsym) }
+	/DISCARD/ : { *(.dynstr*) }
+	/DISCARD/ : { *(.dynamic*) }
+	/DISCARD/ : { *(.plt*) }
+	/DISCARD/ : { *(.interp*) }
+	/DISCARD/ : { *(.gnu*) }
+}